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/pull_request_template.md b/.github/pull_request_template.md index ae14ef5b59..4d0c170187 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -32,6 +32,12 @@ _**Author:**_ Is a change note required? - [ ] Yes - [ ] No +🚨🚨🚨 +_**Reviewer:**_ Confirm that format of *shared* queries (not the .qll file, the +.ql file that imports it) is valid by running them within VS Code. + - [ ] Confirmed + + _**Reviewer:**_ Confirm that either a change note is not required or the change note is required and has been added. - [ ] Confirmed diff --git a/.github/touch b/.github/touch deleted file mode 100644 index 8b13789179..0000000000 --- a/.github/touch +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml deleted file mode 100644 index aa3bb668ca..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-latest - 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 0814e059e8..678b3be403 100644 --- a/.github/workflows/code-scanning-pack-gen.yml +++ b/.github/workflows/code-scanning-pack-gen.yml @@ -1,51 +1,52 @@ 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-latest + runs-on: ubuntu-22.04 outputs: 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/deviations codeql-coding-standards/scripts/reports + 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 1a2374d19d..2fc28fc900 100644 --- a/.github/workflows/codeql_unit_tests.yml +++ b/.github/workflows/codeql_unit_tests.yml @@ -1,34 +1,38 @@ 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-latest + runs-on: ubuntu-22.04 outputs: 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] - runs-on: ubuntu-latest + 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 43bf8cac06..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-latest - 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 new file mode 100644 index 0000000000..02d37f92b2 --- /dev/null +++ b/.github/workflows/extra-rule-validation.yml @@ -0,0 +1,60 @@ +name: ⚙️ Extra Rule Validation + +on: + merge_group: + types: [checks_requested] + push: + branches: + - main + - "rc/**" + - next + pull_request: + branches: + - main + - "rc/**" + - next + + +jobs: + validate-rules-csv: + name: Validate Rules CSV + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rules + shell: pwsh + run: scripts/util/Get-DuplicateRules.ps1 -Language 'all' -CIMode + + + validate-shared-rules-test-structure: + name: Validate Rules Test Structure + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Ensure CPP Shared Rules Have Valid Structure + shell: pwsh + run: scripts/util/Test-SharedImplementationsHaveTestCases.ps1 -Language cpp -CIMode + + - name: Ensure C Shared Rules Have Valid Structure + shell: pwsh + run: scripts/util/Test-SharedImplementationsHaveTestCases.ps1 -Language c -CIMode + + + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: missing-test-report.csv + path: MissingTestReport*.csv + + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: test-report.csv + path: TestReport*.csv + if-no-files-found: error + + 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 0142c2feed..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 @@ -15,13 +17,13 @@ on: jobs: generate-html-doc: name: Generate HTML documentation - 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" @@ -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 0a4e58dbd3..a401150b07 100644 --- a/.github/workflows/standard_library_upgrade_tests.yml +++ b/.github/workflows/standard_library_upgrade_tests.yml @@ -14,12 +14,12 @@ on: jobs: prepare-unit-test-matrix: name: Prepare CodeQL unit test matrix - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: 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: | @@ -154,15 +154,15 @@ jobs: validate-test-results: name: Validate test results needs: [run-test-suites] - runs-on: ubuntu-latest + 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 new file mode 100644 index 0000000000..3f4fde932d --- /dev/null +++ b/.github/workflows/tooling-unit-tests.yml @@ -0,0 +1,117 @@ +name: 🧰 Tooling unit tests + +on: + merge_group: + types: [checks_requested] + push: + branches: + - main + - "rc/**" + - next + pull_request: + branches: + - main + - "rc/**" + - next + +jobs: + prepare-supported-codeql-env-matrix: + name: Prepare supported CodeQL environment matrix + runs-on: ubuntu-22.04 + outputs: + matrix: ${{ steps.export-supported-codeql-env-matrix.outputs.matrix }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Export supported CodeQL environment matrix + id: export-supported-codeql-env-matrix + run: | + echo "::set-output name=matrix::$( + jq --compact-output '.supported_environment | {include: .}' supported_codeql_configs.json + )" + + analysis-report-tests: + name: Run analysis report tests + needs: prepare-supported-codeql-env-matrix + 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@v4 + + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + + - name: Install Python dependencies + run: pip install -r scripts/reports/requirements.txt + + - name: Cache CodeQL + id: cache-codeql + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/codeql_home + 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 + 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 + run: | + PATH=$PATH:$CODEQL_HOME/codeql + pytest scripts/reports/analysis_report_test.py + + recategorization-tests: + name: Run Guideline Recategorization 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/guideline_recategorization/requirements.txt + + - 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 01f8bcf339..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-latest + 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 f7fc7563a1..0000000000 --- a/.github/workflows/validate-coding-standards.yml +++ /dev/null @@ -1,174 +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-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: "3.9" - - - 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 --max-procs "$XARGS_MAX_PROCS" --max-args 1 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 --max-procs "$XARGS_MAX_PROCS" --max-args 1 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-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Fetch CodeQL - run: | - TAG="v$( jq -r '.supported_environment | .[0] | .codeql_cli' supported_codeql_configs.json)" - gh release download $TAG --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip - unzip -q codeql-linux64.zip - 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/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/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-latest - 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-latest - 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-latest - 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 d1f141cced..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", @@ -185,6 +207,7 @@ "type": "pickString", "options": [ "Allocations", + "Banned", "BannedFunctions", "BannedLibraries", "BannedSyntax", @@ -192,19 +215,30 @@ "Classes", "Comments", "Contracts1", + "Contracts2", + "Contracts3", + "Contracts4", + "Contracts5", + "Contracts6", "Concurrency", "Concurrency", "Concurrency1", "Concurrency2", "Concurrency3", - "Concurrency4", - "Concurrency5", + "Concurrency4", + "Concurrency5", "Conditionals", "Const", "DeadCode", "Declarations", "Declarations1", "Declarations2", + "Declarations3", + "Declarations4", + "Declarations5", + "Declarations6", + "Declarations7", + "Declarations8", "Exceptions1", "Exceptions2", "Expressions", @@ -217,25 +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", @@ -252,6 +298,7 @@ "Preprocessor3", "Preprocessor4", "Preprocessor5", + "Preprocessor6", "IntegerConversion", "Expressions", "DeadCode", 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 9b8b4c6d56..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.9.0 +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 + +
\[ Finlay 2003 \]
\[ Microsoft 2003 \]
\[ Pethia 2003 \]
\[ Seacord 2013b \] Chapter 1, "Running with Scissors"
\[ Viega 2005 \] Section 5.2.13, "Unchecked Array Indexing"
\[ xorl 2009 \] "CVE-2008-1517: Apple Mac OS X (XNU) Missing Array Index Validation"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard INT01-C. Use rsize_t or size_t for all integer values representing the size of an object 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:2013 Tainted, potentially mutilated, or out-of-domain integer values are used in a restricted sink \[taintsink\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-758 2017-06-29: 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-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 + +
\[ Griffiths 2006 \]
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C CTR54-CPP. Do not subtract iterators that do not refer to the same container Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Subtracting or comparing two pointers that do not refer to the same array \[ptrobj\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-469 , Use of Pointer Subtraction to Determine Size 2017-07-10: CERT: Exact
CWE 3.11 CWE-469 , Use of Pointer Subtraction to Determine Size 2018-10-18:CERT: CWE subset of rule
+ + +## 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 + +
\[ Banahan 2003 \] Section 5.3, "Pointers" Section 5.7, "Expressions Involving Pointers"
\[ ISO/IEC 9899:2011 \] 6.5.6, "Additive Operators"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C CTR54-CPP. Do not subtract iterators that do not refer to the same container Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Subtracting or comparing two pointers that do not refer to the same array \[ptrobj\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-469 , Use of Pointer Subtraction to Determine Size 2017-07-10: CERT: Exact
CWE 3.11 CWE-469 , Use of Pointer Subtraction to Determine Size 2018-10-18:CERT: CWE subset of rule
+ + +## 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 + +
\[ Banahan 2003 \] Section 5.3, "Pointers" Section 5.7, "Expressions Involving Pointers"
\[ ISO/IEC 9899:2011 \] 6.5.6, "Additive Operators"
+ + +## 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 + +
\[ Banahan 2003 \] Section 5.3, "Pointers" Section 5.7, "Expressions Involving Pointers"
\[ ISO/IEC 9899:2011 \] 6.5.6, "Additive Operators"
\[ VU\#162289 \]
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
C Secure Coding Standard API00-C. Functions should validate their parameters Prior to 2018-01-12: CERT: Unspecified Relationship
C Secure Coding Standard ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array Prior to 2018-01-12: CERT: Unspecified Relationship
C Secure Coding Standard INT30-C. Ensure that unsigned integer operations do not wrap Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Forming invalid pointers by library functions \[libptr\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Buffer Boundary Violation (Buffer Overflow) \[HCB\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Unchecked Array Copying \[XYW\] 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-121 , Stack-based Buffer Overflow 2017-05-18: CERT: Partial overlap
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
CWE 2.11 CWE-805 , Buffer Access with Incorrect Length Value 2017-05-18: CERT: Partial overlap
CWE 3.1 CWE-129 , Improper Validation of Array Index 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-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 + +
\[ Cassidy 2014 \] Existential Type Crisis : Diagnosis of the OpenSSL Heartbleed Bug
\[ IETF: RFC 6520 \]
\[ ISO/IEC TS 17961:2013 \]
\[ VU\#720951 \]
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard MSC00-C. Compile cleanly at high warning levels Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP54-CPP. Do not access an object outside of its lifetime Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Dangling References to Stack Frames \[DCM\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Escaping of the address of an automatic object \[addrescape\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 18.6 (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-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 + +
\[ Coverity 2007 \]
\[ ISO/IEC 9899:2011 \] 6.2.4, "Storage Durations of Objects"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard MSC00-C. Compile cleanly at high warning levels Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP54-CPP. Do not access an object outside of its lifetime Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Dangling References to Stack Frames \[DCM\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Escaping of the address of an automatic object \[addrescape\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 18.6 (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-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 + +
\[ Coverity 2007 \]
\[ ISO/IEC 9899:2011 \] 6.2.4, "Storage Durations of Objects"
+ + +## 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 b5f7087ab0..d6000852c6 100644 --- a/c/cert/src/rules/DCL38-C/DeclaringAFlexibleArrayMember.ql +++ b/c/cert/src/rules/DCL38-C/DeclaringAFlexibleArrayMember.ql @@ -10,34 +10,22 @@ * 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 */ import cpp import codingstandards.c.cert - -/** - * A member with the type array that is last in a struct - * includes any sized array (either specified or not) - */ -class FlexibleArrayMember extends MemberVariable { - Struct s; - - FlexibleArrayMember() { - this.getType() instanceof ArrayType and - this.getDeclaringType() = s and - not exists(int i, int j | - s.getAMember(i) = this and - exists(s.getAMember(j)) and - j > i - ) - } -} +import codingstandards.c.Variable from VariableDeclarationEntry m, ArrayType a where not isExcluded(m, Declarations2Package::declaringAFlexibleArrayMemberQuery()) and m.getType() = a and - m.getVariable() instanceof FlexibleArrayMember and + m.getVariable() instanceof FlexibleArrayMemberCandidate and a.getArraySize() = 1 select m, "Incorrect syntax used for declaring this flexible array member." 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard DCL03-C. Use a static assertion to test the value of a constant expression Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.2.6.1, "General" 6.7.2.1, "Structure and Union Specifiers"
\[ Graff 2003 \]
\[ Sun 1993 \]
+ + +## 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.md b/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.md new file mode 100644 index 0000000000..417688d1af --- /dev/null +++ b/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.md @@ -0,0 +1,263 @@ +# ERR30-C: Errno is not set to zero prior to an errno-setting call + +This query implements the CERT-C rule ERR30-C: + +> Take care when reading errno + + +## Description + +The value of `errno` is initialized to zero at program startup, but it is never subsequently set to zero by any C standard library function. The value of `errno` may be set to nonzero by a C standard library function call whether or not there is an error, provided the use of `errno` is not documented in the description of the function. It is meaningful for a program to inspect the contents of `errno` only after an error might have occurred. More precisely, `errno` is meaningful only after a library function that sets `errno` on error has returned an error code. + +According to Question 20.4 of C-FAQ \[[Summit 2005](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Summit05)\], + +> In general, you should detect errors by checking return values, and use `errno` only to distinguish among the various causes of an error, such as "File not found" or "Permission denied." (Typically, you use `perror` or `strerror` to print these discriminating error messages.) It's only necessary to detect errors with `errno` when a function does not have a unique, unambiguous, out-of-band error return (that is, because all of its possible return values are valid; one example is `atoi [*sic*]`). In these cases (and in these cases only; check the documentation to be sure whether a function allows this), you can detect errors by setting `errno` to 0, calling the function, and then testing `errno`. (Setting `errno` to 0 first is important, as no library function ever does that for you.) + + +Note that `atoi()` is not required to set the value of `errno`. + +Library functions fall into the following categories: + +* Those that set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator) +* Those that set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator) +* Those that do not promise to set `errno` +* Those with differing standards documentation + +## Library Functions that Set errno and Return an Out-of-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator). That is, their return value on error can never be returned by a successful call. + +A program may check `errno` after invoking these library functions but is not required to do so. The program should not check the value of `errno` without first verifying that the function returned an error indicator. For example, `errno` should not be checked after calling `signal()` without first ensuring that `signal()` actually returned `SIG_ERR`. + +**Functions That Set `errno` and Return an Out-of-Band Error Indicator** + +
Function Name Return Value errno Value
ftell() -1L Positive
fgetpos() , fsetpos() Nonzero Positive
mbrtowc() , mbsrtowcs() (size_t)(-1) EILSEQ
signal() SIG_ERR Positive
wcrtomb() , wcsrtombs() (size_t)(-1) EILSEQ
mbrtoc16() , mbrtoc32() (size_t)(-1) EILSEQ
c16rtomb() , c32rtomb() (size_t)(-1) EILSEQ
+ + +## Library Functions that Set errno and Return an In-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator). That is, the return value when an error occurs is also a valid return value for successful calls. For example, the `strtoul()` function returns `ULONG_MAX` and sets `errno` to `ERANGE` if an error occurs. Because `ULONG_MAX` is a valid return value, the only way to confirm that an error occurred when LONG_MAX is returned is to check `errno`. + +The `fgetwc()` and `fputwc()` functions return `WEOF` in multiple cases, only one of which results in setting `errno`. The string conversion functions will return the maximum or minimum representable value and set `errno` to `ERANGE` if the converted value cannot be represented by the data type. However, if the conversion cannot happen because the input is invalid, the function will return `0`, and the output pointer parameter will be assigned the value of the input pointer parameter, provided the output parameter is non-null. + +A program that uses `errno` for error checking a function that returns an in-band error indicator must set `errno` to `0` before calling one of these library functions and then inspect `errno` before a subsequent library function call. + +**Functions that Set `errno` and Return an In-Band Error Indicator** + +
Function Name Return Value errno Value
fgetwc() , fputwc() WEOF EILSEQ
strtol() , wcstol() LONG_MIN or LONG_MAX ERANGE
strtoll() , wcstoll() LLONG_MIN or LLONG_MAX ERANGE
strtoul() , wcstoul() ULONG_MAX ERANGE
strtoull() , wcstoull() ULLONG_MAX ERANGE
strtoumax() , wcstoumax() UINTMAX_MAX ERANGE
strtod() , wcstod() 0 or ±HUGE_VAL ERANGE
strtof() , wcstof() 0 or ±HUGE_VALF ERANGE
strtold() , wcstold() 0 or ±HUGE_VALL ERANGE
strtoimax() , wcstoimax() INTMAX_MIN , INTMAX_MAX ERANGE
+ + +## Library Functions that Do Not Promise to Set errno + +The C Standard fails to document the behavior of `errno` for some functions. For example, the `setlocale()` function normally returns a null pointer in the event of an error, but no guarantees are made about setting `errno`. + +After calling one of these functions, a program should not rely solely on the value of `errno` to determine if an error occurred. The function might have altered `errno`, but this does not ensure that `errno` will properly indicate an error condition. If the program does check `errno` after calling one of these functions, it should set `errno` to 0 before the function call. + +## Library Functions with Differing Standards Documentation + +Some functions behave differently regarding `errno` in various standards. The `fopen()` function is one such example. When `fopen()` encounters an error, it returns a null pointer. The C Standard makes no mention of `errno` when describing `fopen()`. However, POSIX.1 declares that when `fopen()` encounters an error, it returns a null pointer and sets `errno` to a value indicating the error \[[IEEE Std 1003.1-2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\]. The implication is that a program conforming to C but not to POSIX (such as a Windows program) should not check `errno` after calling `fopen()`, but a POSIX program may check `errno` if `fopen()` returns a null pointer. + +## Library Functions and errno + +The following uses of `errno` are documented in the C Standard: + +* Functions defined in `` may set `errno` but are not required to. +* For numeric conversion functions in the `strtod`, `strtol`, `wcstod`, and `wcstol` families, if the correct result is outside the range of representable values, an appropriate minimum or maximum value is returned and the value `ERANGE` is stored in `errno`. For floating-point conversion functions in the `strtod` and `wcstod` families, if an underflow occurs, whether `errno` acquires the value `ERANGE` is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior). If the conversion fails, `0` is returned and `errno` is not set. +* The numeric conversion function `atof()` and those in the `atoi` family "need not affect the value of" `errno`. +* For mathematical functions in ``, if the integer expression `math_errhandling & MATH_ERRNO` is nonzero, on a domain error, `errno` acquires the value `EDOM`; on an overflow with default rounding or if the mathematical result is an exact infinity from finite arguments, `errno` acquires the value `ERANGE`; and on an underflow, whether `errno` acquires the value `ERANGE` is implementation-defined. +* If a request made by calling `signal()` cannot be honored, a value of `SIG_ERR` is returned and a positive value is stored in `errno`. +* The byte I/O functions, wide-character I/O functions, and multibyte conversion functions store the value of the macro `EILSEQ` in `errno` if and only if an encoding error occurs. +* On failure, `fgetpos()` and `fsetpos()` return nonzero and store an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* On failure, `ftell()` returns `-1L` and stores an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* The `perror()` function maps the error number in `errno` to a message and writes it to `stderr`. +The POSIX.1 standard defines the use of `errno` by many more functions (including the C standard library function). POSIX also has a small set of functions that are exceptions to the rule. These functions have no return value reserved to indicate an error, but they still set `errno` on error. To detect an error, an application must set `errno` to `0` before calling the function and check whether it is nonzero after the call. Affected functions include `strcoll()`, `strxfrm()`, `strerror()`, `wcscoll()`, `wcsxfrm()`, and `fwide()`. The C Standard allows these functions to set `errno` to a nonzero value on success. Consequently, this type of error checking should be performed only on POSIX systems. + +## Noncompliant Code Example (strtoul()) + +This noncompliant code example fails to set `errno` to `0` before invoking `strtoul()`. If an error occurs, `strtoul()` returns a valid value (`ULONG_MAX`), so `errno` is the only means of determining if `strtoul()` ran successfully. + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` +Any error detected in this manner may have occurred earlier in the program or may not represent an actual error. + +## Compliant Solution (strtoul()) + +This compliant solution sets `errno` to `0` before the call to `strtoul()` and inspects `errno` after the call: + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` + +## Noncompliant Code Example (ftell()) + +This noncompliant code example, after calling `ftell()`, examines `errno` without first checking whether the out-of-band indicator returned by `ftell() `indicates an error. + +```cpp +#include +#include + +void func(FILE* fp) { + errno=0; + ftell(fp); + if (errno) { + perror("ftell"); + } +} +``` + +## Compliant Solution (ftell()) + +This compliant solution first detects that `ftell() `failed using its out-of-band error indicator. Once an error has been confirmed, reading `errno` (implicitly by using the `perror()` function) is permitted. + +```cpp +#include +#include + +void func(FILE* fp) { + if (ftell(fp) == -1) { + perror("ftell"); + } +} +``` + +## Noncompliant Code Example (fopen()) + +This noncompliant code example may fail to diagnose errors because `fopen()` might not set `errno` even if an error occurs: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (errno != 0) { + /* Handle error */ + } +} +``` + +## Compliant Solution (fopen(), C) + +The C Standard makes no mention of `errno` when describing `fopen()`. In this compliant solution, the results of the call to `fopen()` are used to determine failure and `errno` is not checked: + +```cpp +#include + +void func(const char *filename) { + FILE *fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* An error occurred in fopen() */ + } +} +``` + +## Compliant Solution (fopen(), POSIX) + +In this compliant solution, `errno` is checked only after an error has already been detected by another means: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* + * An error occurred in fopen(); now it's valid + * to examine errno. + */ + perror(filename); + } +} +``` + +## Risk Assessment + +The improper use of `errno` may result in failing to detect an error condition or in incorrectly identifying an error condition when none exists. + +
Rule Severity Likelihood Remediation Cost Priority Level
ERR30-C Medium Probable Medium P8 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 errno-reset Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ERR30 Fully implemented
CodeSonar 7.1p0 LANG.STRUCT.RC Redundant Condition
Compass/ROSE Could detect violations of this rule by ensuring that each library function is accompanied by the proper treatment of errno
Coverity 2017.07 MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 Implemented
Helix QAC 2022.3 C2500, C2501, C2502, C2503 C++3172, C++3173, C++3174, C++3175, C++3176, C++3177, C++3178, C++3179, C++3183, C++3184
Klocwork 2022.3 CXX.ERRNO.NOT_SET CXX.ERRNO.NOT_CHECKED CXX.ERRNO.INCORRECTLY_CHECKED
LDRA tool suite 9.7.1 111 D, 121 D, 122 D, 132 D, 134 D Fully implemented
Parasoft C/C++test 2022.1 CERT_C-ERR30-a CERT_C-ERR30-b Properly use errno value Provide error handling for file opening errors right next to the call to fopen
Polyspace Bug Finder R2022b CERT C: Rule ERR30-C Checks for: Misuse of errnoisuse of errno, errno not resetrrno not reset. Rule fully covered.
PRQA QA-C 9.7 2500, 2501, 2502, 2503
+ + +## 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+ERR30-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
CERT C Secure Coding Standard EXP12-C. Do not ignore values returned by functions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Incorrectly setting and using errno \[inverrno\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-456 , Missing Initialization of a Variable 2017-07-05: 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-456 and ERR30-C** + +CWE-456 = EXP33-C + +CWE-456 = Union( ERR30-C, list) where list = + +* Reading potentially uninitialized variables besides errno +**CWE-248 and ERR30-C** + +Intersection( CWE-248, ERR30-C) = Ø + +CWE-248 is only for languages that support exceptions. It lists C++ and Java, but not C. + +## Bibliography + +
\[ Brainbell.com \] Macros and Miscellaneous Pitfalls
\[ Horton 1990 \] Section 11, p. 168 Section 14, p. 254
\[ IEEE Std 1003.1-2013 \] XSH, System Interfaces, fopen
\[ Koenig 1989 \] Section 5.4, p. 73
\[ Summit 2005 \]
+ + +## Implementation notes + +None + +## References + +* CERT-C: [ERR30-C: Take care when reading errno](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql b/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql new file mode 100644 index 0000000000..06ac9d1198 --- /dev/null +++ b/c/cert/src/rules/ERR30-C/ErrnoNotSetToZero.ql @@ -0,0 +1,55 @@ +/** + * @id c/cert/errno-not-set-to-zero + * @name ERR30-C: Errno is not set to zero prior to an errno-setting call + * @description Set errno to zero prior to each call to an errno-setting function. Failing to do so + * might end in spurious errno values. + * @kind problem + * @precision high + * @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 + +/** + * CFG nodes preceding a `ErrnoSettingFunctionCall` + */ +ControlFlowNode notZeroedPriorToErrnoSet(InBandErrnoSettingFunctionCall fc) { + result = fc + or + exists(ControlFlowNode mid | + result = mid.getAPredecessor() and + mid = notZeroedPriorToErrnoSet(fc) and + // stop recursion when `errno` is set to zero + not result instanceof ErrnoZeroed and + not result = any(ErrnoGuard g).getZeroedSuccessor() + ) +} + +from InBandErrnoSettingFunctionCall fc, ControlFlowNode cause +where + not isExcluded(cause, Contracts4Package::errnoNotSetToZeroQuery()) and + cause = notZeroedPriorToErrnoSet(fc) and + ( + // `errno` is not reset anywhere in the function + cause = fc.getEnclosingFunction().getBlock() + or + // `errno` is not reset after a call to an errno-setting function + cause = any(InBandErrnoSettingFunctionCall ec | ec != fc) + or + // `errno` is not reset after a call to a function + cause = any(FunctionCall fc2 | fc2 != fc) + or + // `errno` value is known to be != 0 + cause = any(ErrnoGuard g).getNonZeroedSuccessor() + ) +select fc, "The value of `errno` may be different than `0` when this function is called." diff --git a/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.md b/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.md new file mode 100644 index 0000000000..edfe19bfd4 --- /dev/null +++ b/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.md @@ -0,0 +1,263 @@ +# ERR30-C: Do not check errno before the function return value + +This query implements the CERT-C rule ERR30-C: + +> Take care when reading errno + + +## Description + +The value of `errno` is initialized to zero at program startup, but it is never subsequently set to zero by any C standard library function. The value of `errno` may be set to nonzero by a C standard library function call whether or not there is an error, provided the use of `errno` is not documented in the description of the function. It is meaningful for a program to inspect the contents of `errno` only after an error might have occurred. More precisely, `errno` is meaningful only after a library function that sets `errno` on error has returned an error code. + +According to Question 20.4 of C-FAQ \[[Summit 2005](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Summit05)\], + +> In general, you should detect errors by checking return values, and use `errno` only to distinguish among the various causes of an error, such as "File not found" or "Permission denied." (Typically, you use `perror` or `strerror` to print these discriminating error messages.) It's only necessary to detect errors with `errno` when a function does not have a unique, unambiguous, out-of-band error return (that is, because all of its possible return values are valid; one example is `atoi [*sic*]`). In these cases (and in these cases only; check the documentation to be sure whether a function allows this), you can detect errors by setting `errno` to 0, calling the function, and then testing `errno`. (Setting `errno` to 0 first is important, as no library function ever does that for you.) + + +Note that `atoi()` is not required to set the value of `errno`. + +Library functions fall into the following categories: + +* Those that set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator) +* Those that set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator) +* Those that do not promise to set `errno` +* Those with differing standards documentation + +## Library Functions that Set errno and Return an Out-of-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator). That is, their return value on error can never be returned by a successful call. + +A program may check `errno` after invoking these library functions but is not required to do so. The program should not check the value of `errno` without first verifying that the function returned an error indicator. For example, `errno` should not be checked after calling `signal()` without first ensuring that `signal()` actually returned `SIG_ERR`. + +**Functions That Set `errno` and Return an Out-of-Band Error Indicator** + +
Function Name Return Value errno Value
ftell() -1L Positive
fgetpos() , fsetpos() Nonzero Positive
mbrtowc() , mbsrtowcs() (size_t)(-1) EILSEQ
signal() SIG_ERR Positive
wcrtomb() , wcsrtombs() (size_t)(-1) EILSEQ
mbrtoc16() , mbrtoc32() (size_t)(-1) EILSEQ
c16rtomb() , c32rtomb() (size_t)(-1) EILSEQ
+ + +## Library Functions that Set errno and Return an In-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator). That is, the return value when an error occurs is also a valid return value for successful calls. For example, the `strtoul()` function returns `ULONG_MAX` and sets `errno` to `ERANGE` if an error occurs. Because `ULONG_MAX` is a valid return value, the only way to confirm that an error occurred when LONG_MAX is returned is to check `errno`. + +The `fgetwc()` and `fputwc()` functions return `WEOF` in multiple cases, only one of which results in setting `errno`. The string conversion functions will return the maximum or minimum representable value and set `errno` to `ERANGE` if the converted value cannot be represented by the data type. However, if the conversion cannot happen because the input is invalid, the function will return `0`, and the output pointer parameter will be assigned the value of the input pointer parameter, provided the output parameter is non-null. + +A program that uses `errno` for error checking a function that returns an in-band error indicator must set `errno` to `0` before calling one of these library functions and then inspect `errno` before a subsequent library function call. + +**Functions that Set `errno` and Return an In-Band Error Indicator** + +
Function Name Return Value errno Value
fgetwc() , fputwc() WEOF EILSEQ
strtol() , wcstol() LONG_MIN or LONG_MAX ERANGE
strtoll() , wcstoll() LLONG_MIN or LLONG_MAX ERANGE
strtoul() , wcstoul() ULONG_MAX ERANGE
strtoull() , wcstoull() ULLONG_MAX ERANGE
strtoumax() , wcstoumax() UINTMAX_MAX ERANGE
strtod() , wcstod() 0 or ±HUGE_VAL ERANGE
strtof() , wcstof() 0 or ±HUGE_VALF ERANGE
strtold() , wcstold() 0 or ±HUGE_VALL ERANGE
strtoimax() , wcstoimax() INTMAX_MIN , INTMAX_MAX ERANGE
+ + +## Library Functions that Do Not Promise to Set errno + +The C Standard fails to document the behavior of `errno` for some functions. For example, the `setlocale()` function normally returns a null pointer in the event of an error, but no guarantees are made about setting `errno`. + +After calling one of these functions, a program should not rely solely on the value of `errno` to determine if an error occurred. The function might have altered `errno`, but this does not ensure that `errno` will properly indicate an error condition. If the program does check `errno` after calling one of these functions, it should set `errno` to 0 before the function call. + +## Library Functions with Differing Standards Documentation + +Some functions behave differently regarding `errno` in various standards. The `fopen()` function is one such example. When `fopen()` encounters an error, it returns a null pointer. The C Standard makes no mention of `errno` when describing `fopen()`. However, POSIX.1 declares that when `fopen()` encounters an error, it returns a null pointer and sets `errno` to a value indicating the error \[[IEEE Std 1003.1-2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\]. The implication is that a program conforming to C but not to POSIX (such as a Windows program) should not check `errno` after calling `fopen()`, but a POSIX program may check `errno` if `fopen()` returns a null pointer. + +## Library Functions and errno + +The following uses of `errno` are documented in the C Standard: + +* Functions defined in `` may set `errno` but are not required to. +* For numeric conversion functions in the `strtod`, `strtol`, `wcstod`, and `wcstol` families, if the correct result is outside the range of representable values, an appropriate minimum or maximum value is returned and the value `ERANGE` is stored in `errno`. For floating-point conversion functions in the `strtod` and `wcstod` families, if an underflow occurs, whether `errno` acquires the value `ERANGE` is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior). If the conversion fails, `0` is returned and `errno` is not set. +* The numeric conversion function `atof()` and those in the `atoi` family "need not affect the value of" `errno`. +* For mathematical functions in ``, if the integer expression `math_errhandling & MATH_ERRNO` is nonzero, on a domain error, `errno` acquires the value `EDOM`; on an overflow with default rounding or if the mathematical result is an exact infinity from finite arguments, `errno` acquires the value `ERANGE`; and on an underflow, whether `errno` acquires the value `ERANGE` is implementation-defined. +* If a request made by calling `signal()` cannot be honored, a value of `SIG_ERR` is returned and a positive value is stored in `errno`. +* The byte I/O functions, wide-character I/O functions, and multibyte conversion functions store the value of the macro `EILSEQ` in `errno` if and only if an encoding error occurs. +* On failure, `fgetpos()` and `fsetpos()` return nonzero and store an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* On failure, `ftell()` returns `-1L` and stores an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* The `perror()` function maps the error number in `errno` to a message and writes it to `stderr`. +The POSIX.1 standard defines the use of `errno` by many more functions (including the C standard library function). POSIX also has a small set of functions that are exceptions to the rule. These functions have no return value reserved to indicate an error, but they still set `errno` on error. To detect an error, an application must set `errno` to `0` before calling the function and check whether it is nonzero after the call. Affected functions include `strcoll()`, `strxfrm()`, `strerror()`, `wcscoll()`, `wcsxfrm()`, and `fwide()`. The C Standard allows these functions to set `errno` to a nonzero value on success. Consequently, this type of error checking should be performed only on POSIX systems. + +## Noncompliant Code Example (strtoul()) + +This noncompliant code example fails to set `errno` to `0` before invoking `strtoul()`. If an error occurs, `strtoul()` returns a valid value (`ULONG_MAX`), so `errno` is the only means of determining if `strtoul()` ran successfully. + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` +Any error detected in this manner may have occurred earlier in the program or may not represent an actual error. + +## Compliant Solution (strtoul()) + +This compliant solution sets `errno` to `0` before the call to `strtoul()` and inspects `errno` after the call: + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` + +## Noncompliant Code Example (ftell()) + +This noncompliant code example, after calling `ftell()`, examines `errno` without first checking whether the out-of-band indicator returned by `ftell() `indicates an error. + +```cpp +#include +#include + +void func(FILE* fp) { + errno=0; + ftell(fp); + if (errno) { + perror("ftell"); + } +} +``` + +## Compliant Solution (ftell()) + +This compliant solution first detects that `ftell() `failed using its out-of-band error indicator. Once an error has been confirmed, reading `errno` (implicitly by using the `perror()` function) is permitted. + +```cpp +#include +#include + +void func(FILE* fp) { + if (ftell(fp) == -1) { + perror("ftell"); + } +} +``` + +## Noncompliant Code Example (fopen()) + +This noncompliant code example may fail to diagnose errors because `fopen()` might not set `errno` even if an error occurs: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (errno != 0) { + /* Handle error */ + } +} +``` + +## Compliant Solution (fopen(), C) + +The C Standard makes no mention of `errno` when describing `fopen()`. In this compliant solution, the results of the call to `fopen()` are used to determine failure and `errno` is not checked: + +```cpp +#include + +void func(const char *filename) { + FILE *fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* An error occurred in fopen() */ + } +} +``` + +## Compliant Solution (fopen(), POSIX) + +In this compliant solution, `errno` is checked only after an error has already been detected by another means: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* + * An error occurred in fopen(); now it's valid + * to examine errno. + */ + perror(filename); + } +} +``` + +## Risk Assessment + +The improper use of `errno` may result in failing to detect an error condition or in incorrectly identifying an error condition when none exists. + +
Rule Severity Likelihood Remediation Cost Priority Level
ERR30-C Medium Probable Medium P8 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 errno-reset Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ERR30 Fully implemented
CodeSonar 7.1p0 LANG.STRUCT.RC Redundant Condition
Compass/ROSE Could detect violations of this rule by ensuring that each library function is accompanied by the proper treatment of errno
Coverity 2017.07 MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 Implemented
Helix QAC 2022.3 C2500, C2501, C2502, C2503 C++3172, C++3173, C++3174, C++3175, C++3176, C++3177, C++3178, C++3179, C++3183, C++3184
Klocwork 2022.3 CXX.ERRNO.NOT_SET CXX.ERRNO.NOT_CHECKED CXX.ERRNO.INCORRECTLY_CHECKED
LDRA tool suite 9.7.1 111 D, 121 D, 122 D, 132 D, 134 D Fully implemented
Parasoft C/C++test 2022.1 CERT_C-ERR30-a CERT_C-ERR30-b Properly use errno value Provide error handling for file opening errors right next to the call to fopen
Polyspace Bug Finder R2022b CERT C: Rule ERR30-C Checks for: Misuse of errnoisuse of errno, errno not resetrrno not reset. Rule fully covered.
PRQA QA-C 9.7 2500, 2501, 2502, 2503
+ + +## 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+ERR30-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
CERT C Secure Coding Standard EXP12-C. Do not ignore values returned by functions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Incorrectly setting and using errno \[inverrno\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-456 , Missing Initialization of a Variable 2017-07-05: 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-456 and ERR30-C** + +CWE-456 = EXP33-C + +CWE-456 = Union( ERR30-C, list) where list = + +* Reading potentially uninitialized variables besides errno +**CWE-248 and ERR30-C** + +Intersection( CWE-248, ERR30-C) = Ø + +CWE-248 is only for languages that support exceptions. It lists C++ and Java, but not C. + +## Bibliography + +
\[ Brainbell.com \] Macros and Miscellaneous Pitfalls
\[ Horton 1990 \] Section 11, p. 168 Section 14, p. 254
\[ IEEE Std 1003.1-2013 \] XSH, System Interfaces, fopen
\[ Koenig 1989 \] Section 5.4, p. 73
\[ Summit 2005 \]
+ + +## Implementation notes + +None + +## References + +* CERT-C: [ERR30-C: Take care when reading errno](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql b/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql new file mode 100644 index 0000000000..13f7e40303 --- /dev/null +++ b/c/cert/src/rules/ERR30-C/ErrnoReadBeforeReturn.ql @@ -0,0 +1,58 @@ +/** + * @id c/cert/errno-read-before-return + * @name ERR30-C: Do not check errno before the function return value + * @description Do not check errno before the function return value. Failing to do so might + * invalidate the error detection. + * @kind problem + * @precision high + * @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` + */ +class OutOfBandErrnoSettingFunctionCertCall extends FunctionCall { + OutOfBandErrnoSettingFunctionCertCall() { + this.getTarget() instanceof OutOfBandErrnoSettingFunctionCert + } +} + +/** + * A successor of an ErrnoSettingFunctionCertCall appearing + * before a check of the return value + */ +ControlFlowNode returnNotCheckedAfter(OutOfBandErrnoSettingFunctionCertCall errnoSet) { + result = errnoSet + or + exists(ControlFlowNode mid | + result = mid.getASuccessor() and + mid = returnNotCheckedAfter(errnoSet) and + // stop recursion on a return value check + not ( + any(ControlStructure cs).getControllingExpr() = result and + DataFlow::localExprFlow(errnoSet, result.(Operation).getAnOperand*()) + ) and + // stop recursion on a following errno setting function call + not result instanceof OutOfBandErrnoSettingFunctionCertCall + ) +} + +from OutOfBandErrnoSettingFunctionCertCall errnoSet, ErrnoRead check +where + not isExcluded(check, Contracts4Package::errnoReadBeforeReturnQuery()) and + check = returnNotCheckedAfter(errnoSet) +select check, "Do not read `errno` before checking the return value of function $@.", errnoSet, + errnoSet.toString() diff --git a/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.md b/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.md new file mode 100644 index 0000000000..5ec6577e45 --- /dev/null +++ b/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.md @@ -0,0 +1,263 @@ +# ERR30-C: Do not call a function before checking errno + +This query implements the CERT-C rule ERR30-C: + +> Take care when reading errno + + +## Description + +The value of `errno` is initialized to zero at program startup, but it is never subsequently set to zero by any C standard library function. The value of `errno` may be set to nonzero by a C standard library function call whether or not there is an error, provided the use of `errno` is not documented in the description of the function. It is meaningful for a program to inspect the contents of `errno` only after an error might have occurred. More precisely, `errno` is meaningful only after a library function that sets `errno` on error has returned an error code. + +According to Question 20.4 of C-FAQ \[[Summit 2005](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Summit05)\], + +> In general, you should detect errors by checking return values, and use `errno` only to distinguish among the various causes of an error, such as "File not found" or "Permission denied." (Typically, you use `perror` or `strerror` to print these discriminating error messages.) It's only necessary to detect errors with `errno` when a function does not have a unique, unambiguous, out-of-band error return (that is, because all of its possible return values are valid; one example is `atoi [*sic*]`). In these cases (and in these cases only; check the documentation to be sure whether a function allows this), you can detect errors by setting `errno` to 0, calling the function, and then testing `errno`. (Setting `errno` to 0 first is important, as no library function ever does that for you.) + + +Note that `atoi()` is not required to set the value of `errno`. + +Library functions fall into the following categories: + +* Those that set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator) +* Those that set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator) +* Those that do not promise to set `errno` +* Those with differing standards documentation + +## Library Functions that Set errno and Return an Out-of-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator). That is, their return value on error can never be returned by a successful call. + +A program may check `errno` after invoking these library functions but is not required to do so. The program should not check the value of `errno` without first verifying that the function returned an error indicator. For example, `errno` should not be checked after calling `signal()` without first ensuring that `signal()` actually returned `SIG_ERR`. + +**Functions That Set `errno` and Return an Out-of-Band Error Indicator** + +
Function Name Return Value errno Value
ftell() -1L Positive
fgetpos() , fsetpos() Nonzero Positive
mbrtowc() , mbsrtowcs() (size_t)(-1) EILSEQ
signal() SIG_ERR Positive
wcrtomb() , wcsrtombs() (size_t)(-1) EILSEQ
mbrtoc16() , mbrtoc32() (size_t)(-1) EILSEQ
c16rtomb() , c32rtomb() (size_t)(-1) EILSEQ
+ + +## Library Functions that Set errno and Return an In-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator). That is, the return value when an error occurs is also a valid return value for successful calls. For example, the `strtoul()` function returns `ULONG_MAX` and sets `errno` to `ERANGE` if an error occurs. Because `ULONG_MAX` is a valid return value, the only way to confirm that an error occurred when LONG_MAX is returned is to check `errno`. + +The `fgetwc()` and `fputwc()` functions return `WEOF` in multiple cases, only one of which results in setting `errno`. The string conversion functions will return the maximum or minimum representable value and set `errno` to `ERANGE` if the converted value cannot be represented by the data type. However, if the conversion cannot happen because the input is invalid, the function will return `0`, and the output pointer parameter will be assigned the value of the input pointer parameter, provided the output parameter is non-null. + +A program that uses `errno` for error checking a function that returns an in-band error indicator must set `errno` to `0` before calling one of these library functions and then inspect `errno` before a subsequent library function call. + +**Functions that Set `errno` and Return an In-Band Error Indicator** + +
Function Name Return Value errno Value
fgetwc() , fputwc() WEOF EILSEQ
strtol() , wcstol() LONG_MIN or LONG_MAX ERANGE
strtoll() , wcstoll() LLONG_MIN or LLONG_MAX ERANGE
strtoul() , wcstoul() ULONG_MAX ERANGE
strtoull() , wcstoull() ULLONG_MAX ERANGE
strtoumax() , wcstoumax() UINTMAX_MAX ERANGE
strtod() , wcstod() 0 or ±HUGE_VAL ERANGE
strtof() , wcstof() 0 or ±HUGE_VALF ERANGE
strtold() , wcstold() 0 or ±HUGE_VALL ERANGE
strtoimax() , wcstoimax() INTMAX_MIN , INTMAX_MAX ERANGE
+ + +## Library Functions that Do Not Promise to Set errno + +The C Standard fails to document the behavior of `errno` for some functions. For example, the `setlocale()` function normally returns a null pointer in the event of an error, but no guarantees are made about setting `errno`. + +After calling one of these functions, a program should not rely solely on the value of `errno` to determine if an error occurred. The function might have altered `errno`, but this does not ensure that `errno` will properly indicate an error condition. If the program does check `errno` after calling one of these functions, it should set `errno` to 0 before the function call. + +## Library Functions with Differing Standards Documentation + +Some functions behave differently regarding `errno` in various standards. The `fopen()` function is one such example. When `fopen()` encounters an error, it returns a null pointer. The C Standard makes no mention of `errno` when describing `fopen()`. However, POSIX.1 declares that when `fopen()` encounters an error, it returns a null pointer and sets `errno` to a value indicating the error \[[IEEE Std 1003.1-2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\]. The implication is that a program conforming to C but not to POSIX (such as a Windows program) should not check `errno` after calling `fopen()`, but a POSIX program may check `errno` if `fopen()` returns a null pointer. + +## Library Functions and errno + +The following uses of `errno` are documented in the C Standard: + +* Functions defined in `` may set `errno` but are not required to. +* For numeric conversion functions in the `strtod`, `strtol`, `wcstod`, and `wcstol` families, if the correct result is outside the range of representable values, an appropriate minimum or maximum value is returned and the value `ERANGE` is stored in `errno`. For floating-point conversion functions in the `strtod` and `wcstod` families, if an underflow occurs, whether `errno` acquires the value `ERANGE` is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior). If the conversion fails, `0` is returned and `errno` is not set. +* The numeric conversion function `atof()` and those in the `atoi` family "need not affect the value of" `errno`. +* For mathematical functions in ``, if the integer expression `math_errhandling & MATH_ERRNO` is nonzero, on a domain error, `errno` acquires the value `EDOM`; on an overflow with default rounding or if the mathematical result is an exact infinity from finite arguments, `errno` acquires the value `ERANGE`; and on an underflow, whether `errno` acquires the value `ERANGE` is implementation-defined. +* If a request made by calling `signal()` cannot be honored, a value of `SIG_ERR` is returned and a positive value is stored in `errno`. +* The byte I/O functions, wide-character I/O functions, and multibyte conversion functions store the value of the macro `EILSEQ` in `errno` if and only if an encoding error occurs. +* On failure, `fgetpos()` and `fsetpos()` return nonzero and store an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* On failure, `ftell()` returns `-1L` and stores an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* The `perror()` function maps the error number in `errno` to a message and writes it to `stderr`. +The POSIX.1 standard defines the use of `errno` by many more functions (including the C standard library function). POSIX also has a small set of functions that are exceptions to the rule. These functions have no return value reserved to indicate an error, but they still set `errno` on error. To detect an error, an application must set `errno` to `0` before calling the function and check whether it is nonzero after the call. Affected functions include `strcoll()`, `strxfrm()`, `strerror()`, `wcscoll()`, `wcsxfrm()`, and `fwide()`. The C Standard allows these functions to set `errno` to a nonzero value on success. Consequently, this type of error checking should be performed only on POSIX systems. + +## Noncompliant Code Example (strtoul()) + +This noncompliant code example fails to set `errno` to `0` before invoking `strtoul()`. If an error occurs, `strtoul()` returns a valid value (`ULONG_MAX`), so `errno` is the only means of determining if `strtoul()` ran successfully. + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` +Any error detected in this manner may have occurred earlier in the program or may not represent an actual error. + +## Compliant Solution (strtoul()) + +This compliant solution sets `errno` to `0` before the call to `strtoul()` and inspects `errno` after the call: + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` + +## Noncompliant Code Example (ftell()) + +This noncompliant code example, after calling `ftell()`, examines `errno` without first checking whether the out-of-band indicator returned by `ftell() `indicates an error. + +```cpp +#include +#include + +void func(FILE* fp) { + errno=0; + ftell(fp); + if (errno) { + perror("ftell"); + } +} +``` + +## Compliant Solution (ftell()) + +This compliant solution first detects that `ftell() `failed using its out-of-band error indicator. Once an error has been confirmed, reading `errno` (implicitly by using the `perror()` function) is permitted. + +```cpp +#include +#include + +void func(FILE* fp) { + if (ftell(fp) == -1) { + perror("ftell"); + } +} +``` + +## Noncompliant Code Example (fopen()) + +This noncompliant code example may fail to diagnose errors because `fopen()` might not set `errno` even if an error occurs: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (errno != 0) { + /* Handle error */ + } +} +``` + +## Compliant Solution (fopen(), C) + +The C Standard makes no mention of `errno` when describing `fopen()`. In this compliant solution, the results of the call to `fopen()` are used to determine failure and `errno` is not checked: + +```cpp +#include + +void func(const char *filename) { + FILE *fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* An error occurred in fopen() */ + } +} +``` + +## Compliant Solution (fopen(), POSIX) + +In this compliant solution, `errno` is checked only after an error has already been detected by another means: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* + * An error occurred in fopen(); now it's valid + * to examine errno. + */ + perror(filename); + } +} +``` + +## Risk Assessment + +The improper use of `errno` may result in failing to detect an error condition or in incorrectly identifying an error condition when none exists. + +
Rule Severity Likelihood Remediation Cost Priority Level
ERR30-C Medium Probable Medium P8 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 errno-reset Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ERR30 Fully implemented
CodeSonar 7.1p0 LANG.STRUCT.RC Redundant Condition
Compass/ROSE Could detect violations of this rule by ensuring that each library function is accompanied by the proper treatment of errno
Coverity 2017.07 MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 Implemented
Helix QAC 2022.3 C2500, C2501, C2502, C2503 C++3172, C++3173, C++3174, C++3175, C++3176, C++3177, C++3178, C++3179, C++3183, C++3184
Klocwork 2022.3 CXX.ERRNO.NOT_SET CXX.ERRNO.NOT_CHECKED CXX.ERRNO.INCORRECTLY_CHECKED
LDRA tool suite 9.7.1 111 D, 121 D, 122 D, 132 D, 134 D Fully implemented
Parasoft C/C++test 2022.1 CERT_C-ERR30-a CERT_C-ERR30-b Properly use errno value Provide error handling for file opening errors right next to the call to fopen
Polyspace Bug Finder R2022b CERT C: Rule ERR30-C Checks for: Misuse of errnoisuse of errno, errno not resetrrno not reset. Rule fully covered.
PRQA QA-C 9.7 2500, 2501, 2502, 2503
+ + +## 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+ERR30-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
CERT C Secure Coding Standard EXP12-C. Do not ignore values returned by functions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Incorrectly setting and using errno \[inverrno\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-456 , Missing Initialization of a Variable 2017-07-05: 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-456 and ERR30-C** + +CWE-456 = EXP33-C + +CWE-456 = Union( ERR30-C, list) where list = + +* Reading potentially uninitialized variables besides errno +**CWE-248 and ERR30-C** + +Intersection( CWE-248, ERR30-C) = Ø + +CWE-248 is only for languages that support exceptions. It lists C++ and Java, but not C. + +## Bibliography + +
\[ Brainbell.com \] Macros and Miscellaneous Pitfalls
\[ Horton 1990 \] Section 11, p. 168 Section 14, p. 254
\[ IEEE Std 1003.1-2013 \] XSH, System Interfaces, fopen
\[ Koenig 1989 \] Section 5.4, p. 73
\[ Summit 2005 \]
+ + +## Implementation notes + +None + +## References + +* CERT-C: [ERR30-C: Take care when reading errno](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql b/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql new file mode 100644 index 0000000000..8bf583faff --- /dev/null +++ b/c/cert/src/rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql @@ -0,0 +1,51 @@ +/** + * @id c/cert/function-call-before-errno-check + * @name ERR30-C: Do not call a function before checking errno + * @description After calling an errno-setting function, check errno before calling any other + * function. Failing to do so might end in errno being overwritten. + * @kind problem + * @precision high + * @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 + +/** + * A call to an `OutOfBandErrnoSettingFunction` + */ +class ErrnoSettingFunctionCall extends FunctionCall { + ErrnoSettingFunctionCall() { this.getTarget() instanceof InBandErrnoSettingFunction } +} + +/** + * A successor of an ErrnoSettingFunctionCall appearing + * before a check of errno + */ +ControlFlowNode errnoNotCheckedAfter(ErrnoSettingFunctionCall errnoSet) { + result = errnoSet + or + exists(ControlFlowNode mid | + result = mid.getASuccessor() and + mid = errnoNotCheckedAfter(errnoSet) and + // stop recursion on an error check + not result instanceof ErrnoRead + ) +} + +from ErrnoSettingFunctionCall errnoSet, FunctionCall fc +where + not isExcluded(fc, Contracts4Package::functionCallBeforeErrnoCheckQuery()) and + fc != errnoSet and + fc = errnoNotCheckedAfter(errnoSet) +select errnoSet, + "The value of `errno` is not checked after this call to `" + errnoSet.getTarget().getName() + "`." diff --git a/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.md b/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.md new file mode 100644 index 0000000000..e4b0a0e067 --- /dev/null +++ b/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.md @@ -0,0 +1,263 @@ +# ERR30-C: Do not rely solely on errno to determine if en error occurred in setlocale + +This query implements the CERT-C rule ERR30-C: + +> Take care when reading errno + + +## Description + +The value of `errno` is initialized to zero at program startup, but it is never subsequently set to zero by any C standard library function. The value of `errno` may be set to nonzero by a C standard library function call whether or not there is an error, provided the use of `errno` is not documented in the description of the function. It is meaningful for a program to inspect the contents of `errno` only after an error might have occurred. More precisely, `errno` is meaningful only after a library function that sets `errno` on error has returned an error code. + +According to Question 20.4 of C-FAQ \[[Summit 2005](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Summit05)\], + +> In general, you should detect errors by checking return values, and use `errno` only to distinguish among the various causes of an error, such as "File not found" or "Permission denied." (Typically, you use `perror` or `strerror` to print these discriminating error messages.) It's only necessary to detect errors with `errno` when a function does not have a unique, unambiguous, out-of-band error return (that is, because all of its possible return values are valid; one example is `atoi [*sic*]`). In these cases (and in these cases only; check the documentation to be sure whether a function allows this), you can detect errors by setting `errno` to 0, calling the function, and then testing `errno`. (Setting `errno` to 0 first is important, as no library function ever does that for you.) + + +Note that `atoi()` is not required to set the value of `errno`. + +Library functions fall into the following categories: + +* Those that set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator) +* Those that set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator) +* Those that do not promise to set `errno` +* Those with differing standards documentation + +## Library Functions that Set errno and Return an Out-of-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [out-of-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-out-of-banderrorindicator). That is, their return value on error can never be returned by a successful call. + +A program may check `errno` after invoking these library functions but is not required to do so. The program should not check the value of `errno` without first verifying that the function returned an error indicator. For example, `errno` should not be checked after calling `signal()` without first ensuring that `signal()` actually returned `SIG_ERR`. + +**Functions That Set `errno` and Return an Out-of-Band Error Indicator** + +
Function Name Return Value errno Value
ftell() -1L Positive
fgetpos() , fsetpos() Nonzero Positive
mbrtowc() , mbsrtowcs() (size_t)(-1) EILSEQ
signal() SIG_ERR Positive
wcrtomb() , wcsrtombs() (size_t)(-1) EILSEQ
mbrtoc16() , mbrtoc32() (size_t)(-1) EILSEQ
c16rtomb() , c32rtomb() (size_t)(-1) EILSEQ
+ + +## Library Functions that Set errno and Return an In-Band Error Indicator + +The C Standard specifies that the functions listed in the following table set `errno` and return an [in-band error indicator](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-in-banderrorindicator). That is, the return value when an error occurs is also a valid return value for successful calls. For example, the `strtoul()` function returns `ULONG_MAX` and sets `errno` to `ERANGE` if an error occurs. Because `ULONG_MAX` is a valid return value, the only way to confirm that an error occurred when LONG_MAX is returned is to check `errno`. + +The `fgetwc()` and `fputwc()` functions return `WEOF` in multiple cases, only one of which results in setting `errno`. The string conversion functions will return the maximum or minimum representable value and set `errno` to `ERANGE` if the converted value cannot be represented by the data type. However, if the conversion cannot happen because the input is invalid, the function will return `0`, and the output pointer parameter will be assigned the value of the input pointer parameter, provided the output parameter is non-null. + +A program that uses `errno` for error checking a function that returns an in-band error indicator must set `errno` to `0` before calling one of these library functions and then inspect `errno` before a subsequent library function call. + +**Functions that Set `errno` and Return an In-Band Error Indicator** + +
Function Name Return Value errno Value
fgetwc() , fputwc() WEOF EILSEQ
strtol() , wcstol() LONG_MIN or LONG_MAX ERANGE
strtoll() , wcstoll() LLONG_MIN or LLONG_MAX ERANGE
strtoul() , wcstoul() ULONG_MAX ERANGE
strtoull() , wcstoull() ULLONG_MAX ERANGE
strtoumax() , wcstoumax() UINTMAX_MAX ERANGE
strtod() , wcstod() 0 or ±HUGE_VAL ERANGE
strtof() , wcstof() 0 or ±HUGE_VALF ERANGE
strtold() , wcstold() 0 or ±HUGE_VALL ERANGE
strtoimax() , wcstoimax() INTMAX_MIN , INTMAX_MAX ERANGE
+ + +## Library Functions that Do Not Promise to Set errno + +The C Standard fails to document the behavior of `errno` for some functions. For example, the `setlocale()` function normally returns a null pointer in the event of an error, but no guarantees are made about setting `errno`. + +After calling one of these functions, a program should not rely solely on the value of `errno` to determine if an error occurred. The function might have altered `errno`, but this does not ensure that `errno` will properly indicate an error condition. If the program does check `errno` after calling one of these functions, it should set `errno` to 0 before the function call. + +## Library Functions with Differing Standards Documentation + +Some functions behave differently regarding `errno` in various standards. The `fopen()` function is one such example. When `fopen()` encounters an error, it returns a null pointer. The C Standard makes no mention of `errno` when describing `fopen()`. However, POSIX.1 declares that when `fopen()` encounters an error, it returns a null pointer and sets `errno` to a value indicating the error \[[IEEE Std 1003.1-2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\]. The implication is that a program conforming to C but not to POSIX (such as a Windows program) should not check `errno` after calling `fopen()`, but a POSIX program may check `errno` if `fopen()` returns a null pointer. + +## Library Functions and errno + +The following uses of `errno` are documented in the C Standard: + +* Functions defined in `` may set `errno` but are not required to. +* For numeric conversion functions in the `strtod`, `strtol`, `wcstod`, and `wcstol` families, if the correct result is outside the range of representable values, an appropriate minimum or maximum value is returned and the value `ERANGE` is stored in `errno`. For floating-point conversion functions in the `strtod` and `wcstod` families, if an underflow occurs, whether `errno` acquires the value `ERANGE` is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior). If the conversion fails, `0` is returned and `errno` is not set. +* The numeric conversion function `atof()` and those in the `atoi` family "need not affect the value of" `errno`. +* For mathematical functions in ``, if the integer expression `math_errhandling & MATH_ERRNO` is nonzero, on a domain error, `errno` acquires the value `EDOM`; on an overflow with default rounding or if the mathematical result is an exact infinity from finite arguments, `errno` acquires the value `ERANGE`; and on an underflow, whether `errno` acquires the value `ERANGE` is implementation-defined. +* If a request made by calling `signal()` cannot be honored, a value of `SIG_ERR` is returned and a positive value is stored in `errno`. +* The byte I/O functions, wide-character I/O functions, and multibyte conversion functions store the value of the macro `EILSEQ` in `errno` if and only if an encoding error occurs. +* On failure, `fgetpos()` and `fsetpos()` return nonzero and store an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* On failure, `ftell()` returns `-1L` and stores an [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) positive value in `errno`. +* The `perror()` function maps the error number in `errno` to a message and writes it to `stderr`. +The POSIX.1 standard defines the use of `errno` by many more functions (including the C standard library function). POSIX also has a small set of functions that are exceptions to the rule. These functions have no return value reserved to indicate an error, but they still set `errno` on error. To detect an error, an application must set `errno` to `0` before calling the function and check whether it is nonzero after the call. Affected functions include `strcoll()`, `strxfrm()`, `strerror()`, `wcscoll()`, `wcsxfrm()`, and `fwide()`. The C Standard allows these functions to set `errno` to a nonzero value on success. Consequently, this type of error checking should be performed only on POSIX systems. + +## Noncompliant Code Example (strtoul()) + +This noncompliant code example fails to set `errno` to `0` before invoking `strtoul()`. If an error occurs, `strtoul()` returns a valid value (`ULONG_MAX`), so `errno` is the only means of determining if `strtoul()` ran successfully. + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` +Any error detected in this manner may have occurred earlier in the program or may not represent an actual error. + +## Compliant Solution (strtoul()) + +This compliant solution sets `errno` to `0` before the call to `strtoul()` and inspects `errno` after the call: + +```cpp +#include +#include +#include + +void func(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); + if (endptr == c_str || (number == ULONG_MAX + && errno == ERANGE)) { + /* Handle error */ + } else { + /* Computation succeeded */ + } +} +``` + +## Noncompliant Code Example (ftell()) + +This noncompliant code example, after calling `ftell()`, examines `errno` without first checking whether the out-of-band indicator returned by `ftell() `indicates an error. + +```cpp +#include +#include + +void func(FILE* fp) { + errno=0; + ftell(fp); + if (errno) { + perror("ftell"); + } +} +``` + +## Compliant Solution (ftell()) + +This compliant solution first detects that `ftell() `failed using its out-of-band error indicator. Once an error has been confirmed, reading `errno` (implicitly by using the `perror()` function) is permitted. + +```cpp +#include +#include + +void func(FILE* fp) { + if (ftell(fp) == -1) { + perror("ftell"); + } +} +``` + +## Noncompliant Code Example (fopen()) + +This noncompliant code example may fail to diagnose errors because `fopen()` might not set `errno` even if an error occurs: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (errno != 0) { + /* Handle error */ + } +} +``` + +## Compliant Solution (fopen(), C) + +The C Standard makes no mention of `errno` when describing `fopen()`. In this compliant solution, the results of the call to `fopen()` are used to determine failure and `errno` is not checked: + +```cpp +#include + +void func(const char *filename) { + FILE *fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* An error occurred in fopen() */ + } +} +``` + +## Compliant Solution (fopen(), POSIX) + +In this compliant solution, `errno` is checked only after an error has already been detected by another means: + +```cpp +#include +#include + +void func(const char *filename) { + FILE *fileptr; + + errno = 0; + fileptr = fopen(filename, "rb"); + if (fileptr == NULL) { + /* + * An error occurred in fopen(); now it's valid + * to examine errno. + */ + perror(filename); + } +} +``` + +## Risk Assessment + +The improper use of `errno` may result in failing to detect an error condition or in incorrectly identifying an error condition when none exists. + +
Rule Severity Likelihood Remediation Cost Priority Level
ERR30-C Medium Probable Medium P8 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 errno-reset Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ERR30 Fully implemented
CodeSonar 7.1p0 LANG.STRUCT.RC Redundant Condition
Compass/ROSE Could detect violations of this rule by ensuring that each library function is accompanied by the proper treatment of errno
Coverity 2017.07 MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 Implemented
Helix QAC 2022.3 C2500, C2501, C2502, C2503 C++3172, C++3173, C++3174, C++3175, C++3176, C++3177, C++3178, C++3179, C++3183, C++3184
Klocwork 2022.3 CXX.ERRNO.NOT_SET CXX.ERRNO.NOT_CHECKED CXX.ERRNO.INCORRECTLY_CHECKED
LDRA tool suite 9.7.1 111 D, 121 D, 122 D, 132 D, 134 D Fully implemented
Parasoft C/C++test 2022.1 CERT_C-ERR30-a CERT_C-ERR30-b Properly use errno value Provide error handling for file opening errors right next to the call to fopen
Polyspace Bug Finder R2022b CERT C: Rule ERR30-C Checks for: Misuse of errnoisuse of errno, errno not resetrrno not reset. Rule fully covered.
PRQA QA-C 9.7 2500, 2501, 2502, 2503
+ + +## 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+ERR30-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
CERT C Secure Coding Standard EXP12-C. Do not ignore values returned by functions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Incorrectly setting and using errno \[inverrno\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-456 , Missing Initialization of a Variable 2017-07-05: 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-456 and ERR30-C** + +CWE-456 = EXP33-C + +CWE-456 = Union( ERR30-C, list) where list = + +* Reading potentially uninitialized variables besides errno +**CWE-248 and ERR30-C** + +Intersection( CWE-248, ERR30-C) = Ø + +CWE-248 is only for languages that support exceptions. It lists C++ and Java, but not C. + +## Bibliography + +
\[ Brainbell.com \] Macros and Miscellaneous Pitfalls
\[ Horton 1990 \] Section 11, p. 168 Section 14, p. 254
\[ IEEE Std 1003.1-2013 \] XSH, System Interfaces, fopen
\[ Koenig 1989 \] Section 5.4, p. 73
\[ Summit 2005 \]
+ + +## Implementation notes + +None + +## References + +* CERT-C: [ERR30-C: Take care when reading errno](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql b/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql new file mode 100644 index 0000000000..a7ccf8c041 --- /dev/null +++ b/c/cert/src/rules/ERR30-C/SetlocaleMightSetErrno.ql @@ -0,0 +1,98 @@ +/** + * @id c/cert/setlocale-might-set-errno + * @name ERR30-C: Do not rely solely on errno to determine if en error occurred in setlocale + * @description Do not rely solely on errno to determine if en error occurred in setlocale. + * @kind problem + * @precision high + * @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") } +} + +/** + * An `errno` read after setlocale + */ +ControlFlowNode errnoChecked(SetlocaleFunctionCall setlocale) { + result = setlocale + or + exists(ControlFlowNode mid | + result = mid.getASuccessor() and + mid = errnoChecked(setlocale) and + // stop recursion on a following errno-setting function call + not result instanceof OutOfBandErrnoSettingFunctionCert and + not result instanceof InBandErrnoSettingFunction + ) +} + +/** + * CFG nodes preceding a call to setlocale + */ +ControlFlowNode notZeroedPriorToSetlocale(SetlocaleFunctionCall fc) { + result = fc + or + exists(ControlFlowNode mid | + result = mid.getAPredecessor() and + mid = notZeroedPriorToSetlocale(fc) and + // stop recursion when `errno` is set to zero + not result instanceof ErrnoZeroed and + not result = any(ErrnoGuard g).getZeroedSuccessor() + ) +} + +/** + * A successor of a `setlocale` call appearing + * before a check of the return value + */ +ControlFlowNode returnNotCheckedAfter(SetlocaleFunctionCall setlocale) { + result = setlocale + or + exists(ControlFlowNode mid | + result = mid.getASuccessor() and + mid = returnNotCheckedAfter(setlocale) and + // stop recursion on a return value check + not ( + any(ControlStructure cs).getControllingExpr() = result and + DataFlow::localExprFlow(setlocale, result.(Operation).getAnOperand*()) + ) and + // stop recursion on a following errno setting function call + not result instanceof SetlocaleFunctionCall + ) +} + +from SetlocaleFunctionCall setlocale, ErrnoRead check, string msg +where + not isExcluded(setlocale, Contracts4Package::setlocaleMightSetErrnoQuery()) and + // errno is checked after setlocale + check = errnoChecked(setlocale) and + ( + // errno is not set to zero before the call + exists(ControlFlowNode cause | cause = notZeroedPriorToSetlocale(setlocale) | + // `errno` is not reset anywhere in the function + cause = setlocale.getEnclosingFunction().getBlock() + or + // `errno` is not reset after a call to a function + cause = any(FunctionCall fc2 | fc2 != setlocale) + ) and + msg = + "The value of `errno` may be different than `0` when `setlocale` is called. The following `errno` check might be invalid." + or + //errno is checked before the return value + check = returnNotCheckedAfter(setlocale) and + msg = "Do not read `errno` before checking the return value of a call to `setlocale`." + ) +select setlocale, msg diff --git a/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.md b/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.md new file mode 100644 index 0000000000..614bc9a5f4 --- /dev/null +++ b/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.md @@ -0,0 +1,207 @@ +# ERR32-C: Do not rely on indeterminate values of errno + +This query implements the CERT-C rule ERR32-C: + +> Do not rely on indeterminate values of errno + + +## Description + +According to the C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], the behavior of a program is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) when + +> the value of `errno` is referred to after a signal occurred other than as the result of calling the `abort` or `raise` function and the corresponding signal handler obtained a `SIG_ERR` return from a call to the `signal` function. + + +See [undefined behavior 133](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_133). + +A signal handler is allowed to call `signal();` if that fails, `signal()` returns `SIG_ERR` and sets `errno` to a positive value. However, if the event that caused a signal was external (not the result of the program calling `abort()` or `raise()`), the only functions the signal handler may call are `_Exit()` or `abort()`, or it may call `signal()` on the signal currently being handled; if `signal()` fails, the value of `errno` is [indeterminate](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue). + +This rule is also a special case of [SIG31-C. Do not access shared objects in signal handlers](https://wiki.sei.cmu.edu/confluence/display/c/SIG31-C.+Do+not+access+shared+objects+in+signal+handlers). The object designated by `errno` is of static storage duration and is not a `volatile sig_atomic_t`. As a result, performing any action that would require `errno` to be set would normally cause [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The C Standard, 7.14.1.1, paragraph 5, makes a special exception for `errno` in this case, allowing `errno` to take on an indeterminate value but specifying that there is no other [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). This special exception makes it possible to call `signal()` from within a signal handler without risking [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior), but the handler, and any code executed after the handler returns, must not depend on the value of `errno` being meaningful. + +## Noncompliant Code Example + +The `handler()` function in this noncompliant code example attempts to restore default handling for the signal indicated by `signum`. If the request to set the signal to default can be honored, the `signal()` function returns the value of the signal handler for the most recent successful call to the `signal()` function for the specified signal. Otherwise, a value of `SIG_ERR` is returned and a positive value is stored in `errno`. Unfortunately, the value of `errno` is indeterminate because the `handler()` function is called when an external signal is raised, so any attempt to read `errno` (for example, by the `perror()` function) is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): + +```cpp +#include +#include +#include + +typedef void (*pfv)(int); + +void handler(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler == SIG_ERR) { + perror("SIGINT handler"); /* Undefined behavior */ + /* Handle error */ + } +} + +int main(void) { + pfv old_handler = signal(SIGINT, handler); + if (old_handler == SIG_ERR) { + perror("SIGINT handler"); + /* Handle error */ + } + + /* Main code loop */ + + return EXIT_SUCCESS; +} + +``` +The call to `perror()` from `handler()` also violates [SIG30-C. Call only asynchronous-safe functions within signal handlers](https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers). + +## Compliant Solution + +This compliant solution does not reference `errno` and does not return from the signal handler if the `signal()` call fails: + +```cpp +#include +#include +#include + +typedef void (*pfv)(int); + +void handler(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler == SIG_ERR) { + abort(); + } +} + +int main(void) { + pfv old_handler = signal(SIGINT, handler); + if (old_handler == SIG_ERR) { + perror("SIGINT handler"); + /* Handle error */ + } + + /* Main code loop */ + + return EXIT_SUCCESS; +} + +``` + +## Noncompliant Code Example (POSIX) + +POSIX is less restrictive than C about what applications can do in signal handlers. It has a long list of [asynchronous-safe](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safe) functions that can be called. (See [SIG30-C. Call only asynchronous-safe functions within signal handlers](https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers).) Many of these functions set `errno` on error, which can lead to a signal handler being executed between a call to a failed function and the subsequent inspection of `errno`. Consequently, the value inspected is not the one set by that function but the one set by a function call in the signal handler. POSIX applications can avoid this problem by ensuring that signal handlers containing code that might alter `errno`; always save the value of `errno` on entry and restore it before returning. + +The signal handler in this noncompliant code example alters the value of `errno`. As a result, it can cause incorrect error handling if executed between a failed function call and the subsequent inspection of `errno`: + +```cpp +#include +#include +#include +#include + +void reaper(int signum) { + errno = 0; + for (;;) { + int rc = waitpid(-1, NULL, WNOHANG); + if ((0 == rc) || (-1 == rc && EINTR != errno)) { + break; + } + } + if (ECHILD != errno) { + /* Handle error */ + } +} + +int main(void) { + struct sigaction act; + act.sa_handler = reaper; + act.sa_flags = 0; + if (sigemptyset(&act.sa_mask) != 0) { + /* Handle error */ + } + if (sigaction(SIGCHLD, &act, NULL) != 0) { + /* Handle error */ + } + + /* ... */ + + return EXIT_SUCCESS; +} + +``` + +## Compliant Solution (POSIX) + +This compliant solution saves and restores the value of `errno` in the signal handler: + +```cpp +#include +#include +#include +#include + +void reaper(int signum) { + errno_t save_errno = errno; + errno = 0; + for (;;) { + int rc = waitpid(-1, NULL, WNOHANG); + if ((0 == rc) || (-1 == rc && EINTR != errno)) { + break; + } + } + if (ECHILD != errno) { + /* Handle error */ + } + errno = save_errno; +} + +int main(void) { + struct sigaction act; + act.sa_handler = reaper; + act.sa_flags = 0; + if (sigemptyset(&act.sa_mask) != 0) { + /* Handle error */ + } + if (sigaction(SIGCHLD, &act, NULL) != 0) { + /* Handle error */ + } + + /* ... */ + + return EXIT_SUCCESS; +} + +``` + +## Risk Assessment + +Referencing indeterminate values of `errno` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
ERR32-C Low Unlikely Low P3 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-ERR32
Compass/ROSE Could detect violations of this rule by looking for signal handlers that themselves call signal() . A violation is reported if the call fails and the handler therefore checks errno . A violation also exists if the signal handler modifies errno without first copying its value elsewhere
Coverity 2017.07 MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 Implemented
Helix QAC 2022.3 C2031, C4781, C4782, C4783 C++4781, C++4782, C++4783
Klocwork 2022.3 MISRA.INCL.SIGNAL.2012 MISRA.STDLIB.SIGNAL
LDRA tool suite 9.7.1 44 S Enhanced enforcement
Parasoft C/C++test 2022.1 CERT_C-ERR32-a Properly use errno value
Polyspace Bug Finder R2022b CERT C: Rule ERR32-C Checks for misuse of errno in a signal handler (rule fully covered)
PRQA QA-C 9.7 2031
+ + +## 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+ERR32-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
CERT C Secure Coding Standard SIG30-C. Call only asynchronous-safe functions within signal handlers Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard SIG31-C. Do not access shared objects in signal handlers Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.14.1.1, "The signal Function"
+ + +## Implementation notes + +The rule is enforced in the context of a single function. + +## References + +* CERT-C: [ERR32-C: Do not rely on indeterminate values of errno](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql b/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql new file mode 100644 index 0000000000..146d0cb30f --- /dev/null +++ b/c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql @@ -0,0 +1,95 @@ +/** + * @id c/cert/do-not-rely-on-indeterminate-values-of-errno + * @name ERR32-C: Do not rely on indeterminate values of errno + * @description Do not rely on indeterminate values of errno. This may result in undefined behavior. + * @kind problem + * @precision high + * @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 +import semmle.code.cpp.dataflow.DataFlow + +/** + * A check on `signal` call return value + * `if (signal(SIGINT, handler) == SIG_ERR)` + */ +class SignalCheckOperation extends EqualityOperation instanceof GuardCondition { + BasicBlock errorSuccessor; + + SignalCheckOperation() { + this.getAnOperand() = any(MacroInvocation m | m.getMacroName() = "SIG_ERR").getExpr() and + ( + this.getOperator() = "==" and + super.controls(errorSuccessor, true) + or + this.getOperator() = "!=" and + super.controls(errorSuccessor, false) + ) + } + + BasicBlock getCheckedSuccessor() { result != errorSuccessor and result = this.getASuccessor() } + + BasicBlock getErrorSuccessor() { result = errorSuccessor } +} + +/** + * Models signal handlers that call signal() and return + */ +class SignalCallingHandler extends SignalHandler { + SignalCallingHandler() { + // calls signal() on the handled signal + exists(SignalCall sCall | + sCall.getEnclosingFunction() = this and + DataFlow::localFlow(DataFlow::parameterNode(this.getParameter(0)), + DataFlow::exprNode(sCall.getArgument(0))) and + // does not abort on error + not exists(SignalCheckOperation sCheck, AbortCall abort | + DataFlow::localExprFlow(sCall, sCheck.getAnOperand()) and + abort = sCheck.getErrorSuccessor().(BlockStmt).getStmt(0).(ExprStmt).getExpr() + ) + ) + } +} + +/** + * CFG nodes preceeding `ErrnoRead` + */ +ControlFlowNode preceedErrnoRead(ErrnoRead er) { + result = er + or + exists(ControlFlowNode mid | + result = mid.getAPredecessor() and + mid = preceedErrnoRead(er) and + // stop recursion on calls to `abort` and `_Exit` + not result instanceof AbortCall and + // stop recursion on successful `SignalCheckOperation` + not result = any(SignalCheckOperation o).getCheckedSuccessor() + ) +} + +from ErrnoRead errno, SignalCall signal +where + not isExcluded(errno, Contracts5Package::doNotRelyOnIndeterminateValuesOfErrnoQuery()) and + exists(SignalCallingHandler handler | + // errno read after the handler returns + handler.getRegistration() = signal + or + // errno read inside the handler + signal.getEnclosingFunction() = handler + | + signal = preceedErrnoRead(errno) + ) +select errno, "`errno` has indeterminate value after this $@.", signal, signal.toString() diff --git a/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.md b/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.md new file mode 100644 index 0000000000..64c7d0a7f0 --- /dev/null +++ b/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.md @@ -0,0 +1,379 @@ +# ERR33-C: Detect and handle standard library errors + +This query implements the CERT-C rule ERR33-C: + +> Detect and handle standard library errors + + +## Description + +The majority of the standard library functions, including I/O functions and memory allocation functions, return either a valid value or a value of the correct return type that indicates an error (for example, −1 or a null pointer). Assuming that all calls to such functions will succeed and failing to check the return value for an indication of an error is a dangerous practice that may lead to [unexpected](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior) or [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) when an error occurs. It is essential that programs detect and appropriately handle all errors in accordance with an error-handling policy. + +The successful completion or failure of each of the standard library functions listed in the following table shall be determined either by comparing the function’s return value with the value listed in the column labeled “Error Return” or by calling one of the library functions mentioned in the footnotes. + +**Standard Library Functions** + +
Function Successful Return Error Return
aligned_alloc() Pointer to space NULL
asctime_s() 0 Nonzero
at_quick_exit() 0 Nonzero
atexit() 0 Nonzero
bsearch() Pointer to matching element NULL
bsearch_s() Pointer to matching element NULL
btowc() Converted wide character WEOF
c16rtomb() Number of bytes (size_t)(-1)
c32rtomb() Number of bytes (size_t)(-1)
calloc() Pointer to space NULL
clock() Processor time (clock_t)(-1)
cnd_broadcast() thrd_success thrd_error
cnd_init() thrd_success thrd_nomem or thrd_error
cnd_signal() thrd_success thrd_error
cnd_timedwait() thrd_success thrd_timedout or thrd_error
cnd_wait() thrd_success thrd_error
ctime_s() 0 Nonzero
fclose() 0 EOF (negative)
fflush() 0 EOF (negative)
fgetc() Character read EOF 1
fgetpos() 0 Nonzero, errno > 0
fgets() Pointer to string NULL
fgetwc() Wide character read WEOF 1
fopen() Pointer to stream NULL
fopen_s() 0 Nonzero
fprintf() Number of characters (nonnegative) Negative
fprintf_s() Number of characters (nonnegative) Negative
fputc() Character written EOF 2
fputs() Nonnegative EOF (negative)
fputwc() Wide character written WEOF
fputws() Nonnegative EOF (negative)
fread() Elements read Elements read
freopen() Pointer to stream NULL
freopen_s() 0 Nonzero
fscanf() Number of conversions (nonnegative) EOF (negative)
fscanf_s() Number of conversions (nonnegative) EOF (negative)
fseek() 0 Nonzero
fsetpos() 0 Nonzero, errno > 0
ftell() File position −1L , errno > 0
fwprintf() Number of wide characters (nonnegative) Negative
fwprintf_s() Number of wide characters (nonnegative) Negative
fwrite() Elements written Elements written
fwscanf() Number of conversions (nonnegative) EOF (negative)
fwscanf_s() Number of conversions (nonnegative) EOF (negative)
getc() Character read EOF 1
getchar() Character read EOF 1
getenv() Pointer to string NULL
getenv_s() Pointer to string NULL
gets_s() Pointer to string NULL
getwc() Wide character read WEOF
getwchar() Wide character read WEOF
gmtime() Pointer to broken-down time NULL
gmtime_s() Pointer to broken-down time NULL
localtime() Pointer to broken-down time NULL
localtime_s() Pointer to broken-down time NULL
malloc() Pointer to space NULL
mblen(), s != NULL Number of bytes −1
mbrlen(), s != NULL Number of bytes or status (size_t)(-1)
mbrtoc16() Number of bytes or status (size_t)(-1) , errno == EILSEQ
mbrtoc32() Number of bytes or status (size_t)(-1) , errno == EILSEQ
mbrtowc(), s != NULL Number of bytes or status (size_t)(-1) , errno == EILSEQ
mbsrtowcs() Number of non-null elements (size_t)(-1) , errno == EILSEQ
mbsrtowcs_s() 0 Nonzero
mbstowcs() Number of non-null elements (size_t)(-1)
mbstowcs_s() 0 Nonzero
mbtowc(), s != NULL Number of bytes −1
memchr() Pointer to located character NULL
mktime() Calendar time (time_t)(-1)
mtx_init() thrd_success thrd_error
mtx_lock() thrd_success thrd_error
mtx_timedlock() thrd_success thrd_timedout or thrd_error
mtx_trylock() thrd_success thrd_busy or thrd_error
mtx_unlock() thrd_success thrd_error
printf_s() Number of characters (nonnegative) Negative
putc() Character written EOF 2
putwc() Wide character written WEOF
raise() 0 Nonzero
realloc() Pointer to space NULL
remove() 0 Nonzero
rename() 0 Nonzero
setlocale() Pointer to string NULL
setvbuf() 0 Nonzero
scanf() Number of conversions (nonnegative) EOF (negative)
scanf_s() Number of conversions (nonnegative) EOF (negative)
signal() Pointer to previous function SIG_ERR , errno > 0
snprintf() Number of characters that would be written (nonnegative) Negative
snprintf_s() Number of characters that would be written (nonnegative) Negative
sprintf() Number of non-null characters written Negative
sprintf_s() Number of non-null characters written Negative
sscanf() Number of conversions (nonnegative) EOF (negative)
sscanf_s() Number of conversions (nonnegative) EOF (negative)
strchr() Pointer to located character NULL
strerror_s() 0 Nonzero
strftime() Number of non-null characters 0
strpbrk() Pointer to located character NULL
strrchr() Pointer to located character NULL
strstr() Pointer to located string NULL
strtod() Converted value 0 , errno == ERANGE
strtof() Converted value 0 , errno == ERANGE
strtoimax() Converted value INTMAX_MAX or INTMAX_MIN , errno == ERANGE
strtok() Pointer to first character of a token NULL
strtok_s() Pointer to first character of a token NULL
strtol() Converted value LONG_MAX or LONG_MIN , errno == ERANGE
strtold() Converted value 0, errno == ERANGE
strtoll() Converted value LLONG_MAX or LLONG_MIN , errno == ERANGE
strtoumax() Converted value UINTMAX_MAX , errno == ERANGE
strtoul() Converted value ULONG_MAX , errno == ERANGE
strtoull() Converted value ULLONG_MAX , errno == ERANGE
strxfrm() Length of transformed string >= n
swprintf() Number of non-null wide characters Negative
swprintf_s() Number of non-null wide characters Negative
swscanf() Number of conversions (nonnegative) EOF (negative)
swscanf_s() Number of conversions (nonnegative) EOF (negative)
thrd_create() thrd_success thrd_nomem or thrd_error
thrd_detach() thrd_success thrd_error
thrd_join() thrd_success thrd_error
thrd_sleep() 0 Negative
time() Calendar time (time_t)(-1)
timespec_get() Base 0
tmpfile() Pointer to stream NULL
tmpfile_s() 0 Nonzero
tmpnam() Non-null pointer NULL
tmpnam_s() 0 Nonzero
tss_create() thrd_success thrd_error
tss_get() Value of thread-specific storage 0
tss_set() thrd_success thrd_error
ungetc() Character pushed back EOF (see below )
ungetwc() Character pushed back WEOF
vfprintf() Number of characters (nonnegative) Negative
vfprintf_s() Number of characters (nonnegative) Negative
vfscanf() Number of conversions (nonnegative) EOF (negative)
vfscanf_s() Number of conversions (nonnegative) EOF (negative)
vfwprintf() Number of wide characters (nonnegative) Negative
vfwprintf_s() Number of wide characters (nonnegative) Negative
vfwscanf() Number of conversions (nonnegative) EOF (negative)
vfwscanf_s() Number of conversions (nonnegative) EOF (negative)
vprintf_s() Number of characters (nonnegative) Negative
vscanf() Number of conversions (nonnegative) EOF (negative)
vscanf_s() Number of conversions (nonnegative) EOF (negative)
vsnprintf() Number of characters that would be written (nonnegative) Negative
vsnprintf_s() Number of characters that would be written (nonnegative) Negative
vsprintf() Number of non-null characters (nonnegative) Negative
vsprintf_s() Number of non-null characters (nonnegative) Negative
vsscanf() Number of conversions (nonnegative) EOF (negative)
vsscanf_s() Number of conversions (nonnegative) EOF (negative)
vswprintf() Number of non-null wide characters Negative
vswprintf_s() Number of non-null wide characters Negative
vswscanf() Number of conversions (nonnegative) EOF (negative)
vswscanf_s() Number of conversions (nonnegative) EOF (negative)
vwprintf_s() Number of wide characters (nonnegative) Negative
vwscanf() Number of conversions (nonnegative) EOF (negative)
vwscanf_s() Number of conversions (nonnegative) EOF (negative)
wcrtomb() Number of bytes stored (size_t)(-1)
wcschr() Pointer to located wide character NULL
wcsftime() Number of non-null wide characters 0
wcspbrk() Pointer to located wide character NULL
wcsrchr() Pointer to located wide character NULL
wcsrtombs() Number of non-null bytes (size_t)(-1) , errno == EILSEQ
wcsrtombs_s() 0 Nonzero
wcsstr() Pointer to located wide string NULL
wcstod() Converted value 0 , errno == ERANGE
wcstof() Converted value 0 , errno == ERANGE
wcstoimax() Converted value INTMAX_MAX or INTMAX_MIN , errno == ERANGE
wcstok() Pointer to first wide character of a token NULL
wcstok_s() Pointer to first wide character of a token NULL
wcstol() Converted value LONG_MAX or LONG_MIN , errno == ERANGE
wcstold() Converted value 0 , errno == ERANGE
wcstoll() Converted value LLONG_MAX or LLONG_MIN , errno == ERANGE
wcstombs() Number of non-null bytes (size_t)(-1)
wcstombs_s() 0 Nonzero
wcstoumax() Converted value UINTMAX_MAX , errno == ERANGE
wcstoul() Converted value ULONG_MAX , errno == ERANGE
wcstoull() Converted value ULLONG_MAX , errno == ERANGE
wcsxfrm() Length of transformed wide string >= n
wctob() Converted character EOF
wctomb(), s != NULL Number of bytes stored −1
wctomb_s(), s != NULL Number of bytes stored −1
wctrans() Valid argument to towctrans 0
wctype() Valid argument to iswctype 0
wmemchr() Pointer to located wide character NULL
wprintf_s() Number of wide characters (nonnegative) Negative
wscanf() Number of conversions (nonnegative) EOF (negative)
wscanf_s() Number of conversions (nonnegative) EOF (negative)
+Note: According to [FIO35-C](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152389)[. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char)](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152389), callers should verify end-of-file and file errors for the functions in this table as follows: + + +1 By calling `ferror()` and `feof()`2 By calling `ferror()` + +The `ungetc()` function does not set the error indicator even when it fails, so it is not possible to check for errors reliably unless it is known that the argument is not equal to `EOF`. The C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] states that "one character of pushback is guaranteed," so this should not be an issue if, at most, one character is ever pushed back before reading again. (See [FIO13-C](https://wiki.sei.cmu.edu/confluence/display/c/FIO13-C.+Never+push+back+anything+other+than+one+read+character)[. Never push back anything other than one read character](https://wiki.sei.cmu.edu/confluence/display/c/FIO13-C.+Never+push+back+anything+other+than+one+read+character).) + +## Noncompliant Code Example (setlocale()) + +In this noncompliant code example, the function `utf8_to_wcs()` attempts to convert a sequence of UTF-8 characters to wide characters. It first invokes `setlocale()` to set the global locale to the implementation-defined `en_US.UTF-8` but does not check for failure. The `setlocale()` function will fail by returning a null pointer, for example, when the locale is not installed. The function may fail for other reasons as well, such as the lack of resources. Depending on the sequence of characters pointed to by `utf8`, the subsequent call to `mbstowcs()` may fail or result in the function storing an unexpected sequence of wide characters in the supplied buffer `wcs`. + +```cpp +#include +#include + +int utf8_to_wcs(wchar_t *wcs, size_t n, const char *utf8, + size_t *size) { + if (NULL == size) { + return -1; + } + setlocale(LC_CTYPE, "en_US.UTF-8"); + *size = mbstowcs(wcs, utf8, n); + return 0; +} + +``` + +## Compliant Solution (setlocale()) + +This compliant solution checks the value returned by `setlocale()` and avoids calling `mbstowcs()` if the function fails. The function also takes care to restore the locale to its initial setting before returning control to the caller. + +```cpp +#include +#include + +int utf8_to_wcs(wchar_t *wcs, size_t n, const char *utf8, + size_t *size) { + if (NULL == size) { + return -1; + } + const char *save = setlocale(LC_CTYPE, "en_US.UTF-8"); + if (NULL == save) { + return -1; + } + + *size = mbstowcs(wcs, utf8, n); + if (NULL == setlocale(LC_CTYPE, save)) { + return -1; + } + return 0; +} + +``` + +## Noncompliant Code Example (calloc()) + +In this noncompliant code example, `temp_num`,` tmp2`, and `num_of_records` are derived from a [tainted source](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedsource). Consequently, an attacker can easily cause `calloc()` to fail by providing a large value for `num_of_records`. + +```cpp +#include +#include + +enum { SIG_DESC_SIZE = 32 }; + +typedef struct { + char sig_desc[SIG_DESC_SIZE]; +} signal_info; + +void func(size_t num_of_records, size_t temp_num, + const char *tmp2, size_t tmp2_size_bytes) { + signal_info *start = (signal_info *)calloc(num_of_records, + sizeof(signal_info)); + + if (tmp2 == NULL) { + /* Handle error */ + } else if (temp_num > num_of_records) { + /* Handle error */ + } else if (tmp2_size_bytes < SIG_DESC_SIZE) { + /* Handle error */ + } + + signal_info *point = start + temp_num - 1; + memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE); + point->sig_desc[SIG_DESC_SIZE - 1] = '\0'; + /* ... */ + free(start); +} +``` +When `calloc()` fails, it returns a null pointer that is assigned to `start`. If `start` is null, an attacker can provide a value for `temp_num` that, when scaled by `sizeof(signal_info)`, references a writable address to which control is eventually transferred. The contents of the string referenced by `tmp2` can then be used to overwrite the address, resulting in an arbitrary code execution [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability). + +## Compliant Solution (calloc()) + +To correct this error, ensure the pointer returned by `calloc()` is not null: + +```cpp +#include +#include + +enum { SIG_DESC_SIZE = 32 }; + +typedef struct { + char sig_desc[SIG_DESC_SIZE]; +} signal_info; + +void func(size_t num_of_records, size_t temp_num, + const char *tmp2, size_t tmp2_size_bytes) { + signal_info *start = (signal_info *)calloc(num_of_records, + sizeof(signal_info)); + if (start == NULL) { + /* Handle allocation error */ + } else if (tmp2 == NULL) { + /* Handle error */ + } else if (temp_num > num_of_records) { + /* Handle error */ + } else if (tmp2_size_bytes < SIG_DESC_SIZE) { + /* Handle error */ + } + + signal_info *point = start + temp_num - 1; + memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE); + point->sig_desc[SIG_DESC_SIZE - 1] = '\0'; + /* ... */ + free(start); +} +``` + +## Noncompliant Code Example (realloc()) + +This noncompliant code example calls `realloc()` to resize the memory referred to by `p`. However, if `realloc()` fails, it returns a null pointer and the connection between the original block of memory and `p` is lost, resulting in a memory leak. + +```cpp +#include + +void *p; +void func(size_t new_size) { + if (new_size == 0) { + /* Handle error */ + } + p = realloc(p, new_size); + if (p == NULL) { + /* Handle error */ + } +} +``` +This code example complies with [MEM04-C](https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations)[. Do not perform zero-length allocations](https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations). + +## Compliant Solution (realloc()) + +In this compliant solution, the result of `realloc()` is assigned to the temporary pointer `q` and validated before it is assigned to the original pointer `p`: + +```cpp +#include + +void *p; +void func(size_t new_size) { + void *q; + + if (new_size == 0) { + /* Handle error */ + } + + q = realloc(p, new_size); + if (q == NULL) { + /* Handle error */ + } else { + p = q; + } +} +``` + +## Noncompliant Code Example (fseek()) + +In this noncompliant code example, the `fseek()` function is used to set the file position to a location `offset` in the file referred to by `file` prior to reading a sequence of bytes from the file. However, if an I/O error occurs during the seek operation, the subsequent read will fill the buffer with the wrong contents. + +```cpp +#include + +size_t read_at(FILE *file, long offset, + void *buf, size_t nbytes) { + fseek(file, offset, SEEK_SET); + return fread(buf, 1, nbytes, file); +} + +``` + +## Compliant Solution (fseek()) + +According to the C Standard, the `fseek()` function returns a nonzero value to indicate that an error occurred. This compliant solution tests for this condition before reading from a file to eliminate the chance of operating on the wrong portion of the file if `fseek()` fails: + +```cpp +#include + +size_t read_at(FILE *file, long offset, + void *buf, size_t nbytes) { + if (fseek(file, offset, SEEK_SET) != 0) { + /* Indicate error to caller */ + return 0; + } + return fread(buf, 1, nbytes, file); +} + +``` + +## Noncompliant Code Example (snprintf()) + +In this noncompliant code example, `snprintf()` is assumed to succeed. However, if the call fails (for example, because of insufficient memory, as described in GNU libc bug [441945](http://bugzilla.redhat.com/show_bug.cgi?id=441945)), the subsequent call to `log_message()` has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) because the character buffer is uninitialized and need not be null-terminated. + +```cpp +#include + +extern void log_message(const char *); + +void f(int i, int width, int prec) { + char buf[40]; + snprintf(buf, sizeof(buf), "i = %*.*i", width, prec, i); + log_message(buf); + /* ... */ +} + +``` + +## Compliant Solution (snprintf()) + +This compliant solution does not assume that `snprintf()` will succeed regardless of its arguments. It tests the return value of `snprintf()` before subsequently using the formatted buffer. This compliant solution also treats the case where the static buffer is not large enough for `snprintf()` to append the terminating null character as an error. + +```cpp +#include +#include + +extern void log_message(const char *); + +void f(int i, int width, int prec) { + char buf[40]; + int n; + n = snprintf(buf, sizeof(buf), "i = %*.*i", width, prec, i); + if (n < 0 || n >= sizeof(buf)) { + /* Handle snprintf() error */ + strcpy(buf, "unknown error"); + } + log_message(buf); +} + +``` + +## Compliant Solution (snprintf(null)) + +If unknown, the length of the formatted string can be discovered by invoking `snprintf()` with a null buffer pointer to determine the size required for the output, then dynamically allocating a buffer of sufficient size, and finally calling `snprintf()` again to format the output into the dynamically allocated buffer. Even with this approach, the success of all calls still needs to be tested, and any errors must be appropriately handled. A possible optimization is to first attempt to format the string into a reasonably small buffer allocated on the stack and, only when the buffer turns out to be too small, dynamically allocate one of a sufficient size: + +```cpp +#include +#include +#include + +extern void log_message(const char *); + +void f(int i, int width, int prec) { + char buffer[20]; + char *buf = buffer; + int n = sizeof(buffer); + const char fmt[] = "i = %*.*i"; + + n = snprintf(buf, n, fmt, width, prec, i); + if (n < 0) { + /* Handle snprintf() error */ + strcpy(buffer, "unknown error"); + goto write_log; + } + + if (n < sizeof(buffer)) { + goto write_log; + } + + buf = (char *)malloc(n + 1); + if (NULL == buf) { + /* Handle malloc() error */ + strcpy(buffer, "unknown error"); + goto write_log; + } + + n = snprintf(buf, n, fmt, width, prec, i); + if (n < 0) { + /* Handle snprintf() error */ + strcpy(buffer, "unknown error"); + } + +write_log: + log_message(buf); + + if (buf != buffer) { + free(buf); + } +} + +``` +This solution uses the `goto` statement, as suggested in [MEM12-C](https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consider+using+a+goto+chain+when+leaving+a+function+on+error+when+using+and+releasing+resources)[. Consider using a goto chain when leaving a function on error when using and releasing resources](https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consider+using+a+goto+chain+when+leaving+a+function+on+error+when+using+and+releasing+resources). + +## Exceptions + +**ERR33-C-EX1:** It is acceptable to ignore the return value of a function if: + +* that function cannot fail. +* its return value is inconsequential; that is, it does not indicate an error. +* it is one of a handful of functions whose return values are not traditionally checked. These functions are listed in the following table: +**Functions for which Return Values Need Not Be Checked** + +
Function Successful Return Error Return
putchar() Character written EOF
putwchar() Wide character written WEOF
puts() Nonnegative EOF (negative)
printf() , vprintf() Number of characters (nonnegative) Negative
wprintf() , vwprintf() Number of wide characters (nonnegative) Negative
kill_dependency() The input parameter NA
memcpy() , wmemcpy() The destination input parameter NA
memmove() , wmemmove() The destination input parameter NA
strcpy() , wcscpy() The destination input parameter NA
strncpy() , wcsncpy() The destination input parameter NA
strcat() , wcscat() The destination input parameter NA
strncat() , wcsncat() The destination input parameter NA
memset() , wmemset() The destination input parameter NA
+The function's results should be explicitly cast to `void` to signify programmer intent: + + +```cpp +int main() { + (void) printf("Hello, world\n"); // printf() return value safely ignored +} + +``` + +## Risk Assessment + +Failing to detect error conditions can lead to unpredictable results, including [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination) and [denial-of-service attacks](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-denial-of-service) or, in some situations, could even allow an attacker to run arbitrary code. + +
Rule Severity Likelihood Remediation Cost Priority Level
ERR33-C High Likely Medium P18 L1
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 error-information-unusederror-information-unused-computed Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ERR33
CodeSonar 7.1p0 LANG.FUNCS.IRV Ignored return value
Compass/ROSE Can detect violations of this recommendation when checking for violations of EXP12-C. Do not ignore values returned by functions and EXP34-C . Do not dereference null pointers
Coverity 2017.07 MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 Implemented
Helix QAC 2022.3 C3200 C++2820, C++2821, C++2822, C++2823, C++2824, C++2930, C++2931, C++2932, C++2933, C++2934, C++3802, C++3803, C++3804
Klocwork 2022.3 NPD.CHECK.MUST NPD.FUNC.MUST SV.RVT.RETVAL_NOTTESTED
LDRA tool suite 9.7.1 80 D Partially implemented
Parasoft C/C++test 2022.1 CERT_C-ERR33-a CERT_C-ERR33-b CERT_C-ERR33-c CERT_C-ERR33-d The value returned by a function having non-void return type shall be used The value returned by a function having non-void return type shall be used Avoid null pointer dereferencing Always check the returned value of non-void function
Parasoft Insure++ Runtime analysis
PC-lint Plus 1.4 534 Partially supported
Polyspace Bug Finder R2022b CERT C: Rule ERR33-C Checks for: Errno not checkedrrno not checked, return value of a sensitive function not checkedeturn value of a sensitive function not checked, unprotected dynamic memory allocationnprotected dynamic memory allocation. Rule partially covered.
PRQA QA-C 9.7 3200 Partially implemented
PRQA QA-C++ 4.4 2820, 2821, 2822, 2823, 2824, 2930, 2931, 2932, 2933, 2934, 3802, 3803, 3804
RuleChecker 22.04 error-information-unused Partially checked
TrustInSoft Analyzer 1.38 pointer arithmetic Exhaustively verified.
+ + +## Related Vulnerabilities + +The [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in Adobe Flash \[[VU\#159523](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-VU%23159523)\] arises because Flash neglects to check the return value from `calloc()`. Even when `calloc()` returns a null pointer, Flash writes to an offset from the return value. Dereferencing a null pointer usually results in a program crash, but dereferencing an offset from a null pointer allows an [exploit](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit) to succeed without crashing the program. + +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+ERR33-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
CERT C Secure Coding Standard ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard EXP34-C . Do not dereference null pointers Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard FIO13-C . Never push back anything other than one read character Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MEM04-C . Do not perform zero-length allocations Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MEM12-C . Consider using a goto chain when leaving a function on error when using and releasing resources Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ERR10-CPP. Check for error conditions Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C FIO04-CPP. Detect and handle input and output errors Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Failing to detect and handle standard library errors \[liberr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-252 , Unchecked Return Value 2017-07-06: CERT: Partial overlap
CWE 2.11 CWE-253 , Incorrect Check of Function Return Value 2017-07-06: CERT: Partial overlap
CWE 2.11 CWE-391 , Unchecked Error Condition 2017-07-06: 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-252/CWE-253/CWE-391 and ERR33-C/POS34-C** + +Independent( ERR33-C, POS54-C, FLP32-C, ERR34-C) Intersection( CWE-252, CWE-253) = Ø CWE-391 = Union( CWE-252, CWE-253) CWE-391 = Union( ERR33-C, POS34-C, list) where list = + +* Ignoring return values of functions outside the C or POSIX standard libraries + +## Bibliography + +
\[ DHS 2006 \] Handle All Errors Safely
\[ Henricson 1997 \] Recommendation 12.1, "Check for All Errors Reported from Functions"
\[ ISO/IEC 9899:2011 \] Subclause 7.21.7.10, "The ungetc Function"
\[ VU\#159523 \]
+ + +## Implementation notes + +The rule is enforced in the context of a single function. + +## References + +* CERT-C: [ERR33-C: Detect and handle standard library errors](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql b/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql new file mode 100644 index 0000000000..5e473b226e --- /dev/null +++ b/c/cert/src/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql @@ -0,0 +1,475 @@ +/** + * @id c/cert/detect-and-handle-standard-library-errors + * @name ERR33-C: Detect and handle standard library errors + * @description Detect and handle standard library errors. Undetected failures can lead to + * unexpected or undefined behavior. + * @kind problem + * @precision high + * @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 + */ + +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" + or + spec = "=NULL" and result.(EqualityOperation).getAnOperand() instanceof NULL + or + spec = "=EOF" and result.(EqualityOperation).getAnOperand() = any(EOFInvocation i).getExpr() + or + spec = "=WEOF" and result.(EqualityOperation).getAnOperand() = any(WEOFInvocation i).getExpr() + or + spec = "=thrd_error" and + result.(EqualityOperation).getAnOperand().(EnumConstantAccess).toString() = "thrd_error" + or + spec = "=thrd_nomem" and + result.(EqualityOperation).getAnOperand().(EnumConstantAccess).toString() = "thrd_nomem" + or + spec = "=thrd_timedout" and + result.(EqualityOperation).getAnOperand().(EnumConstantAccess).toString() = "thrd_timedout" + or + spec = "=thrd_busy" and + result.(EqualityOperation).getAnOperand().(EnumConstantAccess).toString() = "thrd_busy" + or + spec = "=UINTMAX_MAX" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = "UINTMAX_MAX").getExpr() + or + spec = "=ULONG_MAX" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = "ULONG_MAX").getExpr() + or + spec = "=ULLONG_MAX" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = "ULLONG_MAX").getExpr() + or + spec = "=SIG_ERR" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = "SIG_ERR").getExpr() + or + spec = "=INTMAX_MAX" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = ["INTMAX_MAX", "INTMAX_MIN"]).getExpr() + or + spec = "=LONG_MAX" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = ["LONG_MAX", "LONG_MIN"]).getExpr() + or + spec = "=LLONG_MAX" and + result.(EqualityOperation).getAnOperand() = + any(MacroInvocation i | i.getMacroName() = ["LLONG_MAX", "LLONG_MIN"]).getExpr() + or + spec = "=-1" and + result.(EqualityOperation).getAnOperand().(UnaryMinusExpr).getOperand().getValue() = "1" + or + spec = "=int" and + result.(EqualityOperation).getAnOperand().getType() instanceof IntType + or + spec = "<0" and + result.(RelationalOperation).getOperator() = ["<", ">="] and + result.(RelationalOperation).getGreaterOperand().getValue() = "0" + or + spec = "="] and + result.(RelationalOperation).getLesserOperand().getType() instanceof IntType +} + +/** + * Calls whose return value must be checked + * using an `errOperator` against `errValue` + */ +abstract class ExpectedErrReturn extends FunctionCall { + ComparisonOperation errOperator; + + ComparisonOperation getErrOperator() { result = errOperator } +} + +/** + * Calls that must be checked agains `0`. + * + * example: + * ``` + * if (strftime(b, 10, "", local) == 0) { ... } + * ``` + */ +class ExpectedErrReturnEqZero extends ExpectedErrReturn { + ExpectedErrReturnEqZero() { + this.getTarget() + .hasName([ + "asctime_s", "at_quick_exit", "atexit", "ctime_s", "fgetpos", "fopen_s", "freopen_s", + "fseek", "fsetpos", "mbsrtowcs_s", "mbstowcs_s", "raise", "remove", "rename", "setvbuf", + "strerror_s", "strftime", "strtod", "strtof", "strtold", "timespec_get", "tmpfile_s", + "tmpnam_s", "tss_get", "wcsftime", "wcsrtombs_s", "wcstod", "wcstof", "wcstold", + "wcstombs_s", "wctrans", "wctype" + ]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=0") } +} + +/** + * Calls that must be checked agains `NULL`. + * + * example: + * ``` + * if (aligned_alloc(0, 0) == NULL) { ... } + * ``` + */ +class ExpectedErrReturnEqNull extends ExpectedErrReturn { + ExpectedErrReturnEqNull() { + this.getTarget() + .hasName([ + "aligned_alloc", "bsearch_s", "bsearch", "calloc", "fgets", "fopen", "freopen", + "getenv_s", "getenv", "gets_s", "gmtime_s", "gmtime", "localtime_s", "localtime", + "malloc", "memchr", "realloc", "setlocale", "strchr", "strpbrk", "strrchr", "strstr", + "strtok_s", "strtok", "tmpfile", "tmpnam", "wcschr", "wcspbrk", "wcsrchr", "wcsstr", + "wcstok_s", "wcstok", "wmemchr" + ]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=0") } +} + +/** + * Calls that must be checked agains `EOF` or `WEOF`. + * + * example: + * ``` + * if (wctob(0) == EOF) { ... } + * ``` + */ +class ExpectedErrReturnEqEof extends ExpectedErrReturn { + ExpectedErrReturnEqEof() { + this.getTarget() + .hasName([ + "fclose", "fflush", "fputs", "fputws", "fscanf_s", "fscanf", "fwscanf_s", "fwscanf", + "scanf_s", "scanf", "sscanf_s", "sscanf", "swscanf_s", "swscanf", "ungetc", "vfscanf_s", + "vfscanf", "vfwscanf_s", "vfwscanf", "vscanf_s", "vscanf", "vsscanf_s", "vsscanf", + "vswscanf_s", "vswscanf", "vwscanf_s", "vwscanf", "wctob", "wscanf_s", "wscanf", + "fgetc", "fputc", "getc", "getchar", "putc", "putchar", "puts" + ]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=EOF") } +} + +/** + * Calls that must be checked agains`WEOF`. + * + * example: + * ``` + * if (btowc(0) == WEOF) { ... } + * ``` + */ +class ExpectedErrReturnEqWeof extends ExpectedErrReturn { + ExpectedErrReturnEqWeof() { + this.getTarget() + .hasName(["btowc", "fgetwc", "fputwc", "getwc", "getwchar", "putwc", "ungetwc", "putwchar"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=WEOF") } +} + +/** + * Calls that must be checked agains an enun constant. + * + * example: + * ``` + * if (cnd_broadcast(&q) == thrd_error) { ... } + * ``` + */ +class ExpectedErrReturnEqEnumConstant_thrd_error extends ExpectedErrReturn { + ExpectedErrReturnEqEnumConstant_thrd_error() { + this.getTarget() + .hasName([ + "cnd_broadcast", "cnd_init", "cnd_signal", "cnd_timedwait", "cnd_wait", "mtx_init", + "mtx_lock", "mtx_timedlock", "mtx_trylock", "mtx_unlock", "thrd_create", "thrd_detach", + "thrd_join", "tss_create", "tss_set" + ]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=thrd_error") } +} + +class ExpectedErrReturnEqEnumConstant_thrd_nomem extends ExpectedErrReturn { + ExpectedErrReturnEqEnumConstant_thrd_nomem() { + this.getTarget().hasName(["cnd_init", "thrd_create"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=thrd_nomem") } +} + +class ExpectedErrReturnEqEnumConstant_thrd_timedout extends ExpectedErrReturn { + ExpectedErrReturnEqEnumConstant_thrd_timedout() { + this.getTarget().hasName(["cnd_timedwait", "mtx_timedlock"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=thrd_timedout") } +} + +class ExpectedErrReturnEqEnumConstant_thrd_busy extends ExpectedErrReturn { + ExpectedErrReturnEqEnumConstant_thrd_busy() { this.getTarget().hasName(["mtx_trylock"]) } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=thrd_busy") } +} + +/** + * Calls that must be checked agains a macro. + * + * example: + * ``` + * if (strtoumax(str, &endptr, 0) == UINTMAX_MAX) { ... } + * ``` + */ +class ExpectedErrReturnEqMacroInvocation_UINTMAX_MAX extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_UINTMAX_MAX() { + this.getTarget().hasName(["strtoumax", "wcstoumax"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=UINTMAX_MAX") } +} + +class ExpectedErrReturnEqMacroInvocation_ULONG_MAX extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_ULONG_MAX() { + this.getTarget().hasName(["strtoul", "wcstoul"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=ULONG_MAX") } +} + +class ExpectedErrReturnEqMacroInvocation_ULLONG_MAX extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_ULLONG_MAX() { + this.getTarget().hasName(["strtoull", "wcstoull"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=ULLONG_MAX") } +} + +class ExpectedErrReturnEqMacroInvocation_SIG_ERR extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_SIG_ERR() { this.getTarget().hasName(["signal"]) } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=SIG_ERR") } +} + +class ExpectedErrReturnEqMacroInvocation_INTMAX_MAX extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_INTMAX_MAX() { + this.getTarget().hasName(["strtoimax", "wcstoimax"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=INTMAX_MAX") } +} + +class ExpectedErrReturnEqMacroInvocation_LONG_MAX extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_LONG_MAX() { this.getTarget().hasName(["strtol", "wcstol"]) } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=LONG_MAX") } +} + +class ExpectedErrReturnEqMacroInvocation_LLONG_MAX extends ExpectedErrReturn { + ExpectedErrReturnEqMacroInvocation_LLONG_MAX() { + this.getTarget().hasName(["strtoll", "wcstoll"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=LLONG_MAX") } +} + +/** + * Calls that must be checked agains `-1`. + * + * example: + * ``` + * if (clock() == (clock_t)(-1)) { ... } + * ``` + */ +class ExpectedErrReturnEqMinusOne extends ExpectedErrReturn { + ExpectedErrReturnEqMinusOne() { + this.getTarget() + .hasName([ + "c16rtomb", "c32rtomb", "clock", "ftell", "mbrtoc16", "mbrtoc32", "mbsrtowcs", + "mbstowcs", "mktime", "time", "wcrtomb", "wcsrtombs", "wcstombs" + ]) + or + // functions that behave differently when the first argument is NULL + not this.getArgument(0) instanceof NULL and + this.getTarget().hasName(["mblen", "mbrlen", "mbrtowc", "mbtowc", "wctomb_s", "wctomb"]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=-1") } +} + +/** + * Calls that must be checked agains an integer value. + * + * example: + * ``` + * if (fread(b, sizeof *b, SIZE, fp) == SIZE) { ... } + * ``` + */ +class ExpectedErrReturnEqInt extends ExpectedErrReturn { + ExpectedErrReturnEqInt() { this.getTarget().hasName(["fread", "fwrite"]) } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("=int") } +} + +/** + * Calls that must be compared to `0`. + * + * example: + * ``` + * if (snprintf(NULL, 0, fmt, sqrt(2) >= 0) { ... } + * ``` + */ +class ExpectedErrReturnLtZero extends ExpectedErrReturn { + ExpectedErrReturnLtZero() { + this.getTarget() + .hasName([ + "fprintf_s", "fprintf", "fwprintf_s", "fwprintf", "printf_s", "snprintf_s", "snprintf", + "sprintf_s", "sprintf", "swprintf_s", "swprintf", "thrd_sleep", "vfprintf_s", + "vfprintf", "vfwprintf_s", "vfwprintf", "vprintf_s", "vsnprintf_s", "vsnprintf", + "vsprintf_s", "vsprintf", "vswprintf_s", "vswprintf", "vwprintf_s", "wprintf_s", + "printf", "vprintf", "wprintf", "vwprintf" + ]) + } + + override ComparisonOperation getErrOperator() { result = getAValidComparison("<0") } +} + +/** + * Calls that must be compared to an integer value. + * + * example: + * ``` + * if (strxfrm(out, in, sizeof out) >= 10) { ... } + * ``` + */ +class ExpectedErrReturnLtInt extends ExpectedErrReturn { + ExpectedErrReturnLtInt() { this.getTarget().hasName(["strxfrm", "wcsxfrm"]) } + + override ComparisonOperation getErrOperator() { result = getAValidComparison(" 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 + +
SEI CERT C++ Coding Standard VOID EXP16-CPP. Avoid conversions using void pointers
ISO/IEC TR 24772:2013 Likely incorrect expressions \[KOA\]
ISO/IEC TS 17961 Comparing function addresses to zero \[funcaddr\]
MITRE CWE CWE-480 , Use of incorrect operator CWE-482 , Comparing instead of assigning
+ + +## 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 3070f8d310..48b9487728 100644 --- a/c/cert/src/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.ql +++ b/c/cert/src/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.ql @@ -8,15 +8,19 @@ * @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.GlobalValueNumberingImpl +import semmle.code.cpp.valuenumbering.GlobalValueNumbering /** Holds if the function's return value is derived from the `AliasParamter` p. */ predicate returnValueDependsOnAliasParameter(AliasParameter p) { 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) + +
Taxonomy Taxonomy item Relationship
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes \[HFC\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Type System \[IHN\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.8 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP55-CPP. Do not access a cv-qualified object through a cv-unqualified type Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.3, "Type Qualifiers"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard MSC00-C. Compile cleanly at high warning levels Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MSC01-C. Strive for logical completeness Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP53-CPP. Do not read uninitialized memory Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Initialization of Variables \[LAV\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Referencing uninitialized memory \[uninitref\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-456 2017-07-05: CERT: Exact
CWE 2.11 CWE-457 2017-07-05: CERT: Exact
CWE 2.11 CWE-758 2017-07-05: CERT: Rule subset of CWE
CWE 2.11 CWE-908 2017-07-05: 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 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 + +
\[ Flake 2006 \]
\[ ISO/IEC 9899:2011 \] Subclause 6.7.9, "Initialization" Subclause 6.2.6.1, "General" Subclause 6.3.2.1, "Lvalues, Arrays, and Function Designators"
\[ Mercy 2006 \]
\[ VU\#925211 \]
\[ Wang 2012 \] "More Randomness or Less"
\[ xorl 2009 \] "CVE-2009-1888: SAMBA ACLs Uninitialized Memory Read"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT Oracle Secure Coding Standard for Java EXP01-J. Do not use a null in a case where an object is required Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes \[HFC\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Null Pointer Dereference \[XYH\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Dereferencing an out-of-domain pointer \[nullref\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-476 , NULL Pointer Dereference 2017-07-06: CERT: Exact
+ + +## 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 + +
\[ Goodin 2009 \]
\[ Jack 2007 \]
\[ Liu 2009 \]
\[ van Sprundel 2006 \]
\[ Viega 2005 \] Section 5.2.18, "Null-Pointer Dereference"
+ + +## 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 + +
\[ ISO/IEC 9899:2011 \] 6.2.4, "Storage Durations of Objects"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C VOID EXP56-CPP. Do not cast pointers into more strictly aligned pointer types Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes \[HFC\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Converting pointer values to more strictly aligned pointer types \[alignconv\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.2 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.5 (advisory) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.7 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ Bryant 2003 \]
\[ ISO/IEC 9899:2011 \] 6.3.2.3, "Pointers"
\[ Walfridsson 2003 \] Aliasing, Pointer Casts and GCC 3.3
+ + +## 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 + +
\[ Acton 2006 \] " Understanding Strict Aliasing "
GCC Known Bugs "C Bugs, Aliasing Issues while Casting to Incompatible Types"
\[ ISO/IEC 9899:2011 \] 6.5, "Expressions" 6.7.6.2, "Array Declarators"
\[ Walfridsson 2003 \] Aliasing, Pointer Casts and GCC 3.3
+ + +## 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 + +
Tool Version Checker Description
Astrée 22.04 assignment-to-non-modifiable-lvalue pointer-qualifier-cast-const pointer-qualifier-cast-const-implicit write-to-constant-memory Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-EXP40
Coverity 2017.07 PW MISRA C 2004 Rule 11.5 Implemented
Helix QAC 2022.3 C0563
LDRA tool suite 9.7.1 582 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-EXP40-a A cast shall not remove any 'const' or 'volatile' qualification from the type of a pointer or reference
Polyspace Bug Finder R2022b CERT C: Rule EXP40-C Checks for write operations on const qualified objects (rule fully covered)
PRQA QA-C 9.7 0563 Partially implemented
RuleChecker 22.04 assignment-to-non-modifiable-lvalue pointer-qualifier-cast-const pointer-qualifier-cast-const-implicit Partially checked
TrustInSoft Analyzer 1.38 mem_access 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+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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard EXP05-C. Do not cast away a const qualification Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard STR30-C. Do not attempt to modify string literals Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 6.7.3, "Type Qualifiers"
+ + +## 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 + +
Tool Version Checker Description
Astrée 22.04 memcpy-with-padding Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-EXP42
CodeSonar 7.2p0 BADFUNC.MEMCMP Use of memcmp
Helix QAC 2022.4 DF4726, DF4727, DF4728, DF4729
Klocwork 2022.4 MISRA.STDLIB.MEMCMP.PTR_ARG_TYPES
LDRA tool suite 9.7.1 618 S Partially implemented
Cppcheck 1.66 cert.py Detected by the addon cert.py Does not warn about global/static padding data as this is probably initialized to 0
Parasoft C/C++test 2022.2 CERT_C-EXP42-a Don't memcpy or memcmp non-PODs
PC-lint Plus 1.4 958, 959 Assistance provided: reports structures which require padding between members or after the last member
Polyspace Bug Finder R2023a CERT C: Rule EXP42-C Checks for memory comparison of padding data (rule fully covered)
PRQA QA-C 9.7 1488
RuleChecker 22.04 memcpy-with-padding Partially checked
TrustInSoft Analyzer 1.38 comparable_char_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+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) + +
Taxonomy Taxonomy item Relationship
ISO/IEC TS 17961 Comparison of padding data \[padcomp\] Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.2.1, "Structure and Union Specifiers" 6.7.9, "Initialization"
\[ Summit 1995 \] Question 2.8 Question 2.12
+ + +## 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 + +
Tool Version Checker Description
Astrée 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
CodeSonar 7.2p0 LANG.TYPE.RESTRICT Restrict qualifier used
Coverity 2017.07 MISRA C 2012 Rule 8.14 Partially implemented
GCC 8.1 -Wrestrict Fully implemented
Helix QAC 2022.4 C1057
Klocwork 2022.4 MISRA.TYPE.RESTRICT.QUAL.2012
LDRA tool suite 9.7.1 480 S, 489 S, 613 S Enhanced enforcement
Parasoft C/C++test 2022.2 CERT_C-EXP43-a The restrict type qualifier shall not be used
PC-lint Plus 1.4 586 Assistance provided: reports use of the restrict keyword
Polyspace Bug Finder R2022b CERT C: Rule EXP43-C Checks for copy of overlapping memory (rule partially covered)
PRQA QA-C 9.7 1057
RuleChecker 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
SonarQube C/C++ Plugin 3.11 S1836 Implements MISRA C:2012 Rule 8.14 to flag uses of restrict
+ + +## 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
CERT C Secure Coding Standard FIO30-C. Exclude user input from format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Passing Parameters and Return Values \[CSJ\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Passing pointers into the same object as arguments to different restrict-qualified parameters \[restrict\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.14 (required) 1 Prior to 2018-01-12: CERT: Unspecified Relationship
+1. MISRA Rule 8.14 prohibits the use of the restrict keyword except in C standard library functions. + + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.3.1, "Formal Definition of restrict "
\[ Walls 2006 \]
+ + +## 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 + +
Tool Version Checker Description
Astrée 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
CodeSonar 7.2p0 LANG.TYPE.RESTRICT Restrict qualifier used
Coverity 2017.07 MISRA C 2012 Rule 8.14 Partially implemented
GCC 8.1 -Wrestrict Fully implemented
Helix QAC 2022.4 C1057
Klocwork 2022.4 MISRA.TYPE.RESTRICT.QUAL.2012
LDRA tool suite 9.7.1 480 S, 489 S, 613 S Enhanced enforcement
Parasoft C/C++test 2022.2 CERT_C-EXP43-a The restrict type qualifier shall not be used
PC-lint Plus 1.4 586 Assistance provided: reports use of the restrict keyword
Polyspace Bug Finder R2022b CERT C: Rule EXP43-C Checks for copy of overlapping memory (rule partially covered)
PRQA QA-C 9.7 1057
RuleChecker 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
SonarQube C/C++ Plugin 3.11 S1836 Implements MISRA C:2012 Rule 8.14 to flag uses of restrict
+ + +## 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
CERT C Secure Coding Standard FIO30-C. Exclude user input from format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Passing Parameters and Return Values \[CSJ\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Passing pointers into the same object as arguments to different restrict-qualified parameters \[restrict\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.14 (required) 1 Prior to 2018-01-12: CERT: Unspecified Relationship
+1. MISRA Rule 8.14 prohibits the use of the restrict keyword except in C standard library functions. + + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.3.1, "Formal Definition of restrict "
\[ Walls 2006 \]
+ + +## 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. -
Tool Version Checker Description
Astrée 22.04 Supported, but no explicit checker
CodeSonar 7.0p0 ALLOC.LEAK Leak
Compass/ROSE
Coverity 2017.07 RESOURCE_LEAK (partial) Partially implemented
Helix QAC 2022.2 C2701, C2702, C2703 C++2701, C++2702, C++2703
Klocwork 2022.2 RH.LEAK
LDRA tool suite 9.7.1 49 D Partially implemented
Parasoft C/C++test 2022.1 CERT_C-FIO42-a Ensure resources are freed
PC-lint Plus 1.4 429 Partially supported
Polyspace Bug Finder R2022a CERT C: Rule FIO42-C Checks for resource leak (rule partially covered)
PRQA QA-C 9.7 2701, 2702, 2703
PRQA QA-C++ 4.4 2701, 2702, 2703
SonarQube C/C++ Plugin 3.11 S2095
+
Tool Version Checker Description
Astrée 22.04 Supported, but no explicit checker
CodeSonar 7.2p0 ALLOC.LEAK Leak
Compass/ROSE
Coverity 2017.07 RESOURCE_LEAK (partial) Partially implemented
Helix QAC 2022.4 DF2701, DF2702, DF2703
Klocwork 2022.4 RH.LEAK
LDRA tool suite 9.7.1 49 D Partially implemented
Parasoft C/C++test 2022.2 CERT_C-FIO42-a Ensure resources are freed
PC-lint Plus 1.4 429 Partially supported
Polyspace Bug Finder R2023a CERT C: Rule FIO42-C Checks for resource leak (rule partially covered)
PRQA QA-C 9.7 2701, 2702, 2703
PRQA QA-C++ 4.4 2701, 2702, 2703
SonarQube C/C++ Plugin 3.11 S2095
## 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 + +
Tool Version Checker Description
Astrée 22.04 for-loop-float Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-FLP30 Fully implemented
Clang 3.9 cert-flp30-c Checked by clang-tidy
CodeSonar 7.2p0 LANG.STRUCT.LOOP.FPC Float-typed loop counter
Compass/ROSE
Coverity 2017.07 MISRA C 2004 Rule 13.4 MISRA C 2012 Rule 14.1 Implemented
ECLAIR 1.2 CC2.FLP30 Fully implemented
Helix QAC 2022.4 C3339, C3340, C3342 C++4234
Klocwork 2022.4 MISRA.FOR.COUNTER.FLT
LDRA tool suite 9.7.1 39 S Fully implemented
Parasoft C/C++test 2022.2 CERT_C-FLP30-a Do not use floating point variables as loop counters
PC-lint Plus 1.4 9009 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule FLP30-C Checks for use of float variable as loop counter (rule fully covered)
PRQA QA-C 9.7 3339, 3340, 3342 Partially implemented
PRQA QA-C++ 4.4 4234
PVS-Studio 7.23 V1034
RuleChecker 22.04 for-loop-float Fully checked
SonarQube C/C++ Plugin 3.11 S2193 Fully implemented
TrustInSoft Analyzer 1.38 non-terminating Exhaustively detects non-terminating statements (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+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) + +
Taxonomy Taxonomy item Relationship
CERT C FLP30-CPP. Do not use floating-point variables as loop counters Prior to 2018-01-12: CERT: Unspecified Relationship
CERT Oracle Secure Coding Standard for Java NUM09-J. Do not use floating-point variables as loop counters Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Floating-Point Arithmetic \[PLF\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Directive 1.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 14.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ Aho 1986 \]
\[ Goldberg 1991 \]
\[ Lockheed Martin 05 \] AV Rule 197
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard FLP03-C. Detect and handle floating-point errors Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-682 , Incorrect Calculation 2017-07-07: 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-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 + +
\[ ISO/IEC 9899:2011 \] 7.3.2, "Conventions" 7.12.1, "Treatment of Error Conditions" F.10.7, "Remainder Functions"
\[ IEEE 754 2006 \]
\[ Plum 1985 \] Rule 2-2
\[ Plum 1989 \] Topic 2.10, "conv—Conversions and Overflow"
\[ UNIX 1992 \] System V Interface Definition (SVID3)
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT Oracle Secure Coding Standard for Java NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Numeric Conversion Errors \[FLC\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-681 , Incorrect Conversion between Numeric Types 2017-06-29: CERT: Rule subset of CWE
CWE 2.11 CWE-197 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-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 + +
\[ IEEE 754 2006 \]
\[ ISO/IEC 9899:2011 \] Subclause 6.3.1.4, "Real Floating and Integer" Subclause 6.3.1.5, "Real Floating Types"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard DCL03-C. Use a static assertion to test the value of a constant expression Prior to 2018-01-12: CERT: Unspecified Relationship
CERT Oracle Secure Coding Standard for Java NUM13-J. Avoid loss of precision when converting primitive integers to floating-point Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 6.3.1.4, "Real Floating and Integer"
+ + +## 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 + +
Tool Version Checker Description
Astrée 22.04 memcmp-with-float Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-FLP37 Fully implemented
Helix QAC 2022.4 C5026 C++3118
Klocwork 2022.4 MISRA.STDLIB.MEMCMP.PTR_ARG_TYPES CERT.MEMCMP.FLOAT_MEMBER
LDRA tool suite 9.7.1 618 S Enhanced Enforcement
Parasoft C/C++test 2022.2 CERT_C-FLP37-c Do not use object representations to compare floating-point values
PC-lint Plus 1.4 2498, 2499 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule FLP37-C Checks for memory comparison of floating-point values (rule fully covered)
PRQA QA-C 9.7 5026
PVS-Studio 7.23 V1014
RuleChecker 22.04 memcmp-with-float Partially checked
TrustInSoft Analyzer 1.38 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+FLP37-C). + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Annex F, " IEC 60559 floating-point arithmetic"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C INT02-C. Understand integer conversion rules Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ARR30-C. Do not form or use out-of-bounds pointers or array subscripts Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ARR36-C. Do not subtract or compare two pointers that do not refer to the same array Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ARR37-C. Do not add or subtract an integer to a pointer to a non-array object Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C CON08-C. Do not assume that a group of calls to independently atomic methods is atomic Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Arithmetic Wrap-Around Error \[FIF\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-190 , Integer Overflow or Wraparound 2016-12-02: CERT: Rule subset of CWE
CWE 2.11 CWE-131 2017-05-16: CERT: Partial overlap
CWE 2.11 CWE-191 2017-05-18: CERT: Partial overlap
CWE 2.11 CWE-680 2017-05-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-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 + +
\[ Bailey 2014 \] Raising Lazarus - The 20 Year Old Bug that Went to Mars
\[ Dowd 2006 \] Chapter 6, "C Language Issues" ("Arithmetic Boundary Conditions," pp. 211–223)
\[ ISO/IEC 9899:2011 \] Subclause 6.2.5, "Types"
\[ Seacord 2013b \] Chapter 5, "Integer Security"
\[ Viega 2005 \] Section 5.2.7, "Integer Overflow"
\[ VU\#551436 \]
\[ Warren 2002 \] Chapter 2, "Basics"
\[ Wojtczuk 2008 \]
\[ xorl 2009 \] "CVE-2009-1385: Linux Kernel E1000 Integer Underflow"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C DCL03-C. Use a static assertion to test the value of a constant expression Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C FIO34-C. Distinguish between characters read from a file and EOF or WEOF Prior to 2018-01-12: CERT: Unspecified Relationship
CERT Oracle Secure Coding Standard for Java NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Numeric Conversion Errors \[FLC\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 10.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 10.3 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 10.4 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 10.6 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 10.7 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-192 , Integer Coercion Error 2017-07-17: CERT: Exact
CWE 2.11 CWE-197 , Numeric Truncation Error 2017-06-14: CERT: Rule subset of CWE
CWE 2.11 CWE-681 , Incorrect Conversion between Numeric Types 2017-07-17: CERT: Rule subset of CWE
CWE 2.11 CWE-704 2017-07-17: 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-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) + +
Taxonomy Taxonomy item Relationship
CERT C INT02-C. Understand integer conversion rules Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C INT35-C. Use correct integer precisions Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C INT33-C. Ensure that division and remainder operations do not result in divide-by-zero errors Prior to 2018-01-12: CERT: Unspecified Relationship
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 Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ARR30-C. Do not form or use out-of-bounds pointers or array subscripts Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ARR36-C. Do not subtract or compare two pointers that do not refer to the same array Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C ARR37-C. Do not add or subtract an integer to a pointer to a non-array object Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C MSC15-C. Do not depend on undefined behavior Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C CON08-C. Do not assume that a group of calls to independently atomic methods is atomic Prior to 2018-01-12: CERT: Unspecified Relationship
CERT Oracle Secure Coding Standard for Java INT00-J. Perform explicit range checking to avoid integer overflow Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Arithmetic Wrap-Around Error \[FIF\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Overflowing signed integers \[intoflow\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-190 , Integer Overflow or Wraparound 2017-05-18: CERT: Partial overlap
CWE 2.11 CWE-191 2017-05-18: CERT: Partial overlap
CWE 2.11 CWE-680 2017-05-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-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) + +
Taxonomy Taxonomy item Relationship
CERT C INT32-C. Ensure that operations on signed integers do not result in overflow Prior to 2018-01-12: CERT: Unspecified Relationship
CERT Oracle Secure Coding Standard for Java NUM02-J. Ensure that division and remainder operations do not result in divide-by-zero errors Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Integer division errors \[diverr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-369 , Divide By Zero 2017-07-07: CERT: Exact
+ + +## 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 + +
\[ Seacord 2013b \] Chapter 5, "Integer Security"
\[ Warren 2002 \] Chapter 2, "Basics"
+ + +## 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. + +![](ShiftLeft.JPG) + +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 + +![](ShiftRight.JPG) + +or a logical (unsigned) shift + +![](LogicalShiftRight.JPG) + +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) + +
Taxonomy Taxonomy item Relationship
CERT C INT13-C. Use bitwise operators only on unsigned operands Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C INT35-C. Use correct integer precisions Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C INT32-C. Ensure that operations on signed integers do not result in overflow Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Arithmetic Wrap-Around Error \[FIF\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-682 2017-07-07: CERT: Rule subset of CWE
CWE 2.11 CWE-758 2017-07-07: 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-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 + +
\[ C99 Rationale 2003 \] 6.5.7, "Bitwise Shift Operators"
\[ Dowd 2006 \] Chapter 6, "C Language Issues"
\[ Seacord 2013b \] Chapter 5, "Integer Security"
\[ Viega 2005 \] Section 5.2.7, "Integer Overflow"
+ + +## 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 + +
\[ Dowd 2006 \] Chapter 6, "C Language Issues"
\[ C99 Rationale 2003 \] 6.5.7, "Bitwise Shift Operators"
+ + +## 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) + +
Taxonomy Taxonomy item Relationship
CERT C INT11-CPP. Take care when converting from pointer to integer or integer to pointer Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes \[HFC\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Converting a pointer to integer or integer to pointer \[intptrconv\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-587 , Assignment of a Fixed Address to a Pointer 2017-07-07: CERT: Partial overlap
CWE 2.11 CWE-704 2017-06-14: CERT: Rule subset of CWE
CWE 2.11 CWE-758 2017-07-07: CERT: Rule subset of CWE
CWE 3.1 CWE-119 , Improper Restriction of Operations within the Bounds of a Memory Buffer 2018-10-19:CERT:None
CWE 3.1 CWE-466 , Return of Pointer Value Outside of Expected Range 2018-10-19:CERT:None
+ + +## 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 + +
\[ ISO/IEC 9899:2011 \] 6.3.2.3, "Pointers"
+ + +## 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).) + +
Rule Severity Likelihood Remediation Cost Priority Level
MEM30-C High Likely Medium P18 L1
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 dangling_pointer_use Supported Astrée reports all accesses to freed allocated memory.
Axivion Bauhaus Suite 7.2.0 CertC-MEM30 Detects memory accesses after its deallocation and double memory deallocations
CodeSonar 7.2p0 ALLOC.UAF Use after free
Compass/ROSE
Coverity 2017.07 USE_AFTER_FREE Can detect the specific instances where memory is deallocated more than once or read/written to the target of a freed pointer
Helix QAC 2022.4 DF4866, DF4867, DF4868, DF4871, DF4872, DF4873 C++3339, C++4303, C++4304
Klocwork 2022.4 UFM.DEREF.MIGHT UFM.DEREF.MUST UFM.FFM.MIGHT UFM.FFM.MUST UFM.RETURN.MIGHT UFM.RETURN.MUST UFM.USE.MIGHT UFM.USE.MUST
LDRA tool suite 9.7.1 51 D, 484 S, 112 D Partially implemented
Parasoft C/C++test 2022.2 CERT_C-MEM30-a Do not use resources that have been freed
Parasoft Insure++ Runtime analysis
PC-lint Plus 1.4 449, 2434 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule MEM30-C Checks for: Accessing previously freed pointerccessing previously freed pointer, freeing previously freed pointerreeing previously freed pointer. Rule partially covered.
PRQA QA-C 9.7 2731, 2732, 2733
PRQA QA-C++ 4.4 3339, 4303, 4304
PVS-Studio 7.22 V586 , V774
Splint 3.1.1
TrustInSoft Analyzer 1.38 dangling_pointer Exhaustively verified (see one compliant and one non-compliant example ).
+ + +## Related Vulnerabilities + +[VU\#623332](http://www.kb.cert.org/vuls/id/623332) describes a double-free vulnerability in the MIT Kerberos 5 function [krb5_recvauth()](http://web.mit.edu/kerberos/www/advisories/MITKRB5-SA-2005-003-recvauth.txt). + +Search for [vulnerabilities](https://www.securecoding.cert.org/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) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard MEM01-C. Store a new value in pointers immediately after free() Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C MEM50-CPP. Do not access freed memory Prior to 2018-01-12: CERT: Unspecified 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 Dangling Reference to Heap \[XYK\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Accessing freed memory \[accfree\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Freeing memory multiple times \[dblfree\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 18.6 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-416 , Use After Free 2017-07-07: CERT: Exact
CWE 2.11 CWE-672 2017-07-07: 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-672 and MEM30-C** + +Intersection( MEM30-C, FIO46-C) = Ø CWE-672 = Union( MEM30-C, list) where list = + +* Use of a resource, other than memory after it has been released (eg: reusing a closed file, or expired mutex) +**CWE-666 and MEM30-C** + +Intersection( MEM30-C, FIO46-C) = Ø + +CWE-672 = Subset( CWE-666) + +**CWE-758 and MEM30-C** + +CWE-758 = Union( MEM30-C, list) where list = + +* Undefined behavior that is not covered by use-after-free errors +**CWE-415 and MEM30-C** + +MEM30-C = Union( CWE-456, list) where list = + +* Dereference of a pointer after freeing it (besides passing it to free() a second time) + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 7.22.3, "Memory Management Functions"
\[ Kernighan 1988 \] Section 7.8.5, "Storage Management"
\[ OWASP Freed Memory \]
\[ MIT 2005 \]
\[ Seacord 2013b \] Chapter 4, "Dynamic Memory Management"
\[ Viega 2005 \] Section 5.2.19, "Using Freed Memory"
\[ VU\#623332 \]
\[ xorl 2009 \] CVE-2009-1364: LibWMF Pointer Use after free()
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MEM30-C: Do not access freed memory](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM30-C/DoNotAccessFreedMemory.ql b/c/cert/src/rules/MEM30-C/DoNotAccessFreedMemory.ql new file mode 100644 index 0000000000..59ab0df670 --- /dev/null +++ b/c/cert/src/rules/MEM30-C/DoNotAccessFreedMemory.ql @@ -0,0 +1,71 @@ +/** + * @id c/cert/do-not-access-freed-memory + * @name MEM30-C: Do not access freed memory + * @description Accessing memory that has been deallocated is undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/mem30-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.cpp.Allocations +import semmle.code.cpp.controlflow.StackVariableReachability + +/** `e` is an expression that frees the memory pointed to by `v`. */ +predicate isFreeExpr(Expr e, StackVariable v) { + exists(VariableAccess va | va.getTarget() = v and freeExprOrIndirect(e, va, _)) +} + +/** `e` is an expression that accesses `v` but is not the lvalue of an assignment. */ +predicate isAccessExpr(Expr e, StackVariable v) { + v.getAnAccess() = e and + not exists(Assignment a | a.getLValue() = e) + or + isDerefByCallExpr(_, _, e, v) +} + +/** + * `va` is passed by value as (part of) the `i`th argument in + * call `c`. The target function is either a library function + * or a source code function that dereferences the relevant + * parameter. + */ +predicate isDerefByCallExpr(Call c, int i, VariableAccess va, StackVariable v) { + v.getAnAccess() = va and + va = c.getAnArgumentSubExpr(i) and + not c.passesByReference(i, va) and + (c.getTarget().hasEntryPoint() implies isAccessExpr(_, c.getTarget().getParameter(i))) +} + +class UseAfterFreeReachability extends StackVariableReachability { + UseAfterFreeReachability() { this = "UseAfterFree" } + + override predicate isSource(ControlFlowNode node, StackVariable v) { isFreeExpr(node, v) } + + override predicate isSink(ControlFlowNode node, StackVariable v) { isAccessExpr(node, v) } + + override predicate isBarrier(ControlFlowNode node, StackVariable v) { + definitionBarrier(v, node) or + isFreeExpr(node, v) + } +} + +// This query is a modified version of the `UseAfterFree.ql` +// (cpp/use-after-free) query from the CodeQL standard library. +from UseAfterFreeReachability r, StackVariable v, Expr free, Expr e +where + not isExcluded(e, InvalidMemory1Package::doNotAccessFreedMemoryQuery()) and + r.reaches(free, v, e) +select e, + "Pointer '" + v.getName().toString() + "' accessed but may have been previously freed $@.", free, + "here" diff --git a/c/cert/src/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.md b/c/cert/src/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.md new file mode 100644 index 0000000000..d3c849331a --- /dev/null +++ b/c/cert/src/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.md @@ -0,0 +1,121 @@ +# MEM31-C: Free dynamically allocated memory when no longer needed + +This query implements the CERT-C rule MEM31-C: + +> Free dynamically allocated memory when no longer needed + + +## Description + +Before the lifetime of the last pointer that stores the return value of a call to a standard memory allocation function has ended, it must be matched by a call to `free()` with that pointer value. + +## Noncompliant Code Example + +In this noncompliant example, the object allocated by the call to `malloc()` is not freed before the end of the lifetime of the last pointer `text_buffer` referring to the object: + +```cpp +#include + +enum { BUFFER_SIZE = 32 }; + +int f(void) { + char *text_buffer = (char *)malloc(BUFFER_SIZE); + if (text_buffer == NULL) { + return -1; + } + return 0; +} +``` + +## Compliant Solution + +In this compliant solution, the pointer is deallocated with a call to `free()`: + +```cpp +#include + +enum { BUFFER_SIZE = 32 }; + +int f(void) { + char *text_buffer = (char *)malloc(BUFFER_SIZE); + if (text_buffer == NULL) { + return -1; + } + + free(text_buffer); + return 0; +} + +``` + +## Exceptions + +**MEM31-C-EX1**: Allocated memory does not need to be freed if it is assigned to a pointer whose lifetime includes program termination. The following code example illustrates a pointer that stores the return value from `malloc()` in a `static` variable: + +```cpp +#include + +enum { BUFFER_SIZE = 32 }; + +int f(void) { + static char *text_buffer = NULL; + if (text_buffer == NULL) { + text_buffer = (char *)malloc(BUFFER_SIZE); + if (text_buffer == NULL) { + return -1; + } + } + return 0; +} + +``` + +## Risk Assessment + +Failing to free memory can result in the exhaustion of system memory resources, which can lead to a [denial-of-service attack](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-denial-of-service). + +
Rule Severity Likelihood Remediation Cost Priority Level
MEM31-C Medium Probable Medium P8 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 Supported, but no explicit checker
Axivion Bauhaus Suite 7.2.0 CertC-MEM31 Can detect dynamically allocated resources that are not freed
CodeSonar 7.2p0 ALLOC.LEAK Leak
Compass/ROSE
Coverity 2017.07 RESOURCE_LEAK ALLOC_FREE_MISMATCH Finds resource leaks from variables that go out of scope while owning a resource
Cppcheck 1.66 leakReturnValNotUsed Doesn't use return value of memory allocation function
Helix QAC 2022.4 DF2706, DF2707, DF2708 C++3337, C++3338
Klocwork 2022.4 CL.FFM.ASSIGN CL.FFM.COPY CL.SHALLOW.ASSIGN CL.SHALLOW.COPY FMM.MIGHT FMM.MUST
LDRA tool suite 9.7.1 50 D Partially implemented
Parasoft C/C++test 2022.2 CERT_C-MEM31-a Ensure resources are freed
Parasoft Insure++ Runtime analysis
PC-lint Plus 1.4 429 Fully supported
Polyspace Bug Finder R2023a CERT C: Rule MEM31-C Checks for memory leak (rule fully covered)
PRQA QA-C 9.7 2706, 2707, 2708
PRQA QA-C++ 4.4 2706, 2707, 2708, 3337, 3338
PVS-Studio 7.23 V773
SonarQube C/C++ Plugin 3.11 S3584
Splint 3.1.1
TrustInSoft Analyzer 1.38 malloc 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+MEM31-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 Memory Leak \[XYL\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Failing to close files or free dynamic memory when they are no longer needed \[fileclose\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-401 , Improper Release of Memory Before Removing Last Reference ("Memory Leak") 2017-07-05: CERT: Exact
CWE 2.11 CWE-404 2017-07-06: CERT: Rule subset of CWE
CWE 2.11 CWE-459 2017-07-06: CERT: Rule subset of CWE
CWE 2.11 CWE-771 2017-07-06: CERT: Rule subset of CWE
CWE 2.11 CWE-772 2017-07-06: 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-404/CWE-459/CWE-771/CWE-772 and FIO42-C/MEM31-C** + +Intersection( FIO42-C, MEM31-C) = Ø + +CWE-404 = CWE-459 = CWE-771 = CWE-772 + +CWE-404 = Union( FIO42-C, MEM31-C list) where list = + +* Failure to free resources besides files or memory chunks, such as mutexes) + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.22.3, "Memory Management Functions"
+ + +## Implementation notes + +The rule is enforced in the context of a single function. + +## References + +* CERT-C: [MEM31-C: Free dynamically allocated memory when no longer needed](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.ql b/c/cert/src/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.ql new file mode 100644 index 0000000000..18e9478aee --- /dev/null +++ b/c/cert/src/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.ql @@ -0,0 +1,28 @@ +/** + * @id c/cert/free-memory-when-no-longer-needed-cert + * @name MEM31-C: Free dynamically allocated memory when no longer needed + * @description Failing to free memory that is no longer needed can lead to a memory leak and + * resource exhaustion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/mem31-c + * correctness + * security + * 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.freememorywhennolongerneededshared.FreeMemoryWhenNoLongerNeededShared + +class FreeMemoryWhenNoLongerNeededCertQuery extends FreeMemoryWhenNoLongerNeededSharedSharedQuery { + FreeMemoryWhenNoLongerNeededCertQuery() { + this = Memory2Package::freeMemoryWhenNoLongerNeededCertQuery() + } +} diff --git a/c/cert/src/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.md b/c/cert/src/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.md new file mode 100644 index 0000000000..6a726c1701 --- /dev/null +++ b/c/cert/src/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.md @@ -0,0 +1,249 @@ +# MEM33-C: Allocate structures containing a flexible array member dynamically + +This query implements the CERT-C rule MEM33-C: + +> Allocate and copy structures containing a flexible array member dynamically + + +## Description + +The C Standard, 6.7.2.1, paragraph 18 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], says + +> As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a *flexible array member*. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. + + +The following is an example of a structure that contains a flexible array member: + +```cpp +struct flex_array_struct { + int num; + int data[]; +}; + +``` +This definition means that when computing the size of such a structure, only the first member, `num`, is considered. Unless the appropriate size of the flexible array member has been explicitly added when allocating storage for an object of the `struct`, the result of accessing the member `data` of a variable of nonpointer type `struct flex_array_struct` is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). [DCL38-C. Use the correct syntax when declaring a flexible array member](https://wiki.sei.cmu.edu/confluence/display/c/DCL38-C.+Use+the+correct+syntax+when+declaring+a+flexible+array+member) describes the correct way to declare a `struct` with a flexible array member. + +To avoid the potential for undefined behavior, structures that contain a flexible array member should always be allocated dynamically. Flexible array structures must + +* Have dynamic storage duration (be allocated via `malloc()` or another dynamic allocation function) +* Be dynamically copied using `memcpy()` or a similar function and not by assignment +* When used as an argument to a function, be passed by pointer and not copied by value + +## Noncompliant Code Example (Storage Duration) + +This noncompliant code example uses automatic storage for a structure containing a flexible array member: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(void) { + struct flex_array_struct flex_struct; + size_t array_size = 4; + + /* Initialize structure */ + flex_struct.num = array_size; + + for (size_t i = 0; i < array_size; ++i) { + flex_struct.data[i] = 0; + } +} +``` +Because the memory for `flex_struct` is reserved on the stack, no space is reserved for the `data` member. Accessing the `data` member is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +## Compliant Solution (Storage Duration) + +This compliant solution dynamically allocates storage for `flex_array_struct`: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(void) { + struct flex_array_struct *flex_struct; + size_t array_size = 4; + + /* Dynamically allocate memory for the struct */ + flex_struct = (struct flex_array_struct *)malloc( + sizeof(struct flex_array_struct) + + sizeof(int) * array_size); + if (flex_struct == NULL) { + /* Handle error */ + } + + /* Initialize structure */ + flex_struct->num = array_size; + + for (size_t i = 0; i < array_size; ++i) { + flex_struct->data[i] = 0; + } +} +``` + +## Noncompliant Code Example (Copying) + +This noncompliant code example attempts to copy an instance of a structure containing a flexible array member (`struct `flex_array_struct``) by assignment: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(struct flex_array_struct *struct_a, + struct flex_array_struct *struct_b) { + *struct_b = *struct_a; +} +``` +When the structure is copied, the size of the flexible array member is not considered, and only the first member of the structure, `num`, is copied, leaving the array contents untouched. + +## Compliant Solution (Copying) + +This compliant solution uses `memcpy()` to properly copy the content of `struct_a` into `struct_b`: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(struct flex_array_struct *struct_a, + struct flex_array_struct *struct_b) { + if (struct_a->num > struct_b->num) { + /* Insufficient space; handle error */ + return; + } + memcpy(struct_b, struct_a, + sizeof(struct flex_array_struct) + (sizeof(int) + * struct_a->num)); +} +``` + +## Noncompliant Code Example (Function Arguments) + +In this noncompliant code example, the flexible array structure is passed by value to a function that prints the array elements: + +```cpp +#include +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void print_array(struct flex_array_struct struct_p) { + puts("Array is: "); + for (size_t i = 0; i < struct_p.num; ++i) { + printf("%d ", struct_p.data[i]); + } + putchar('\n'); +} + +void func(void) { + struct flex_array_struct *struct_p; + size_t array_size = 4; + + /* Space is allocated for the struct */ + struct_p = (struct flex_array_struct *)malloc( + sizeof(struct flex_array_struct) + + sizeof(int) * array_size); + if (struct_p == NULL) { + /* Handle error */ + } + struct_p->num = array_size; + + for (size_t i = 0; i < array_size; ++i) { + struct_p->data[i] = i; + } + print_array(*struct_p); +} +``` +Because the argument is passed by value, the size of the flexible array member is not considered when the structure is copied, and only the first member of the structure, `num`, is copied. + +## Compliant Solution (Function Arguments) + +In this compliant solution, the structure is passed by reference and not by value: + +```cpp +#include +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void print_array(struct flex_array_struct *struct_p) { + puts("Array is: "); + for (size_t i = 0; i < struct_p->num; ++i) { + printf("%d ", struct_p->data[i]); + } + putchar('\n'); +} + +void func(void) { + struct flex_array_struct *struct_p; + size_t array_size = 4; + + /* Space is allocated for the struct and initialized... */ + + print_array(struct_p); +} +``` + +## Risk Assessment + +Failure to use structures with flexible array members correctly can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
MEM33-C Low Unlikely Low P3 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 flexible-array-member-assignment flexible-array-member-declaration Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-MEM33 Fully implemented
CodeSonar 7.2p0 LANG.STRUCT.DECL.FAM Declaration of Flexible Array Member
Compass/ROSE Can detect all of these
Helix QAC 2022.4 C1061, C1062, C1063, C1064
Klocwork 2022.4 MISRA.INCOMPLETE.STRUCT MISRA.MEMB.FLEX_ARRAY.2012
LDRA tool suite 9.7.1 649 S, 650 S Fully implemented
Parasoft C/C++test 2022.2 CERT_C-MEM33-a CERT_C-MEM33-b Allocate structures containing a flexible array member dynamically Do not copy instances of structures containing a flexible array member
Polyspace Bug Finder R2023a CERT C: Rule MEM33-C Checks for misuse of structure with flexible array member (rule fully covered)
PRQA QA-C 9.7 1061, 1062, 1063, 1064
RuleChecker 22.04 flexible-array-member-assignment flexible-array-member-declaration 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+MEM33-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
CERT C Secure Coding Standard DCL38-C. Use the correct syntax when declaring a flexible array member 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-401 and MEM33-CPP** + +There is no longer a C++ rule for MEM33-CPP. (In fact, all C++ rules from 30-50 are gone, because we changed the numbering system to be 50-99 for C++ rules.) + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 6.7.2.1, "Structure and Union Specifiers"
\[ JTC1/SC22/WG14 N791 \] Solving the Struct Hack Problem
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MEM33-C: Allocate and copy structures containing a flexible array member dynamically](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.ql b/c/cert/src/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.ql new file mode 100644 index 0000000000..2ed5035ff0 --- /dev/null +++ b/c/cert/src/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.ql @@ -0,0 +1,94 @@ +/** + * @id c/cert/alloc-structs-with-a-flexible-array-member-dynamically + * @name MEM33-C: Allocate structures containing a flexible array member dynamically + * @description A structure containing a flexible array member must be allocated dynamically in + * order for subsequent accesses to the flexible array to point to valid memory. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/mem33-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.Variable +import codingstandards.c.Objects +import semmle.code.cpp.models.interfaces.Allocation +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +abstract class FlexibleArrayAlloc extends Element { + /** + * Returns the `Variable` being allocated. + */ + abstract Element getReportElement(); +} + +/** + * A `FunctionCall` to an `AllocationFunction` that allocates memory + * which is assigned to a `Variable` of type `FlexibleArrayStructType`. + */ +class FlexibleArrayStructDynamicAlloc extends FlexibleArrayAlloc, FunctionCall { + Variable v; + + FlexibleArrayStructDynamicAlloc() { + this.getTarget() instanceof AllocationFunction and + v.getAnAssignedValue() = this and + v.getUnderlyingType().(PointerType).getBaseType().getUnspecifiedType() instanceof + FlexibleArrayStructType + } + + /** + * Holds if the size argument of the allocation function is insufficient to + * allocate at least one byte for the flexible array member. + */ + predicate hasInsufficientAllocationSize() { + upperBound(this.getArgument(this.getTarget().(AllocationFunction).getSizeArg())) <= + max(v.getUnderlyingType() + .(PointerType) + .getBaseType() + .getUnspecifiedType() + .(FlexibleArrayStructType) + .getSize() + ) + } + + override Element getReportElement() { result = v } +} + +/** + * A `Variable` of type `FlexibleArrayStructType` that is not allocated dynamically. + */ +class FlexibleArrayNonDynamicAlloc extends FlexibleArrayAlloc { + ObjectIdentity object; + + FlexibleArrayNonDynamicAlloc() { + this = object and + not object.getStorageDuration().isAllocated() and + // Exclude temporaries. Though they should violate this rule, in practice these results are + // often spurious and redundant, such as (*x = *x) which creates an unused temporary object. + not object.hasTemporaryLifetime() and + object.getType().getUnspecifiedType() instanceof FlexibleArrayStructType and + not exists(Variable v | v.getInitializer().getExpr() = this) + } + + override Element getReportElement() { result = object } +} + +from FlexibleArrayAlloc alloc, string message +where + not isExcluded(alloc, Memory2Package::allocStructsWithAFlexibleArrayMemberDynamicallyQuery()) and + ( + alloc.(FlexibleArrayStructDynamicAlloc).hasInsufficientAllocationSize() and + message = "$@ allocated with insufficient memory for its flexible array member." + or + alloc instanceof FlexibleArrayNonDynamicAlloc and + message = "$@ contains a flexible array member but is not dynamically allocated." + ) +select alloc, message, alloc.getReportElement(), alloc.getReportElement().toString() diff --git a/c/cert/src/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.md b/c/cert/src/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.md new file mode 100644 index 0000000000..3fe0840a96 --- /dev/null +++ b/c/cert/src/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.md @@ -0,0 +1,249 @@ +# MEM33-C: Copy structures containing a flexible array member using memcpy or a similar function + +This query implements the CERT-C rule MEM33-C: + +> Allocate and copy structures containing a flexible array member dynamically + + +## Description + +The C Standard, 6.7.2.1, paragraph 18 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], says + +> As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a *flexible array member*. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. + + +The following is an example of a structure that contains a flexible array member: + +```cpp +struct flex_array_struct { + int num; + int data[]; +}; + +``` +This definition means that when computing the size of such a structure, only the first member, `num`, is considered. Unless the appropriate size of the flexible array member has been explicitly added when allocating storage for an object of the `struct`, the result of accessing the member `data` of a variable of nonpointer type `struct flex_array_struct` is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). [DCL38-C. Use the correct syntax when declaring a flexible array member](https://wiki.sei.cmu.edu/confluence/display/c/DCL38-C.+Use+the+correct+syntax+when+declaring+a+flexible+array+member) describes the correct way to declare a `struct` with a flexible array member. + +To avoid the potential for undefined behavior, structures that contain a flexible array member should always be allocated dynamically. Flexible array structures must + +* Have dynamic storage duration (be allocated via `malloc()` or another dynamic allocation function) +* Be dynamically copied using `memcpy()` or a similar function and not by assignment +* When used as an argument to a function, be passed by pointer and not copied by value + +## Noncompliant Code Example (Storage Duration) + +This noncompliant code example uses automatic storage for a structure containing a flexible array member: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(void) { + struct flex_array_struct flex_struct; + size_t array_size = 4; + + /* Initialize structure */ + flex_struct.num = array_size; + + for (size_t i = 0; i < array_size; ++i) { + flex_struct.data[i] = 0; + } +} +``` +Because the memory for `flex_struct` is reserved on the stack, no space is reserved for the `data` member. Accessing the `data` member is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +## Compliant Solution (Storage Duration) + +This compliant solution dynamically allocates storage for `flex_array_struct`: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(void) { + struct flex_array_struct *flex_struct; + size_t array_size = 4; + + /* Dynamically allocate memory for the struct */ + flex_struct = (struct flex_array_struct *)malloc( + sizeof(struct flex_array_struct) + + sizeof(int) * array_size); + if (flex_struct == NULL) { + /* Handle error */ + } + + /* Initialize structure */ + flex_struct->num = array_size; + + for (size_t i = 0; i < array_size; ++i) { + flex_struct->data[i] = 0; + } +} +``` + +## Noncompliant Code Example (Copying) + +This noncompliant code example attempts to copy an instance of a structure containing a flexible array member (`struct `flex_array_struct``) by assignment: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(struct flex_array_struct *struct_a, + struct flex_array_struct *struct_b) { + *struct_b = *struct_a; +} +``` +When the structure is copied, the size of the flexible array member is not considered, and only the first member of the structure, `num`, is copied, leaving the array contents untouched. + +## Compliant Solution (Copying) + +This compliant solution uses `memcpy()` to properly copy the content of `struct_a` into `struct_b`: + +```cpp +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void func(struct flex_array_struct *struct_a, + struct flex_array_struct *struct_b) { + if (struct_a->num > struct_b->num) { + /* Insufficient space; handle error */ + return; + } + memcpy(struct_b, struct_a, + sizeof(struct flex_array_struct) + (sizeof(int) + * struct_a->num)); +} +``` + +## Noncompliant Code Example (Function Arguments) + +In this noncompliant code example, the flexible array structure is passed by value to a function that prints the array elements: + +```cpp +#include +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void print_array(struct flex_array_struct struct_p) { + puts("Array is: "); + for (size_t i = 0; i < struct_p.num; ++i) { + printf("%d ", struct_p.data[i]); + } + putchar('\n'); +} + +void func(void) { + struct flex_array_struct *struct_p; + size_t array_size = 4; + + /* Space is allocated for the struct */ + struct_p = (struct flex_array_struct *)malloc( + sizeof(struct flex_array_struct) + + sizeof(int) * array_size); + if (struct_p == NULL) { + /* Handle error */ + } + struct_p->num = array_size; + + for (size_t i = 0; i < array_size; ++i) { + struct_p->data[i] = i; + } + print_array(*struct_p); +} +``` +Because the argument is passed by value, the size of the flexible array member is not considered when the structure is copied, and only the first member of the structure, `num`, is copied. + +## Compliant Solution (Function Arguments) + +In this compliant solution, the structure is passed by reference and not by value: + +```cpp +#include +#include + +struct flex_array_struct { + size_t num; + int data[]; +}; + +void print_array(struct flex_array_struct *struct_p) { + puts("Array is: "); + for (size_t i = 0; i < struct_p->num; ++i) { + printf("%d ", struct_p->data[i]); + } + putchar('\n'); +} + +void func(void) { + struct flex_array_struct *struct_p; + size_t array_size = 4; + + /* Space is allocated for the struct and initialized... */ + + print_array(struct_p); +} +``` + +## Risk Assessment + +Failure to use structures with flexible array members correctly can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
MEM33-C Low Unlikely Low P3 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 flexible-array-member-assignment flexible-array-member-declaration Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-MEM33 Fully implemented
CodeSonar 7.2p0 LANG.STRUCT.DECL.FAM Declaration of Flexible Array Member
Compass/ROSE Can detect all of these
Helix QAC 2022.4 C1061, C1062, C1063, C1064
Klocwork 2022.4 MISRA.INCOMPLETE.STRUCT MISRA.MEMB.FLEX_ARRAY.2012
LDRA tool suite 9.7.1 649 S, 650 S Fully implemented
Parasoft C/C++test 2022.2 CERT_C-MEM33-a CERT_C-MEM33-b Allocate structures containing a flexible array member dynamically Do not copy instances of structures containing a flexible array member
Polyspace Bug Finder R2023a CERT C: Rule MEM33-C Checks for misuse of structure with flexible array member (rule fully covered)
PRQA QA-C 9.7 1061, 1062, 1063, 1064
RuleChecker 22.04 flexible-array-member-assignment flexible-array-member-declaration 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+MEM33-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
CERT C Secure Coding Standard DCL38-C. Use the correct syntax when declaring a flexible array member 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-401 and MEM33-CPP** + +There is no longer a C++ rule for MEM33-CPP. (In fact, all C++ rules from 30-50 are gone, because we changed the numbering system to be 50-99 for C++ rules.) + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 6.7.2.1, "Structure and Union Specifiers"
\[ JTC1/SC22/WG14 N791 \] Solving the Struct Hack Problem
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MEM33-C: Allocate and copy structures containing a flexible array member dynamically](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.ql b/c/cert/src/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.ql new file mode 100644 index 0000000000..b4d2a9127b --- /dev/null +++ b/c/cert/src/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.ql @@ -0,0 +1,119 @@ +/** + * @id c/cert/copy-structs-with-a-flexible-array-member-dynamically + * @name MEM33-C: Copy structures containing a flexible array member using memcpy or a similar function + * @description Copying a structure containing a flexbile array member by assignment ignores the + * flexible array member data. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/mem33-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.Variable +import semmle.code.cpp.security.BufferAccess + +/** + * An expanded variant of the CodeQL standard library `MemcpyBA` + * class that additionally models the `__builtin___memcpy_chk` function. + */ +class MemcpyBAExpanded extends BufferAccess { + MemcpyBAExpanded() { + this.(FunctionCall).getTarget().getName() = + ["memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "__builtin___memcpy_chk"] + } + + override string getName() { result = this.(FunctionCall).getTarget().getName() } + + override Expr getBuffer(string bufferDesc, int accessType) { + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "destination buffer" and + accessType = 2 + or + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "source buffer" and + accessType = 2 + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * A class representing an `Expr` that copies a flexible array struct. + */ +abstract class FlexibleArrayCopyExpr extends Expr { } + +/** + * A simple assignment of a flexible array struct to another flexible array struct. + */ +class FlexibleArraySimpleCopyExpr extends FlexibleArrayCopyExpr { + FlexibleArraySimpleCopyExpr() { + exists(Variable v | + this.getUnspecifiedType() instanceof FlexibleArrayStructType and + ( + exists(Initializer init | + init.getDeclaration() = v and + init.getExpr() = this + ) + or + exists(AssignExpr assign | + assign.getLValue().getUnspecifiedType() instanceof FlexibleArrayStructType and + assign.getRValue() = this + ) + ) + ) + } +} + +/** + * A call to a function that copies a flexible array struct. + */ +class FlexibleArrayMemcpyCallExpr extends FlexibleArrayCopyExpr, MemcpyBAExpanded { + FlexibleArrayMemcpyCallExpr() { + not exists(Expr e | + e = this.getBuffer(_, _) and + not e.getType().stripType() instanceof FlexibleArrayStructType + ) + } + + /** + * Holds if the size copied does not account for the flexible array member. + */ + predicate isFlexibleArrayCopiedWithInsufficientSize() { + this.getSize() <= + max(this.getBuffer(_, _) + .getUnderlyingType() + .(DerivedType) + .getBaseType() + .getUnspecifiedType() + .getSize() + ) + } +} + +from FlexibleArrayCopyExpr faCopy, string message +where + not isExcluded(faCopy, Memory2Package::copyStructsWithAFlexibleArrayMemberDynamicallyQuery()) and + ( + // case 1: simple assignment + faCopy instanceof FlexibleArraySimpleCopyExpr and + message = "Struct containing a flexible array member copied by assignment." + or + // case 2: call to memcpy + faCopy.(FlexibleArrayMemcpyCallExpr).isFlexibleArrayCopiedWithInsufficientSize() and + message = + "Struct containing a flexible array member copied by call to memcpy with insufficient size." + ) +select faCopy, message diff --git a/c/cert/src/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.md b/c/cert/src/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.md new file mode 100644 index 0000000000..c6fa7eb298 --- /dev/null +++ b/c/cert/src/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.md @@ -0,0 +1,167 @@ +# MEM34-C: Only free memory allocated dynamically + +This query implements the CERT-C rule MEM34-C: + +> Only free memory allocated dynamically + + +## Description + +The C Standard, Annex J \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states that the behavior of a program is [undefined ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) when + +> The pointer argument to the `free` or `realloc` function does not match a pointer earlier returned by a memory management function, or the space has been deallocated by a call to `free` or `realloc`. + + +See also [undefined behavior 179](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_179). + +Freeing memory that is not allocated dynamically can result in heap corruption and other serious errors. Do not call `free()` on a pointer other than one returned by a standard memory allocation function, such as `malloc()`, `calloc()`, `realloc()`, or `aligned_alloc()`. + +A similar situation arises when `realloc()` is supplied a pointer to non-dynamically allocated memory. The `realloc()` function is used to resize a block of dynamic memory. If `realloc()` is supplied a pointer to memory not allocated by a standard memory allocation function, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One consequence is that the program may [terminate abnormally](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +This rule does not apply to null pointers. The C Standard guarantees that if `free()` is passed a null pointer, no action occurs. + +## Noncompliant Code Example + +This noncompliant code example sets `c_str` to reference either dynamically allocated memory or a statically allocated string literal depending on the value of `argc`. In either case, `c_str` is passed as an argument to `free()`. If anything other than dynamically allocated memory is referenced by `c_str`, the call to `free(c_str)` is erroneous. + +```cpp +#include +#include +#include + +enum { MAX_ALLOCATION = 1000 }; + +int main(int argc, const char *argv[]) { + char *c_str = NULL; + size_t len; + + if (argc == 2) { + len = strlen(argv[1]) + 1; + if (len > MAX_ALLOCATION) { + /* Handle error */ + } + c_str = (char *)malloc(len); + if (c_str == NULL) { + /* Handle error */ + } + strcpy(c_str, argv[1]); + } else { + c_str = "usage: $>a.exe [string]"; + printf("%s\n", c_str); + } + free(c_str); + return 0; +} + +``` + +## Compliant Solution + +This compliant solution eliminates the possibility of `c_str` referencing memory that is not allocated dynamically when passed to `free()`: + +```cpp +#include +#include +#include + +enum { MAX_ALLOCATION = 1000 }; + +int main(int argc, const char *argv[]) { + char *c_str = NULL; + size_t len; + + if (argc == 2) { + len = strlen(argv[1]) + 1; + if (len > MAX_ALLOCATION) { + /* Handle error */ + } + c_str = (char *)malloc(len); + if (c_str == NULL) { + /* Handle error */ + } + strcpy(c_str, argv[1]); + } else { + printf("%s\n", "usage: $>a.exe [string]"); + return EXIT_FAILURE; + } + free(c_str); + return 0; +} + +``` + +## Noncompliant Code Example (realloc()) + +In this noncompliant example, the pointer parameter to `realloc()`, `buf`, does not refer to dynamically allocated memory: + +```cpp +#include + +enum { BUFSIZE = 256 }; + +void f(void) { + char buf[BUFSIZE]; + char *p = (char *)realloc(buf, 2 * BUFSIZE); + if (p == NULL) { + /* Handle error */ + } +} + +``` + +## Compliant Solution (realloc()) + +In this compliant solution, `buf` refers to dynamically allocated memory: + +```cpp +#include + +enum { BUFSIZE = 256 }; + +void f(void) { + char *buf = (char *)malloc(BUFSIZE * sizeof(char)); + char *p = (char *)realloc(buf, 2 * BUFSIZE); + if (p == NULL) { + /* Handle error */ + } +} +``` +Note that `realloc()` will behave properly even if `malloc()` failed, because when given a null pointer, `realloc()` behaves like a call to `malloc()`. + +## Risk Assessment + +The consequences of this error depend on the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation), but they range from nothing to arbitrary code execution if that memory is reused by `malloc()`. + +
Rule Severity Likelihood Remediation Cost Priority Level
MEM34-C High Likely Medium P18 L1
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 invalid-free Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-MEM34 Can detect memory deallocations for stack objects
Clang 3.9 clang-analyzer-unix.Malloc Checked by clang-tidy ; can detect some instances of this rule, but does not detect all
CodeSonar 7.2p0 ALLOC.TM Type Mismatch
Compass/ROSE Can detect some violations of this rule
Coverity 2017.07 BAD_FREE Identifies calls to free() where the argument is a pointer to a function or an array. It also detects the cases where free() is used on an address-of expression, which can never be heap allocated. Coverity Prevent cannot discover all violations of this rule, so further verification is necessary
Helix QAC 2022.4 DF2721, DF2722, DF2723
Klocwork 2022.4 FNH.MIGHT FNH.MUST
LDRA tool suite 9.7.1 407 S, 483 S, 644 S, 645 S, 125 D Partially implemented
Parasoft C/C++test 2022.2 CERT_C-MEM34-a Do not free resources using invalid pointers
Parasoft Insure++ Runtime analysis
PC-lint Plus 1.4 424, 673 Fully supported
Polyspace Bug Finder R2023a CERT C: Rule MEM34-C Checks for: Invalid free of pointernvalid free of pointer, invalid reallocation of pointernvalid reallocation of pointer. Rule fully covered.
PRQA QA-C 9.7 2721, 2722, 2723
PRQA QA-C++ 4.4 2721 , 2722, 2723
PVS-Studio 7.23 V585 , V726
RuleChecker 22.04 invalid-free Partially checked
TrustInSoft Analyzer 1.38 unclassified ("free expects a free-able address") Exhaustively verified (see one compliant and one non-compliant example ).
+ + +## Related Vulnerabilities + +[CVE-2015-0240](https://securityblog.redhat.com/2015/02/23/samba-vulnerability-cve-2015-0240/) describes a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in which an uninitialized pointer is passed to `TALLOC_FREE()`, which is a Samba-specific memory deallocation macro that wraps the `talloc_free()` function. The implementation of `talloc_free()` would access the uninitialized pointer, resulting in a remote [exploit](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit). + +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+MEM34-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
CERT C Secure Coding Standard MEM31-C. Free dynamically allocated memory when no longer needed Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C MEM51-CPP. Properly deallocate dynamically allocated resources Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Reallocating or freeing memory that was not dynamically allocated \[xfree\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-590 , Free of Memory Not on the Heap 2017-07-10: CERT: Exact
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause J.2, "Undefined Behavior"
\[ Seacord 2013b \] Chapter 4, "Dynamic Memory Management"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MEM34-C: Only free memory allocated dynamically](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.ql b/c/cert/src/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.ql new file mode 100644 index 0000000000..78081944be --- /dev/null +++ b/c/cert/src/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.ql @@ -0,0 +1,29 @@ +/** + * @id c/cert/only-free-memory-allocated-dynamically-cert + * @name MEM34-C: Only free memory allocated dynamically + * @description Freeing memory that is not allocated dynamically can lead to heap corruption and + * undefined behavior. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/cert/id/mem34-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.cpp.rules.onlyfreememoryallocateddynamicallyshared.OnlyFreeMemoryAllocatedDynamicallyShared + +class OnlyFreeMemoryAllocatedDynamicallyCertQuery extends OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery +{ + OnlyFreeMemoryAllocatedDynamicallyCertQuery() { + this = Memory2Package::onlyFreeMemoryAllocatedDynamicallyCertQuery() + } +} diff --git a/c/cert/src/rules/MEM35-C/InsufficientMemoryAllocatedForObject.md b/c/cert/src/rules/MEM35-C/InsufficientMemoryAllocatedForObject.md new file mode 100644 index 0000000000..7f3c70efbb --- /dev/null +++ b/c/cert/src/rules/MEM35-C/InsufficientMemoryAllocatedForObject.md @@ -0,0 +1,199 @@ +# MEM35-C: Allocate sufficient memory for an object + +This query implements the CERT-C rule MEM35-C: + +> Allocate sufficient memory for an object + + +## Description + +The types of integer expressions used as size arguments to `malloc()`, `calloc()`, `realloc()`, or `aligned_alloc()` must have sufficient range to represent the size of the objects to be stored. If size arguments are incorrect or can be manipulated by an attacker, then a buffer overflow may occur. Incorrect size arguments, inadequate range checking, integer overflow, or truncation can result in the allocation of an inadequately sized buffer. + +Typically, the amount of memory to allocate will be the size of the type of object to allocate. When allocating space for an array, the size of the object will be multiplied by the bounds of the array. When allocating space for a structure containing a flexible array member, the size of the array member must be added to the size of the structure. (See [MEM33-C. Allocate and copy structures containing a flexible array member dynamically](https://wiki.sei.cmu.edu/confluence/display/c/MEM33-C.++Allocate+and+copy+structures+containing+a+flexible+array+member+dynamically).) Use the correct type of the object when computing the size of memory to allocate. + +[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) is a specific instance of this rule. + +## Noncompliant Code Example (Pointer) + +In this noncompliant code example, inadequate space is allocated for a `struct tm` object because the size of the pointer is being used to determine the size of the pointed-to object: + +```cpp +#include +#include + +struct tm *make_tm(int year, int mon, int day, int hour, + int min, int sec) { + struct tm *tmb; + tmb = (struct tm *)malloc(sizeof(tmb)); + if (tmb == NULL) { + return NULL; + } + *tmb = (struct tm) { + .tm_sec = sec, .tm_min = min, .tm_hour = hour, + .tm_mday = day, .tm_mon = mon, .tm_year = year + }; + return tmb; +} +``` + +## Compliant Solution (Pointer) + +In this compliant solution, the correct amount of memory is allocated for the `struct tm` object. When allocating space for a single object, passing the (dereferenced) pointer type to the `sizeof` operator is a simple way to allocate sufficient memory. Because the `sizeof` operator does not evaluate its operand, dereferencing an uninitialized or null pointer in this context is well-defined behavior. + +```cpp +#include +#include + +struct tm *make_tm(int year, int mon, int day, int hour, + int min, int sec) { + struct tm *tmb; + tmb = (struct tm *)malloc(sizeof(*tmb)); + if (tmb == NULL) { + return NULL; + } + *tmb = (struct tm) { + .tm_sec = sec, .tm_min = min, .tm_hour = hour, + .tm_mday = day, .tm_mon = mon, .tm_year = year + }; + return tmb; +} +``` + +## Noncompliant Code Example (Integer) + +In this noncompliant code example, an array of `long` is allocated and assigned to `p`. The code attempts to check for unsigned integer overflow 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) and also ensures that `len` is not equal to zero. (See [MEM04-C. Beware of zero-length allocations](https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations).) However, because `sizeof(int)` is used to compute the size, and not `sizeof(long)`, an insufficient amount of memory can be allocated on implementations where `sizeof(long)` is larger than `sizeof(int)`, and filling the array can cause a heap buffer overflow. + +```cpp +#include +#include + +void function(size_t len) { + long *p; + if (len == 0 || len > SIZE_MAX / sizeof(long)) { + /* Handle overflow */ + } + p = (long *)malloc(len * sizeof(int)); + if (p == NULL) { + /* Handle error */ + } + free(p); +} + +``` + +## Compliant Solution (Integer) + +This compliant solution uses `sizeof(long)` to correctly size the memory allocation: + +```cpp +#include +#include + +void function(size_t len) { + long *p; + if (len == 0 || len > SIZE_MAX / sizeof(long)) { + /* Handle overflow */ + } + p = (long *)malloc(len * sizeof(long)); + if (p == NULL) { + /* Handle error */ + } + free(p); +} + +``` + +## Compliant Solution (Integer) + +Alternatively, `sizeof(*p)` can be used to properly size the allocation: + +```cpp +#include +#include + +void function(size_t len) { + long *p; + if (len == 0 || len > SIZE_MAX / sizeof(*p)) { + /* Handle overflow */ + } + p = (long *)malloc(len * sizeof(*p)); + if (p == NULL) { + /* Handle error */ + } + free(p); +} +``` + +## Risk Assessment + +Providing invalid size arguments to memory allocation functions can lead to buffer overflows and the execution of arbitrary code with the permissions of the vulnerable process. + +
Rule Severity Likelihood Remediation Cost Priority Level
MEM35-C High Probable High P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 malloc-size-insufficient Partially checked Besides direct rule violations, all undefined behaviour resulting from invalid memory accesses is reported by Astrée.
Axivion Bauhaus Suite 7.2.0 CertC-MEM35
CodeSonar 7.2p0 ALLOC.SIZE.ADDOFLOW ALLOC.SIZE.IOFLOW ALLOC.SIZE.MULOFLOW ALLOC.SIZE.SUBUFLOW ALLOC.SIZE.TRUNC IO.TAINT.SIZE MISC.MEM.SIZE.BADLANG.MEM.BOLANG.MEM.BULANG.STRUCT.PARITHLANG.STRUCT.PBBLANG.STRUCT.PPELANG.MEM.TBALANG.MEM.TOLANG.MEM.TU Addition overflow of allocation size Addition overflow of allocation size Multiplication overflow of allocation size Subtraction underflow of allocation size Truncation of allocation size Tainted allocation size Unreasonable size argument 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 Could check violations of this rule by examining the size expression to malloc() or memcpy() functions. Specifically, the size argument should be bounded by 0, SIZE_MAX , and, unless it is a variable of type size_t or rsize_t , it should be bounds-checked before the malloc() call. If the argument is of the expression a\*b , then an appropriate check is if (a < SIZE_MAX / b && a > 0) ...
Coverity 2017.07 BAD_ALLOC_STRLEN SIZECHECK (deprecated) Partially implemented Can find instances where string length is miscalculated (length calculated may be one less than intended) for memory allocation purposes. Coverity Prevent cannot discover all violations of this rule, so further verification is necessary Finds memory allocations that are assigned to a pointer that reference objects larger than the allocated block
Helix QAC 2022.4 C0696, C0701, C1069, C1071, C1073, C2840 DF2840, DF2841, DF2842, DF2843, DF2935, DF2936, DF2937, DF2938
Klocwork 2022.4 INCORRECT.ALLOC_SIZE SV.TAINTED.ALLOC_SIZE
LDRA tool suite 9.7.1 400 S, 487 S, 115 D Enhanced enforcement
Splint 3.1.1
Parasoft C/C++test 2022.2 CERT_C-MEM35-a Do not use sizeof operator on pointer type to specify the size of the memory to be allocated via 'malloc', 'calloc' or 'realloc' function
PC-lint Plus 1.4 433, 826 Partially supported
Polyspace Bug Finder R2023a CERT C: Rule MEM35-C Checks for: Pointer access out of boundsointer access out of bounds, memory allocation with tainted sizeemory allocation with tainted size. Rule partially covered.
PRQA QA-C 9.7 0696, 0701, 1069, 1071, 1073, 2840, 2841, 2842, 2843, 2935, 2936, 2937, 2938
PRQA QA-C++ 4.4 2840, 2841, 2842, 2843, 2935, 2936, 2937, 2938
PVS-Studio 7.23 V531 , V635 , V781
RuleChecker 22.04 malloc-size-insufficient Partially checked
TrustInSoft Analyzer 1.38 mem_access 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+MEM35-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
CERT C Secure Coding Standard ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard INT32-C. Ensure that operations on signed integers do not result in overflow Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MEM04-C. Beware of zero-length allocations Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Buffer Boundary Violation (Buffer Overflow) \[HCB\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Taking the size of a pointer to determine the size of the pointed-to type \[sizeofptr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-131 , Incorrect Calculation of Buffer Size 2017-05-16: CERT: Rule subset of CWE
CWE 2.11 CWE-680 2017-05-18: CERT: Rule subset of CWE
CWE 2.11 CWE-789 2017-06-12: 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-680 and MEM35-C** + +Intersection( INT32-C, MEM35-C) = Ø + +CWE-680 = Union( MEM35-C, list) where list = + +* Overflowed buffers with inadequate sizes not produced by integer overflow +**CWE-467 and MEM35-C** + +CWE-467 = Subset( MEM35-C) + +**CWE-789 and MEM35-C** + +Intersection( MEM35-C, CWE-789) = + +* Insufficient memory allocation on the heap +MEM35-C – CWE-789 = +* Insufficient memory allocation with trusted value but incorrect calculation +CWE-789 - MEM35-C = +* Sufficient memory allocation (possibly over-allocation) with untrusted value +**CWE-120 and MEM35-C** + +Intersection( MEM35-C, CWE-120) = Ø + +CWE-120 specifically addresses buffer overflow operations, which occur in the context of string-copying. MEM35-C specifically addresses allocation of memory ranges (some of which may be for subsequent string copy operations). + +Consequently, they address different sections of code, although one (or both) may be responsible for a single buffer overflow vulnerability. + +**CWE-131 and MEM35-C** + +* Intersection( INT30-C, MEM35-C) = Ø +* CWE-131 = Union( MEM35-C, list) where list = +* Miscalculating a buffer for a non-heap region (such as a variable-length array) + +## Bibliography + +
\[ Coverity 2007 \]
\[ Drepper 2006 \] Section 2.1.1, "Respecting Memory Bounds"
\[ Seacord 2013 \] Chapter 4, "Dynamic Memory Management" Chapter 5, "Integer Security"
\[ Viega 2005 \] Section 5.6.8, "Use of sizeof() on a Pointer Type"
\[ xorl 2009 \] CVE-2009-0587: Evolution Data Server Base64 Integer Overflows
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MEM35-C: Allocate sufficient memory for an object](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM35-C/InsufficientMemoryAllocatedForObject.ql b/c/cert/src/rules/MEM35-C/InsufficientMemoryAllocatedForObject.ql new file mode 100644 index 0000000000..06fd267560 --- /dev/null +++ b/c/cert/src/rules/MEM35-C/InsufficientMemoryAllocatedForObject.ql @@ -0,0 +1,167 @@ +/** + * @id c/cert/insufficient-memory-allocated-for-object + * @name MEM35-C: Allocate sufficient memory for an object + * @description The size of memory allocated dynamically must be adequate to represent the type of + * object referenced by the allocated memory. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/cert/id/mem35-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.controlflow.Guards +import semmle.code.cpp.dataflow.TaintTracking +import semmle.code.cpp.models.Models + +/** + * Gets the type of the operand of `op`. + */ +Type getSizeofOperatorType(SizeofOperator op) { + result = op.(SizeofExprOperator).getExprOperand().getType() + or + result = op.(SizeofTypeOperator).getTypeOperand() +} + +/** + * A function call which allocates memory, such as `malloc`. + */ +class AllocationFunctionCall extends AllocationExpr, FunctionCall { + AllocationFunctionCall() { this.getTarget() instanceof AllocationFunction } + + /** + * Gets the size argument `Expr` of this allocation function call. + */ + Expr getSizeArg() { + result = this.getArgument(this.getTarget().(AllocationFunction).getSizeArg()) + } + + /** + * Gets the computed value of the size argument of this allocation function call. + */ + int getSizeExprValue() { result = upperBound(this.getSizeArg()) } + + /** + * Gets the type of the object the allocation function result is assigned to. + * + * If the allocation is not assigned to a variable, this predicate does not hold. + */ + Type getBaseType() { + exists(PointerType pointer | + pointer.getBaseType() = result and + ( + exists(AssignExpr assign | + assign.getRValue() = this and assign.getLValue().getType() = pointer + ) + or + exists(Variable v | v.getInitializer().getExpr() = this and v.getType() = pointer) + ) + ) + } + + /** + * Gets a message describing the problem with this allocation function call. + * The `e` and `description` respectively provide an expression that influences + * the size of the allocation and a string describing that expression. + */ + string getMessageAndSourceInfo(Expr e, string description) { none() } +} + +/** + * An `AllocationFunctionCall` where the size argument is tainted by a `SizeofOperator` + * that has an operand of a different type than the base type of the variable assigned + * the result of the allocation call. + */ +class WrongSizeofOperatorAllocationFunctionCall extends AllocationFunctionCall { + SizeofOperator source; + + WrongSizeofOperatorAllocationFunctionCall() { + this.getBaseType() != getSizeofOperatorType(source) and + TaintTracking::localExprTaint(source, this.getSizeArg().getAChild*()) + } + + override string getMessageAndSourceInfo(Expr e, string description) { + result = "Allocation size calculated from the size of a different type ($@)." and + e = source and + description = "sizeof(" + getSizeofOperatorType(source).getName() + ")" + } +} + +/** + * An `AllocationFunctionCall` that allocates a size that is not a multiple + * of the size of the base type of the variable assigned the allocation. + * + * For example, an allocation of 14 bytes for `float` (`sizeof(float) == 4`) + * indicates an erroroneous allocation size, as 14 is not a multiple of 4 and + * thus cannot be the exact size of an array of floats. + * + * This class cannot also be a `WrongSizeofOperatorAllocationFunctionCall` instance, + * as an identified `SizeofOperator` operand type mismatch is more likely to indicate + * the root cause of an allocation size that is not a multiple of the base type size. + */ +class WrongSizeMultipleAllocationFunctionCall extends AllocationFunctionCall { + WrongSizeMultipleAllocationFunctionCall() { + // de-duplicate results if there is more precise info from a sizeof operator + not this instanceof WrongSizeofOperatorAllocationFunctionCall and + // the allocation size is not a multiple of the base type size + exists(int basesize, int allocated | + basesize = min(this.getBaseType().getSize()) and + allocated = this.getSizeExprValue() and + not exists(int size | this.getBaseType().getSize() = size | + size = 0 or + (allocated / size) * size = allocated + ) + ) + } + + override string getMessageAndSourceInfo(Expr e, string description) { + result = + "Allocation size (" + this.getSizeExprValue().toString() + + " bytes) is not a multiple of the size of '" + this.getBaseType().getName() + "' (" + + min(this.getBaseType().getSize()).toString() + " bytes)." and + e = this.getSizeArg() and + description = "" + } +} + +/** + * An `AllocationFunctionCall` where the size argument might be tainted by an overflowing + * or wrapping integer expression that is not checked for validity before the allocation. + */ +class OverflowingSizeAllocationFunctionCall extends AllocationFunctionCall { + InterestingOverflowingOperation bop; + + OverflowingSizeAllocationFunctionCall() { + // `bop` is not pre-checked to prevent overflow/wrapping + not bop.hasValidPreCheck() and + // and the size argument is tainted by `bop` + TaintTracking::localExprTaint(bop, this.getSizeArg().getAChild*()) and + // and there does not exist a post-wrapping-check before the allocation call + not exists(GuardCondition gc | + gc = bop.getAValidPostCheck() and + gc.controls(this.getBasicBlock(), _) + ) + } + + override string getMessageAndSourceInfo(Expr e, string description) { + result = "Allocation size derived from potentially overflowing or wrapping $@." and + e = bop and + description = "integer operation" + } +} + +from AllocationFunctionCall alloc, string message, Expr source, string sourceMessage +where + not isExcluded(alloc, Memory3Package::insufficientMemoryAllocatedForObjectQuery()) and + message = alloc.getMessageAndSourceInfo(source, sourceMessage) +select alloc, message, source, sourceMessage diff --git a/c/cert/src/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.md b/c/cert/src/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.md new file mode 100644 index 0000000000..51cf1b2179 --- /dev/null +++ b/c/cert/src/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.md @@ -0,0 +1,172 @@ +# MEM36-C: Do not modify the alignment of objects by calling realloc + +This query implements the CERT-C rule MEM36-C: + +> Do not modify the alignment of objects by calling realloc + + +## Description + +Do not invoke `realloc()` to modify the size of allocated objects that have stricter alignment requirements than those guaranteed by `malloc()`. Storage allocated by a call to the standard `aligned_alloc()` function, for example, can have stricter than normal alignment requirements. The C standard requires only that a pointer returned by `realloc()` be suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement. + +## Noncompliant Code Example + +This noncompliant code example returns a pointer to allocated memory that has been aligned to a 4096-byte boundary. If the `resize` argument to the `realloc()` function is larger than the object referenced by `ptr`, then `realloc()` will allocate new memory that is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement but may not preserve the stricter alignment of the original object. + +```cpp +#include + +void func(void) { + size_t resize = 1024; + size_t alignment = 1 << 12; + int *ptr; + int *ptr1; + + if (NULL == (ptr = (int *)aligned_alloc(alignment, sizeof(int)))) { + /* Handle error */ + } + + if (NULL == (ptr1 = (int *)realloc(ptr, resize))) { + /* Handle error */ + } +} +``` +**Implementation Details** + +When compiled with GCC 4.1.2 and run on the x86_64 Red Hat Linux platform, the following code produces the following output: + +**CODE** + +```cpp +#include +#include + +int main(void) { + size_t size = 16; + size_t resize = 1024; + size_t align = 1 << 12; + int *ptr; + int *ptr1; + + if (posix_memalign((void **)&ptr, align , size) != 0) { + exit(EXIT_FAILURE); + } + + printf("memory aligned to %zu bytes\n", align); + printf("ptr = %p\n\n", ptr); + + if ((ptr1 = (int*) realloc((int *)ptr, resize)) == NULL) { + exit(EXIT_FAILURE); + } + + puts("After realloc(): \n"); + printf("ptr1 = %p\n", ptr1); + + free(ptr1); + return 0; +} + + +``` +**OUTPUT** + +```cpp +memory aligned to 4096 bytes +ptr = 0x1621b000 + +After realloc(): +ptr1 = 0x1621a010 + +``` +`ptr1` is no longer aligned to 4096 bytes. + +## Compliant Solution + +This compliant solution allocates `resize` bytes of new memory with the same alignment as the old memory, copies the original memory content, and then frees the old memory. This solution has [implementation-defined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior) because it depends on whether extended alignments in excess of `_Alignof (max_align_t)` are supported and the contexts in which they are supported. If not supported, the behavior of this compliant solution is undefined. + +```cpp +#include +#include + +void func(void) { + size_t resize = 1024; + size_t alignment = 1 << 12; + int *ptr; + int *ptr1; + + if (NULL == (ptr = (int *)aligned_alloc(alignment, + sizeof(int)))) { + /* Handle error */ + } + + if (NULL == (ptr1 = (int *)aligned_alloc(alignment, + resize))) { + /* Handle error */ + } + + if (NULL == memcpy(ptr1, ptr, sizeof(int))) { + /* Handle error */ + } + + free(ptr); +} +``` + +## Compliant Solution (Windows) + +Windows defines the `_aligned_malloc()` function to allocate memory on a specified alignment boundary. The `_aligned_realloc()` \[[MSDN](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-MSDN)\] can be used to change the size of this memory. This compliant solution demonstrates one such usage: + +```cpp +#include + +void func(void) { + size_t alignment = 1 << 12; + int *ptr; + int *ptr1; + + /* Original allocation */ + if (NULL == (ptr = (int *)_aligned_malloc(sizeof(int), + alignment))) { + /* Handle error */ +} + + /* Reallocation */ + if (NULL == (ptr1 = (int *)_aligned_realloc(ptr, 1024, + alignment))) { + _aligned_free(ptr); + /* Handle error */ + } + + _aligned_free(ptr1); +} +``` +The `size` and `alignment` arguments for `_aligned_malloc()` are provided in reverse order of the C Standard `aligned_alloc()` function. + +## Risk Assessment + +Improper alignment can lead to arbitrary memory locations being accessed and written to. + +
Recommendation Severity Likelihood Remediation Cost Priority Level
MEM36-C Low Probable High P2 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 Supported, but no explicit checker
Axivion Bauhaus Suite 7.2.0 CertC-MEM36 Fully implemented
CodeSonar 7.2p0 BADFUNC.REALLOC Use of realloc
Helix QAC 2022.4 C5027 C++5034
Klocwork 2022.4 AUTOSAR.STDLIB.MEMORY
LDRA tool suite 9.7.1 44 S Enhanced enforcement
Parasoft C/C++test 2022.2 CERT_C-MEM36-a Do not modify the alignment of objects by calling realloc()
Polyspace Bug Finder R2023a CERT C: Rule MEM36-C Checks for alignment change after memory allocation (rule fully covered)
PRQA QA-C 9.7 5027
PRQA QA-C++ 4.4 5034
+ + +## 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+MEM36-C). + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 7.22.3.1, "The aligned_alloc Function"
\[ MSDN \] aligned_malloc()
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MEM36-C: Do not modify the alignment of objects by calling realloc](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.ql b/c/cert/src/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.ql new file mode 100644 index 0000000000..90c34a44a2 --- /dev/null +++ b/c/cert/src/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.ql @@ -0,0 +1,63 @@ +/** + * @id c/cert/do-not-modify-alignment-of-memory-with-realloc + * @name MEM36-C: Do not modify the alignment of objects by calling realloc + * @description Realloc does not preserve the alignment of memory allocated with aligned_alloc and + * can result in undefined behavior if reallocating more strictly aligned memory. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/cert/id/mem36-c + * correctness + * security + * 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 +import codingstandards.cpp.Alignment +import semmle.code.cpp.dataflow.DataFlow +import AlignedAllocToReallocFlow::PathGraph + +int getStatedValue(Expr e) { + // `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful + // result in this case we pick the minimum value obtainable from dataflow and range analysis. + result = + upperBound(e) + .minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())) +} + +class NonDefaultAlignedAllocCall extends FunctionCall { + NonDefaultAlignedAllocCall() { + this.getTarget().hasName("aligned_alloc") and + not getStatedValue(this.getArgument(0)) = getGlobalMaxAlignT() + } +} + +class ReallocCall extends FunctionCall { + ReallocCall() { this.getTarget().hasName("realloc") } +} + +module AlignedAllocToReallocConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof NonDefaultAlignedAllocCall + } + + predicate isSink(DataFlow::Node sink) { + exists(ReallocCall realloc | sink.asExpr() = realloc.getArgument(0)) + } +} + +module AlignedAllocToReallocFlow = DataFlow::Global; + +from AlignedAllocToReallocFlow::PathNode source, AlignedAllocToReallocFlow::PathNode sink +where + not isExcluded(sink.getNode().asExpr(), + Memory2Package::doNotModifyAlignmentOfMemoryWithReallocQuery()) and + AlignedAllocToReallocFlow::flowPath(source, sink) +select sink, source, sink, "Memory allocated with $@ but reallocated with realloc.", + source.getNode().asExpr(), "aligned_alloc" diff --git a/c/cert/src/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.ql b/c/cert/src/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.ql index 5feb7d5f99..722e6fff80 100644 --- a/c/cert/src/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.ql +++ b/c/cert/src/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/msc30-c * security + * external/cert/severity/medium + * external/cert/likelihood/unlikely + * external/cert/remediation-cost/low + * external/cert/priority/p6 + * external/cert/level/l2 * external/cert/obligation/rule */ @@ -14,7 +19,8 @@ import cpp import codingstandards.c.cert import codingstandards.cpp.rules.donotuserandforgeneratingpseudorandomnumbers.DoNotUseRandForGeneratingPseudorandomNumbers -class RandUsedForGeneratingPseudorandomNumbersQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery { +class RandUsedForGeneratingPseudorandomNumbersQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery +{ RandUsedForGeneratingPseudorandomNumbersQuery() { this = MiscPackage::randUsedForGeneratingPseudorandomNumbersQuery() } diff --git a/c/cert/src/rules/MSC32-C/ProperlySeedPseudorandomNumberGenerators.ql b/c/cert/src/rules/MSC32-C/ProperlySeedPseudorandomNumberGenerators.ql index b175dd5fa2..85623d9390 100644 --- a/c/cert/src/rules/MSC32-C/ProperlySeedPseudorandomNumberGenerators.ql +++ b/c/cert/src/rules/MSC32-C/ProperlySeedPseudorandomNumberGenerators.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/msc32-c * security + * external/cert/severity/medium + * external/cert/likelihood/likely + * external/cert/remediation-cost/low + * external/cert/priority/p18 + * external/cert/level/l1 * external/cert/obligation/rule */ @@ -15,30 +20,22 @@ import codingstandards.c.cert /** Defines a class that models function calls to srandom() */ class SRandomCall extends FunctionCall { - SRandomCall(){ - getTarget().hasGlobalOrStdName("srandom") - } + SRandomCall() { getTarget().hasGlobalOrStdName("srandom") } /** Holds if the call is not obviously trivial. */ - predicate isTrivial(){ - getArgument(0) instanceof Literal - } + predicate isTrivial() { getArgument(0) instanceof Literal } } from FunctionCall fc where not isExcluded(fc, MiscPackage::properlySeedPseudorandomNumberGeneratorsQuery()) and - - // find all calls to random() - fc.getTarget().hasGlobalOrStdName("random") and - + // find all calls to random() + fc.getTarget().hasGlobalOrStdName("random") and // where there isn't a call to srandom that comes before it that is // non-trivial not exists(SRandomCall sr | - // normally we would want to do this in reverse --- but srandom() is - // not pure and the order does not matter. + // normally we would want to do this in reverse --- but srandom() is + // not pure and the order does not matter. sr.getASuccessor*() = fc and not sr.isTrivial() ) - - select fc, "Call to `random()` without a valid call to `srandom()`." diff --git a/c/cert/src/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.md b/c/cert/src/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.md new file mode 100644 index 0000000000..d22bcd6e52 --- /dev/null +++ b/c/cert/src/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.md @@ -0,0 +1,133 @@ +# MSC33-C: Do not pass invalid data to the asctime() function + +This query implements the CERT-C rule MSC33-C: + +> Do not pass invalid data to the asctime() function + + +## Description + +The C Standard, 7.27.3.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], provides the following sample implementation of the `asctime()` function: + +```cpp +char *asctime(const struct tm *timeptr) { + static const char wday_name[7][3] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static const char mon_name[12][3] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + static char result[26]; + sprintf( + result, + "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", + wday_name[timeptr->tm_wday], + mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + 1900 + timeptr->tm_year + ); + return result; +} + +``` +This function is supposed to output a character string of 26 characters at most, including the terminating null character. If we count the length indicated by the format directives, we arrive at 25. Taking into account the terminating null character, the array size of the string appears sufficient. + +However, this [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) assumes that the values of the `struct tm` data are within normal ranges and does nothing to enforce the range limit. If any of the values print more characters than expected, the `sprintf()` function may overflow the `result` array. For example, if `tm_year` has the value `12345,` then 27 characters (including the terminating null character) are printed, resulting in a buffer overflow. + +The* POSIX® Base Specifications* \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\] says the following about the `asctime()` and `asctime_r()` functions: + +> These functions are included only for compatibility with older implementations. They have undefined behavior if the resulting string would be too long, so the use of these functions should be discouraged. On implementations that do not detect output string length overflow, it is possible to overflow the output buffers in such a way as to cause applications to fail, or possible system security violations. Also, these functions do not support localized date and time formats. To avoid these problems, applications should use `strftime()` to generate strings from broken-down times. + + +The C Standard, Annex K, also defines `asctime_s()`, which can be used as a secure substitute for `asctime()`. + +The `asctime()` function appears in the list of obsolescent functions in [MSC24-C. Do not use deprecated or obsolescent functions](https://wiki.sei.cmu.edu/confluence/display/c/MSC24-C.+Do+not+use+deprecated+or+obsolescent+functions). + +## Noncompliant Code Example + +This noncompliant code example invokes the `asctime()` function with potentially unsanitized data: + +```cpp +#include + +void func(struct tm *time_tm) { + char *time = asctime(time_tm); + /* ... */ +} +``` + +## Compliant Solution (strftime()) + +The `strftime()` function allows the programmer to specify a more rigorous format and also to specify the maximum size of the resulting time string: + +```cpp +#include + +enum { maxsize = 26 }; + +void func(struct tm *time) { + char s[maxsize]; + /* Current time representation for locale */ + const char *format = "%c"; + + size_t size = strftime(s, maxsize, format, time); +} +``` +This call has the same effects as `asctime()` but also ensures that no more than `maxsize` characters are printed, preventing buffer overflow. + +## Compliant Solution (asctime_s()) + +The C Standard, Annex K, defines the `asctime_s()` function, which serves as a close replacement for the `asctime()` function but requires an additional argument that specifies the maximum size of the resulting time string: + +```cpp +#define __STDC_WANT_LIB_EXT1__ 1 +#include + +enum { maxsize = 26 }; + +void func(struct tm *time_tm) { + char buffer[maxsize]; + + if (asctime_s(buffer, maxsize, &time_tm)) { + /* Handle error */ + } +} +``` + +## Risk Assessment + +On [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) that do not detect output-string-length overflow, it is possible to overflow the output buffers. + +
Rule Severity Likelihood Remediation Cost Priority Level
MSC33-C High Likely Low P27 L1
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 Supported, but no explicit checker
Axivion Bauhaus Suite 7.2.0 CertC-MSC33
CodeSonar 7.2p0 BADFUNC.TIME_H Use of <time.h> Time/Date Function
Helix QAC 2022.4 C5032 C++5030
Klocwork 2022.4 CERT.MSC.ASCTIME
LDRA tool suite 9.7.1 44 S Enhanced Enforcement
Parasoft C/C++test 2022.2 CERT_C-MSC33-a The 'asctime()' and 'asctime_r()' functions should not be used
PC-lint Plus 1.4 586 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule MSC33-C Checks for use of obsolete standard function (rule fully covered)
PRQA QA-C 9.7 5032
PRQA QA-C++ 4.4 5030
RuleChecker 22.04 Supported, but no explicit checker
+ + +## 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+MSC33-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
CERT C Secure Coding Standard MSC24-C. Do not use deprecated or obsolescent functions Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ IEEE Std 1003.1:2013 \] XSH, System Interfaces, asctime
\[ ISO/IEC 9899:2011 \] 7.27.3.1, "The asctime Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MSC33-C: Do not pass invalid data to the asctime() function](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.ql b/c/cert/src/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.ql new file mode 100644 index 0000000000..67fa83e852 --- /dev/null +++ b/c/cert/src/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.ql @@ -0,0 +1,56 @@ +/** + * @id c/cert/do-not-pass-invalid-data-to-the-asctime-function + * @name MSC33-C: Do not pass invalid data to the asctime() function + * @description The data passed to the asctime() function is invalid. This can lead to buffer + * overflow. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/msc33-c + * security + * correctness + * external/cert/severity/high + * external/cert/likelihood/likely + * external/cert/remediation-cost/low + * external/cert/priority/p27 + * external/cert/level/l1 + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import semmle.code.cpp.dataflow.DataFlow + +/** + * The argument of a call to `asctime` + */ +class AsctimeArg extends Expr { + AsctimeArg() { + this = + any(FunctionCall f | f.getTarget().hasGlobalName(["asctime", "asctime_r"])).getArgument(0) + } +} + +/** + * Dataflow configuration for flow from a library function + * to a call of function `asctime` + */ +module TmStructSafeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + src.asExpr() + .(FunctionCall) + .getTarget() + .hasGlobalName(["localtime", "localtime_r", "localtime_s", "gmtime", "gmtime_r", "gmtime_s"]) + } + + predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof AsctimeArg } +} + +module TmStructSafeFlow = DataFlow::Global; + +from AsctimeArg fc +where + not isExcluded(fc, Contracts7Package::doNotPassInvalidDataToTheAsctimeFunctionQuery()) and + not TmStructSafeFlow::flowToExpr(fc) +select fc, + "The function `asctime` and `asctime_r` should be discouraged. Unsanitized input can overflow the output buffer." diff --git a/c/cert/src/rules/MSC37-C/ControlFlowReachesTheEndOfANonVoidFunction.ql b/c/cert/src/rules/MSC37-C/ControlFlowReachesTheEndOfANonVoidFunction.ql index 2f141417bf..265fc0af55 100644 --- a/c/cert/src/rules/MSC37-C/ControlFlowReachesTheEndOfANonVoidFunction.ql +++ b/c/cert/src/rules/MSC37-C/ControlFlowReachesTheEndOfANonVoidFunction.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/msc37-c * correctness + * external/cert/severity/high + * external/cert/likelihood/unlikely + * external/cert/remediation-cost/low + * external/cert/priority/p9 + * external/cert/level/l2 * external/cert/obligation/rule */ @@ -15,7 +20,8 @@ import cpp import codingstandards.c.cert import codingstandards.cpp.rules.nonvoidfunctiondoesnotreturn.NonVoidFunctionDoesNotReturn -class ControlFlowReachesTheEndOfANonVoidFunctionQuery extends NonVoidFunctionDoesNotReturnSharedQuery { +class ControlFlowReachesTheEndOfANonVoidFunctionQuery extends NonVoidFunctionDoesNotReturnSharedQuery +{ ControlFlowReachesTheEndOfANonVoidFunctionQuery() { this = MiscPackage::controlFlowReachesTheEndOfANonVoidFunctionQuery() } diff --git a/c/cert/src/rules/MSC38-C/DoNotTreatAPredefinedIdentifierAsObject.ql b/c/cert/src/rules/MSC38-C/DoNotTreatAPredefinedIdentifierAsObject.ql index 76e9c4539f..828f86dd95 100644 --- a/c/cert/src/rules/MSC38-C/DoNotTreatAPredefinedIdentifierAsObject.ql +++ b/c/cert/src/rules/MSC38-C/DoNotTreatAPredefinedIdentifierAsObject.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/msc38-c * correctness * 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/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.md b/c/cert/src/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.md new file mode 100644 index 0000000000..4b02f57f0f --- /dev/null +++ b/c/cert/src/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.md @@ -0,0 +1,120 @@ +# MSC39-C: Do not call va_arg() on a va_list that has an indeterminate value + +This query implements the CERT-C rule MSC39-C: + +> Do not call va_arg() on a va_list that has an indeterminate value + + +## Description + +Variadic functions access their variable arguments by using `va_start()` to initialize an object of type `va_list`, iteratively invoking the `va_arg()` macro, and finally calling `va_end()`. The `va_list` may be passed as an argument to another function, but calling `va_arg()` within that function causes the `va_list` to have an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) in the calling function. As a result, attempting to read variable arguments without reinitializing the `va_list` can have [unexpected behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior). According to the C Standard, 7.16, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], + +> If access to the varying arguments is desired, the called function shall declare an object (generally referred to as `ap` in this subclause) having type `va_list`. The object `ap` may be passed as an argument to another function; if that function invokes the `va_arg` macro with parameter `ap`, the value of `ap` in the calling function is indeterminate and shall be passed to the `va_end` macro prior to any further reference to `ap`.253253) It is permitted to create a pointer to a `va_list` and pass that pointer to another function, in which case the original function may take further use of the original list after the other function returns. + + +## Noncompliant Code Example + +This noncompliant code example attempts to check that none of its variable arguments are zero by passing a `va_list` to helper function `contains_zero()`. After the call to `contains_zero()`, the value of `ap` is [indeterminate](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue). + +```cpp +#include +#include + +int contains_zero(size_t count, va_list ap) { + for (size_t i = 1; i < count; ++i) { + if (va_arg(ap, double) == 0.0) { + return 1; + } + } + return 0; +} + +int print_reciprocals(size_t count, ...) { + va_list ap; + va_start(ap, count); + + if (contains_zero(count, ap)) { + va_end(ap); + return 1; + } + + for (size_t i = 0; i < count; ++i) { + printf("%f ", 1.0 / va_arg(ap, double)); + } + + va_end(ap); + return 0; +} + +``` + +## Compliant Solution + +The compliant solution modifies `contains_zero()` to take a pointer to a `va_list`. It then uses the `va_copy` macro to make a copy of the list, traverses the copy, and cleans it up. Consequently, the `print_reciprocals()` function is free to traverse the original `va_list`. + +```cpp +#include +#include + +int contains_zero(size_t count, va_list *ap) { + va_list ap1; + va_copy(ap1, *ap); + for (size_t i = 1; i < count; ++i) { + if (va_arg(ap1, double) == 0.0) { + return 1; + } + } + va_end(ap1); + return 0; +} + +int print_reciprocals(size_t count, ...) { + int status; + va_list ap; + va_start(ap, count); + + if (contains_zero(count, &ap)) { + printf("0 in arguments!\n"); + status = 1; + } else { + for (size_t i = 0; i < count; i++) { + printf("%f ", 1.0 / va_arg(ap, double)); + } + printf("\n"); + status = 0; + } + + va_end(ap); + return status; +} + +``` + +## Risk Assessment + +Reading variable arguments using a `va_list` that has an [indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-indeterminatevalue) can have unexpected results. + +
Rule Severity Likelihood Remediation Cost Priority Level
MSC39-C Low Unlikely Low P3 L3
+ + +## Automated Detection + +
Tool Version Checker Description
CodeSonar 7.2p0 BADMACRO.STDARG_H Use of <stdarg.h> Feature
Helix QAC 2022.4 C3497 C++3146, C++3147, C++3148, C++3149, C++3167
Klocwork 2022.4 VA.LIST.INDETERMINATE
Parasoft C/C++test 2022.2 CERT_C-MSC39-a Use macros for variable arguments correctly
Polyspace Bug Finder R2022b CERT C: Rule MSC39-C Checks for: Invalid va_list argumentnvalid va_list argument, too many va_arg calls for current argument listoo many va_arg calls for current argument list. Rule partially covered.
PRQA QA-C 9.7 3497 Enforced by QAC
TrustInSoft Analyzer 1.38 variadic 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+MSC39-C). + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.16, "Variable Arguments <stdarg.h> "
+ + +## Implementation notes + +None + +## References + +* CERT-C: [MSC39-C: Do not call va_arg() on a va_list that has an indeterminate value](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql b/c/cert/src/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql new file mode 100644 index 0000000000..56613c1943 --- /dev/null +++ b/c/cert/src/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql @@ -0,0 +1,94 @@ +/** + * @id c/cert/do-not-call-va-arg-on-a-va-list-that-has-an-indeterminate-value + * @name MSC39-C: Do not call va_arg() on a va_list that has an indeterminate value + * @description Do not call va_arg() on a va_list that has an indeterminate value. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/msc39-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.cpp.Macro +import semmle.code.cpp.dataflow.DataFlow + +abstract class VaAccess extends Expr { } + +/** + * The argument of a call to `va_arg` + */ +class VaArgArg extends VaAccess { + VaArgArg() { this = any(MacroInvocation m | m.getMacroName() = ["va_arg"]).getExpr().getChild(0) } +} + +/** + * The argument of a call to `va_end` + */ +class VaEndArg extends VaAccess { + VaEndArg() { this = any(MacroInvocation m | m.getMacroName() = ["va_end"]).getExpr().getChild(0) } +} + +/** + * Dataflow configuration for flow from a library function + * to a call of function `asctime` + */ +module VaArgConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { + src.asUninitialized() = + any(VariableDeclarationEntry m | m.getType().hasName("va_list")).getVariable() + } + + predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof VaAccess } +} + +module VaArgFlow = DataFlow::Global; + +/** + * Controlflow nodes preceeding a call to `va_arg` + */ +ControlFlowNode preceedsFC(VaAccess va_arg) { + result = va_arg + or + exists(ControlFlowNode mid | + result = mid.getAPredecessor() and + mid = preceedsFC(va_arg) and + // stop recursion on va_end on the same object + not result = + any(MacroInvocation m | + m.getMacroName() = ["va_start"] and + m.getExpr().getChild(0).(VariableAccess).getTarget() = va_arg.(VariableAccess).getTarget() + ).getExpr() + ) +} + +predicate sameSource(VaAccess e1, VaAccess e2) { + exists(DataFlow::Node source | + VaArgFlow::flow(source, DataFlow::exprNode(e1)) and + VaArgFlow::flow(source, DataFlow::exprNode(e2)) + ) +} + +/** + * Extracted to avoid poor magic join ordering on the `isExcluded` predicate. + */ +predicate query(VaAccess va_acc, VaArgArg va_arg, FunctionCall fc) { + sameSource(va_acc, va_arg) and + fc = preceedsFC(va_acc) and + fc.getTarget().calls*(va_arg.getEnclosingFunction()) +} + +from VaAccess va_acc, VaArgArg va_arg, FunctionCall fc +where + not isExcluded(va_acc, + Contracts7Package::doNotCallVaArgOnAVaListThatHasAnIndeterminateValueQuery()) and + query(va_acc, va_arg, fc) +select va_acc, "The value of " + va_acc.toString() + " is indeterminate after the $@.", fc, + fc.toString() diff --git a/c/cert/src/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.md b/c/cert/src/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.md new file mode 100644 index 0000000000..f767c91baf --- /dev/null +++ b/c/cert/src/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.md @@ -0,0 +1,210 @@ +# MSC40-C: Do not violate inline linkage constraints + +This query implements the CERT-C rule MSC40-C: + +> Do not violate constraints + + +## Description + +According to the C Standard, 3.8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], a constraint is a "restriction, either syntactic or semantic, by which the exposition of language elements is to be interpreted." Despite the similarity of the terms, a runtime constraint is not a kind of constraint. + +Violating any *shall* statement within a constraint clause in the C Standard requires an [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) to issue a diagnostic message, the C Standard, 5.1.1.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\] states + +> A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances. + + +The C Standard further explains in a footnote + +> The intent is that an implementation should identify the nature of, and where possible localize, each violation. Of course, an implementation is free to produce any number of diagnostics as long as a valid program is still correctly translated. It may also successfully translate an invalid program. + + +Any constraint violation is a violation of this rule because it can result in an invalid program. + +## Noncompliant Code Example (Inline, Internal Linkage) + +The C Standard, 6.7.4, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], states + +> An inline definition of a function with external linkage shall not contain a definition of a modifiable object with static or thread storage duration, and shall not contain a reference to an identifier with internal linkage. + + +The motivation behind this constraint lies in the semantics of inline definitions. Paragraph 7 of subclause 6.7.4 reads, in part: + +> An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition. + + +That is, if a function has an external and inline definition, implementations are free to choose which definition to invoke (two distinct invocations of the function may call different definitions, one the external definition, the other the inline definition). Therefore, issues can arise when these definitions reference internally linked objects or mutable objects with static or thread storage duration. + +This noncompliant code example refers to a static variable with file scope and internal linkage from within an external inline function: + +```cpp +static int I = 12; +extern inline void func(int a) { + int b = a * I; + /* ... */ +} + +``` + +## Compliant Solution (Inline, Internal Linkage) + +This compliant solution omits the `static` qualifier; consequently, the variable `I` has external linkage by default: + +```cpp +int I = 12; +extern inline void func(int a) { + int b = a * I; + /* ... */ +} + +``` + +## Noncompliant Code Example (inline, Modifiable Static) + +This noncompliant code example defines a modifiable `static` variable within an `extern inline` function. + +```cpp +extern inline void func(void) { + static int I = 12; + /* Perform calculations which may modify I */ +} + +``` + +## Compliant Solution (Inline, Modifiable Static) + +This compliant solution removes the `static` keyword from the local variable definition. If the modifications to `I` must be retained between invocations of `func()`, it must be declared at file scope so that it will be defined with external linkage. + +```cpp +extern inline void func(void) { + int I = 12; + /* Perform calculations which may modify I */ +} +``` + +## Noncompliant Code Example (Inline, Modifiable static) + +This noncompliant code example includes two translation units: `file1.c` and `file2.c`. The first file, `file1.c`, defines a pseudorandom number generation function: + +```cpp +/* file1.c */ + +/* Externally linked definition of the function get_random() */ +extern unsigned int get_random(void) { + /* Initialize the seeds */ + static unsigned int m_z = 0xdeadbeef; + static unsigned int m_w = 0xbaddecaf; + + /* Compute the next pseudorandom value and update the seeds */ + m_z = 36969 * (m_z & 65535) + (m_z >> 16); + m_w = 18000 * (m_w & 65535) + (m_w >> 16); + return (m_z << 16) + m_w; +} + +``` +The left-shift operation in the last line may wrap, but this is permitted by exception INT30-C-EX3 to rule [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 second file, `file2.c`, defines an `inline` version of this function that references mutable `static` objects—namely, objects that maintain the state of the pseudorandom number generator. Separate invocations of the `get_random()` function can call different definitions, each operating on separate static objects, resulting in a faulty pseudorandom number generator. + +```cpp +/* file2.c */ + +/* Inline definition of get_random function */ +inline unsigned int get_random(void) { + /* + * Initialize the seeds + * Constraint violation: static duration storage referenced + * in non-static inline definition + */ + static unsigned int m_z = 0xdeadbeef; + static unsigned int m_w = 0xbaddecaf; + + /* Compute the next pseudorandom value and update the seeds */ + m_z = 36969 * (m_z & 65535) + (m_z >> 16); + m_w = 18000 * (m_w & 65535) + (m_w >> 16); + return (m_z << 16) + m_w; +} + +int main(void) { + unsigned int rand_no; + for (int ii = 0; ii < 100; ii++) { + /* + * Get a pseudorandom number. Implementation defined whether the + * inline definition in this file or the external definition + * in file2.c is called. + */ + rand_no = get_random(); + /* Use rand_no... */ + } + + /* ... */ + + /* + * Get another pseudorandom number. Behavior is + * implementation defined. + */ + rand_no = get_random(); + /* Use rand_no... */ + return 0; +} + +``` + +## Compliant Solution (Inline, Modifiable static) + +This compliant solution adds the `static` modifier to the `inline` function definition in `file2.c`, giving it internal linkage. All references to `get_random()` in `file.2.c` will now reference the internally linked definition. The first file, which was not changed, is not shown here. + +```cpp +/* file2.c */ + +/* Static inline definition of get_random function */ +static inline unsigned int get_random(void) { + /* + * Initialize the seeds. + * No more constraint violation; the inline function is now + * internally linked. + */ + static unsigned int m_z = 0xdeadbeef; + static unsigned int m_w = 0xbaddecaf; + + /* Compute the next pseudorandom value and update the seeds */ + m_z = 36969 * (m_z & 65535) + (m_z >> 16); + m_w = 18000 * (m_w & 65535) + (m_w >> 16); + return (m_z << 16) + m_w; +} + +int main(void) { + /* Generate pseudorandom numbers using get_random()... */ + return 0; +} + +``` + +## Risk Assessment + +Constraint violations are a broad category of error that can result in unexpected control flow and corrupted data. + +
Rule Severity Likelihood Remediation Cost Priority Level
MSC40-C Low Unlikely Medium P2 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 23.04 alignas-extended assignment-to-non-modifiable-lvalue cast-pointer-void-arithmetic-implicit element-type-incomplete function-pointer-integer-cast-implicit function-return-type inappropriate-pointer-cast-implicit incompatible-function-pointer-conversion incompatible-object-pointer-conversion initializer-excess invalid-array-size non-constant-static-assert parameter-match-type pointer-integral-cast-implicit pointer-qualifier-cast-const-implicit pointer-qualifier-cast-volatile-implicit redeclaration return-empty return-non-empty static-assert type-compatibility type-compatibility-link type-specifier undeclared-parameter unnamed-parameter Partially checked
Helix QAC 2023.4 C0232, C0233, C0244, C0268, C0321, C0322, C0338, C0422, C0423, C0426, C0427, C0429, C0430, C0431, C0432, C0435, C0436, C0437, C0446, C0447, C0448, C0449, C0451, C0452, C0453, C0454, C0456, C0457, C0458, C0460, C0461, C0462, C0463, C0466, C0467, C0468, C0469, C0476, C0477, C0478, C0481, C0482, C0483, C0484, C0485, C0486, C0487, C0493, C0494, C0495, C0496, C0497, C0513, C0514, C0515, C0536, C0537, C0540, C0541, C0542, C0546, C0547, C0550, C0554, C0555, C0556, C0557, C0558, C0559, C0560, C0561, C0562, C0563, C0564, C0565, C0580, C0588, C0589, C0590, C0591, C0605, C0616, C0619, C0620, C0621, C0622, C0627, C0628, C0629, C0631, C0638, C0640, C0641, C0642, C0643, C0644, C0645, C0646, C0649, C0650, C0651, C0653, C0655, C0656, C0657, C0659, C0664, C0665, C0669, C0671, C0673, C0674, C0675, C0677, C0682, C0683, C0684, C0685, C0690, C0698, C0699, C0708, C0709, C0736, C0737, C0738, C0746, C0747, C0755, C0756, C0757, C0758, C0766, C0767, C0768, C0774, C0775, C0801, C0802, C0803, C0804, C0811, C0821, C0834, C0835, C0844, C0845, C0851, C0852, C0866, C0873, C0877, C0940, C0941, C0943, C0944, C1023, C1024, C1025, C1033, C1047, C1048, C1050, C1061, C1062, C3236, C3237, C3238, C3244 C++4122
Klocwork 2023.4 MISRA.FUNC.STATIC.REDECL
LDRA tool suite 9.7.1 21 S, 145 S, 323 S, 345 S, 387 S, 404 S, 481 S, 580 S, 612 S, 615 S, 646 S
Parasoft C/C++test 2023.1 CERT_C-MSC40-a An inline definition of a function with external linkage shall not contain definitions and uses of static objects
Polyspace Bug Finder CERT C: Rule MSC40-C Checks for inline constraint not respected (rule partially covered)
RuleChecker 23.04 alignas-extended assignment-to-non-modifiable-lvalue cast-pointer-void-arithmetic-implicit element-type-incomplete function-pointer-integer-cast-implicit function-return-type inappropriate-pointer-cast-implicit incompatible-function-pointer-conversion incompatible-object-pointer-conversion initializer-excess invalid-array-size non-constant-static-assert parameter-match-type pointer-integral-cast-implicit pointer-qualifier-cast-const-implicit pointer-qualifier-cast-volatile-implicit redeclaration return-empty return-non-empty static-assert type-compatibility type-compatibility-link type-specifier undeclared-parameter unnamed-parameter 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+MSC40-C). + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 4, "Conformance" 5.1.1.3, "Diagnostics" 6.7.4, "Function Specifiers"
+ + +## Implementation notes + +This query only considers the constraints related to inline extern functions. + +## References + +* CERT-C: [MSC40-C: Do not violate constraints](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.ql b/c/cert/src/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.ql new file mode 100644 index 0000000000..746cea2e9f --- /dev/null +++ b/c/cert/src/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.ql @@ -0,0 +1,64 @@ +/** + * @id c/cert/do-not-violate-in-line-linkage-constraints + * @name MSC40-C: Do not violate inline linkage constraints + * @description Inlined external functions are prohibited by the language standard from defining + * modifiable static or thread storage objects, or referencing identifiers with + * internal linkage. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/msc40-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 codingstandards.cpp.Linkage + +/* + * This is C specific, because in C++ all extern function definitions must be identical. + * Only in C is it permitted for an extern function to be defined in multiple translation + * units with different implementations, when using the inline keyword. + */ + +from Element accessOrDecl, Variable v, Function f, string message +where + not isExcluded(f, ContractsPackage::doNotViolateInLineLinkageConstraintsQuery()) and + f.isInline() and + hasExternalLinkage(f) and + // Pre-emptively exclude compiler generated functions + not f.isCompilerGenerated() and + // This rule does not apply to C++, but exclude C++ specific cases anyway + not f instanceof MemberFunction and + not f.isFromUninstantiatedTemplate(_) and + ( + // There exists a modifiable local variable which is static or thread local + exists(LocalVariable lsv, string storageModifier | + lsv.isStatic() and storageModifier = "Static" + or + lsv.isThreadLocal() and storageModifier = "Thread-local" + | + lsv.getFunction() = f and + not lsv.isConst() and + accessOrDecl = lsv and + message = storageModifier + " local variable $@ declared" and + v = lsv + ) + or + // References an identifier with internal linkage + exists(GlobalOrNamespaceVariable gv | + accessOrDecl = v.getAnAccess() and + accessOrDecl.(VariableAccess).getEnclosingFunction() = f and + hasInternalLinkage(v) and + message = "Identifier $@ with internal linkage referenced" and + v = gv + ) + ) +select accessOrDecl, message + " in the extern inlined function $@.", v, v.getName(), f, + f.getQualifiedName() diff --git a/c/cert/src/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.md b/c/cert/src/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.md new file mode 100644 index 0000000000..9a83bee144 --- /dev/null +++ b/c/cert/src/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.md @@ -0,0 +1,209 @@ +# PRE31-C: Avoid side effects in arguments to unsafe macros + +This query implements the CERT-C rule PRE31-C: + +> Avoid side effects in arguments to unsafe macros + + +## Description + +An [unsafe function-like macro](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unsafefunction-likemacro) is one whose expansion results in evaluating one of its parameters more than once or not at all. Never invoke an unsafe macro with arguments containing an assignment, increment, decrement, volatile access, input/output, or other expressions with side effects (including function calls, which may cause side effects). + +The documentation for unsafe macros should warn against invoking them with arguments with side effects, but the responsibility is on the programmer using the macro. Because of the risks associated with their use, it is recommended that the creation of unsafe function-like macros be avoided. (See [PRE00-C. Prefer inline or static functions to function-like macros](https://wiki.sei.cmu.edu/confluence/display/c/PRE00-C.+Prefer+inline+or+static+functions+to+function-like+macros).) + +This rule is similar to [EXP44-C. Do not rely on side effects in operands to sizeof, _Alignof, or _Generic](https://wiki.sei.cmu.edu/confluence/display/c/EXP44-C.+Do+not+rely+on+side+effects+in+operands+to+sizeof%2C+_Alignof%2C+or+_Generic). + +## Noncompliant Code Example + +One problem with unsafe macros is [side effects](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-sideeffect) on macro arguments, as shown by this noncompliant code example: + +```cpp +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +void func(int n) { + /* Validate that n is within the desired range */ + int m = ABS(++n); + + /* ... */ +} +``` +The invocation of the `ABS()` macro in this example expands to + +```cpp +m = (((++n) < 0) ? -(++n) : (++n)); + +``` +The resulting code is well defined but causes `n` to be incremented twice rather than once. + +## Compliant Solution + +In this compliant solution, the increment operation `++n` is performed before the call to the unsafe macro. + +```cpp +#define ABS(x) (((x) < 0) ? -(x) : (x)) /* UNSAFE */ + +void func(int n) { + /* Validate that n is within the desired range */ + ++n; + int m = ABS(n); + + /* ... */ +} +``` +Note the comment warning programmers that the macro is unsafe. The macro can also be renamed `ABS_UNSAFE()` to make it clear that the macro is unsafe. This compliant solution, like all the compliant solutions for this rule, has undefined behavior if the argument to `ABS()` is equal to the minimum (most negative) value for the signed integer type. (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) for more information.) + +## Compliant Solution + +This compliant solution follows the guidance of [PRE00-C. Prefer inline or static functions to function-like macros](https://wiki.sei.cmu.edu/confluence/display/c/PRE00-C.+Prefer+inline+or+static+functions+to+function-like+macros) by defining an inline function `iabs()` to replace the `ABS()` macro. Unlike the `ABS()` macro, which operates on operands of any type, the `iabs()` function will truncate arguments of types wider than `int` whose value is not in range of the latter type. + +```cpp +#include +#include + +static inline int iabs(int x) { + return (((x) < 0) ? -(x) : (x)); +} + +void func(int n) { + /* Validate that n is within the desired range */ + +int m = iabs(++n); + + /* ... */ +} +``` + +## Compliant Solution + +A more flexible compliant solution is to declare the `ABS()` macro using a `_Generic` selection. To support all arithmetic data types, this solution also makes use of inline functions to compute integer absolute values. (See [PRE00-C. Prefer inline or static functions to function-like macros](https://wiki.sei.cmu.edu/confluence/display/c/PRE00-C.+Prefer+inline+or+static+functions+to+function-like+macros) and [PRE12-C. Do not define unsafe macros](https://wiki.sei.cmu.edu/confluence/display/c/PRE12-C.+Do+not+define+unsafe+macros).) + +According to the C Standard, 6.5.1.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\]: + +> The controlling expression of a generic selection is not evaluated. If a generic selection has a generic association with a type name that is compatible with the type of the controlling expression, then the result expression of the generic selection is the expression in that generic association. Otherwise, the result expression of the generic selection is the expression in the `default` generic association. None of the expressions from any other generic association of the generic selection is evaluated. + + +Because the expression is not evaluated as part of the generic selection, the use of a macro in this solution is guaranteed to evaluate the macro parameter `v` only once. + +```cpp +#include +#include + +static inline long long llabs(long long v) { + return v < 0 ? -v : v; +} +static inline long labs(long v) { + return v < 0 ? -v : v; +} +static inline int iabs(int v) { + return v < 0 ? -v : v; +} +static inline int sabs(short v) { + return v < 0 ? -v : v; +} +static inline int scabs(signed char v) { + return v < 0 ? -v : v; +} + +#define ABS(v) _Generic(v, signed char : scabs, \ + short : sabs, \ + int : iabs, \ + long : labs, \ + long long : llabs, \ + float : fabsf, \ + double : fabs, \ + long double : fabsl, \ + double complex : cabs, \ + float complex : cabsf, \ + long double complex : cabsl)(v) + +void func(int n) { + /* Validate that n is within the desired range */ + int m = ABS(++n); + /* ... */ +} +``` +Generic selections were introduced in C11 and are not available in C99 and earlier editions of the C Standard. + +## Compliant Solution (GCC) + +GCC's [__typeof](http://gcc.gnu.org/onlinedocs/gcc/Typeof.html) extension makes it possible to declare and assign the value of the macro operand to a temporary of the same type and perform the computation on the temporary, consequently guaranteeing that the operand will be evaluated exactly once. Another GCC extension, known as *statement expression[](http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html)*, makes it possible for the block statement to appear where an expression is expected: + +```cpp +#define ABS(x) __extension__ ({ __typeof (x) tmp = x; \ + tmp < 0 ? -tmp : tmp; }) +``` +Note that relying on such extensions makes code nonportable and violates [MSC14-C. Do not introduce unnecessary platform dependencies](https://wiki.sei.cmu.edu/confluence/display/c/MSC14-C.+Do+not+introduce+unnecessary+platform+dependencies). + +## Noncompliant Code Example (assert()) + +The `assert()` macro is a convenient mechanism for incorporating diagnostic tests in code. (See [MSC11-C. Incorporate diagnostic tests using assertions](https://wiki.sei.cmu.edu/confluence/display/c/MSC11-C.+Incorporate+diagnostic+tests+using+assertions).) Expressions used as arguments to the standard `assert()` macro should not have side effects. The behavior of the `assert()` macro depends on the definition of the object-like macro `NDEBUG`. If the macro `NDEBUG` is undefined, the `assert()` macro is defined to evaluate its expression argument and, if the result of the expression compares equal to 0, call the `abort()` function. If `NDEBUG` is defined, `assert` is defined to expand to `((void)0)`. Consequently, the expression in the assertion is not evaluated, and no side effects it may have had otherwise take place in non-debugging executions of the code. + +This noncompliant code example includes an `assert()` macro containing an expression (`index++`) that has a side effect: + +```cpp +#include +#include + +void process(size_t index) { + assert(index++ > 0); /* Side effect */ + /* ... */ +} + +``` + +## Compliant Solution (assert()) + +This compliant solution avoids the possibility of side effects in assertions by moving the expression containing the side effect outside of the `assert()` macro. + +```cpp +#include +#include + +void process(size_t index) { + assert(index > 0); /* No side effect */ + ++index; + /* ... */ +} +``` + +## Exceptions + +**PRE31-C-EX1:** An exception can be made for invoking an [unsafe macro](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unsafefunction-likemacro) with a function call argument provided that the function has no [side effects](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-sideeffect). However, it is easy to forget about obscure side effects that a function might have, especially library functions for which source code is not available; even changing `errno` is a side effect. Unless the function is user-written and does nothing but perform a computation and return its result without calling any other functions, it is likely that many developers will forget about some side effect. Consequently, this exception must be used with great care. + +## Risk Assessment + +Invoking an unsafe macro with an argument that has side effects may cause those side effects to occur more than once. This practice 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
PRE31-C Low Unlikely Low P3 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 expanded-side-effect-multiplied expanded-side-effect-not-evaluated side-effect-not-expanded Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-PRE31 Fully implemented
CodeSonar 7.2p0 LANG.PREPROC.FUNCMACRO LANG.STRUCT.SE.DEC LANG.STRUCT.SE.INC Function-Like Macro Side Effects in Expression with Decrement Side Effects in Expression with Increment
Coverity 2017.07 ASSERT_SIDE_EFFECTS Partially implemented Can detect the specific instance where assertion contains an operation/function call that may have a side effect
ECLAIR 1.2 CC2.EXP31CC2.PRE31 Fully implemented
Helix QAC 2022.4 C3462, C3463, C3464, C3465, C3466, C3467 C++3225, C++3226, C++3227, C++3228, C++3229
Klocwork 2022.4 PORTING.VAR.EFFECTS
LDRA tool suite 9.7.1 9 S, 562 S, 572 S, 35 D, 1 Q Fully implemented
Parasoft C/C++test 2022.2 CERT_C-PRE31-b CERT_C-PRE31-c CERT_C-PRE31-d Assertions should not contain assignments, increment, or decrement operators Assertions should not contain function calls nor function-like macro calls Avoid side effects in arguments to unsafe macros
PC-lint Plus 1.4 666, 2666 Fully supported
Polyspace Bug Finder R2023a CERT C: Rule PRE31-C Checks for side effect in arguments to unsafe macro (rule partially covered)
PRQA QA-C 9.7 3462, 3463, 3464, 3465, 3466, 3467 Fully implemented
PRQA QA-C++ 4.4 3225, 3226, 3227, 3228, 3229
RuleChecker 22.04 expanded-side-effect-multiplied expanded-side-effect-not-evaluated side-effect-not-expanded 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+PRE31-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) + +[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 + +
\[ Dewhurst 2002 \] Gotcha \#28, "Side Effects in Assertions"
\[ ISO/IEC 9899:2011 \] Subclause 6.5.1.1, "Generic Selection"
\[ Plum 1985 \] Rule 1-11
+ + +## Implementation notes + +This implementation only considers ++ and function call side effects. Due to the textual nature of macro expansion it is not always possible to determine accurately whether a side-effect was produced by a particular argument, and this may cause both false positives and false negatives. The query does not consider the case where a macro argument including a side-effect is never evaluated. + +## References + +* CERT-C: [PRE31-C: Avoid side effects in arguments to unsafe macros](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.ql b/c/cert/src/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.ql new file mode 100644 index 0000000000..322048f6de --- /dev/null +++ b/c/cert/src/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.ql @@ -0,0 +1,143 @@ +/** + * @id c/cert/side-effects-in-arguments-to-unsafe-macros + * @name PRE31-C: Avoid side effects in arguments to unsafe macros + * @description Macro arguments can be expanded multiple times which can cause side-effects to be + * evaluated multiple times leading to unexpected program behavior. + * @kind problem + * @precision low + * @problem.severity error + * @tags external/cert/id/pre31-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.cpp.Macro +import codingstandards.cpp.SideEffect +import codingstandards.cpp.sideeffect.DefaultEffects +import codingstandards.cpp.sideeffect.Customizations +import semmle.code.cpp.valuenumbering.HashCons + +/** + * Add side-effecting functions to the default set of side-effects. + */ +class FunctionCallEffect extends GlobalSideEffect::Range { + FunctionCallEffect() { + exists(Function f | + // Capture function calls as side-effects + f = this.(FunctionCall).getTarget() and + // Excluding __builtin_expect, which is not a side-effecting function + not f.(BuiltInFunction).getName() = "__builtin_expect" and + // Excluding common math functions + not exists(string name | + name = + [ + "acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "exp", "fabs", "floor", "fmod", + "frexp", "ldexp", "log", "log10", "modf", "pow", "sin", "sinh", "sqrt", "tan", "tanh", + "cbrt", "erf", "erfc", "exp2", "expm1", "fdim", "fma", "fmax", "fmin", "hypot", "ilogb", + "lgamma", "llrint", "llround", "log1p", "log2", "logb", "lrint", "lround", "nan", + "nearbyint", "nextafter", "nexttoward", "remainder", "remquo", "rint", "round", + "scalbln", "scalbn", "tgamma", "trunc" + ] and + f.hasGlobalOrStdName([name, name + "f", name + "l"]) + ) + ) + } +} + +/** + * Add crement operations to the default set of side-effects. + */ +class CrementEffect extends LocalSideEffect::Range { + CrementEffect() { this instanceof CrementOperation } +} + +/** + * A macro that is considered potentially "unsafe" because one or more arguments are expanded + * multiple times. + */ +class UnsafeMacro extends FunctionLikeMacro { + int unsafeArgumentIndex; + + UnsafeMacro() { + exists(this.getAParameterUse(unsafeArgumentIndex)) and + // Only consider arguments that are expanded multiple times, and do not consider "stringified" arguments + count(int indexInBody | + indexInBody = this.getAParameterUse(unsafeArgumentIndex) and + not this.getBody().charAt(indexInBody) = "#" + ) > 1 + } + + int getAnUnsafeArgumentIndex() { result = unsafeArgumentIndex } +} + +/** + * An invocation of a potentially unsafe macro. + */ +class UnsafeMacroInvocation extends MacroInvocation { + UnsafeMacroInvocation() { + this.getMacro() instanceof UnsafeMacro and not exists(this.getParentInvocation()) + } + + /** + * Gets a side-effect for a potentially unsafe argument to the macro. + */ + SideEffect getSideEffectForUnsafeArg(int index) { + index = this.getMacro().(UnsafeMacro).getAnUnsafeArgumentIndex() and + exists(Expr e, string arg | + e = this.getAnExpandedElement() and + result = getASideEffect(e) and + // Unfortunately, there's no semantic way to check whether a particular expression or + // side-effect generated by a macro came from a particular macro argument. The only + // information we get is the string of the expanded argument. We therefore do some basic + // string matching to check whether it looks like this side-effect comes from the given + // argument + arg = this.getExpandedArgument(index) and + ( + // If this is a crement effect, then check that the text of the macro argument includes -- or ++ + result instanceof CrementEffect and + exists(arg.indexOf(result.(CrementOperation).getOperator())) + or + // If this is a functional call effect, then check that the text of the macro argument includes a call to that function + result instanceof FunctionCallEffect and + exists(arg.indexOf(result.(FunctionCall).getTarget().getName() + "(")) + ) + ) + } +} + +from + UnsafeMacroInvocation unsafeMacroInvocation, SideEffect sideEffect, int i, string sideEffectDesc +where + not isExcluded(sideEffect, SideEffects4Package::sideEffectsInArgumentsToUnsafeMacrosQuery()) and + sideEffect = unsafeMacroInvocation.getSideEffectForUnsafeArg(i) and + ( + sideEffect instanceof CrementEffect and + // Do we observe the same side-effect multiple times? + count(SideEffect equivalentSideEffect | + equivalentSideEffect = unsafeMacroInvocation.getSideEffectForUnsafeArg(i) and + hashCons(equivalentSideEffect.(CrementOperation).getOperand()) = + hashCons(sideEffect.(CrementOperation).getOperand()) + ) > 1 and + sideEffectDesc = "the use of the " + sideEffect.(CrementOperation).getOperator() + " operator" + or + sideEffect instanceof FunctionCallEffect and + // Do we observe the same side-effect multiple times? + count(SideEffect equivalentSideEffect | + equivalentSideEffect = unsafeMacroInvocation.getSideEffectForUnsafeArg(i) and + equivalentSideEffect.(FunctionCall).getTarget() = sideEffect.(FunctionCall).getTarget() + ) > 1 and + sideEffectDesc = + "a call to the function '" + sideEffect.(FunctionCall).getTarget().getName() + "'" + ) +select sideEffect, + "Argument " + unsafeMacroInvocation.getUnexpandedArgument(i) + " to unsafe macro '" + + unsafeMacroInvocation.getMacroName() + "' is expanded to '" + + unsafeMacroInvocation.getExpandedArgument(i) + "' multiple times and includes " + sideEffectDesc + + " as a side-effect." diff --git a/c/cert/src/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.ql b/c/cert/src/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.ql index c323c2d31f..0a777dc25d 100644 --- a/c/cert/src/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.ql +++ b/c/cert/src/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.ql @@ -10,6 +10,11 @@ * @tags external/cert/id/pre32-c * correctness * 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 */ @@ -32,11 +37,38 @@ predicate isFunctionSuccessorLocation(ControlFlowNode node, File f, int endline) PreprocessorDirective isLocatedInAFunctionInvocation(FunctionCall c) { exists(PreprocessorDirective p, File f, int startCall, int endCall | isFunctionInvocationLocation(c, f, startCall, endCall) and - exists(int startLine, int endLine | isPreprocDirectiveLine(p, f, startLine, endLine) | - startCall < startLine and - startCall < endLine and - endLine <= endCall and - endLine <= endCall + exists(Expr arg, int preprocStartLine, int preprocEndLine | + c.getAnArgument() = arg and + isPreprocDirectiveLine(p, f, preprocStartLine, preprocEndLine) and + // function call begins before preprocessor directive + startCall < preprocStartLine and + ( + // argument's location is after the preprocessor directive + arg.getLocation().getStartLine() > preprocStartLine + or + // arg's location is before an endif token that is part of a + // preprocessor directive defined before the argument. + // E.g. + // memcpy(dest, src, + // #ifdef SOMEMACRO + // 12 + // #else + // 24 // 'arg' exists here + // #endif // endif after 'arg', but part of a preproc. branch before 'arg' + // ); + p instanceof PreprocessorEndif and + // exists a preprocessor branch of which this is the endif + // and that preprocessor directive exists before + // the argument and after the function call begins. + exists(PreprocessorBranchDirective another | + another.getEndIf() = p and + another.getLocation().getFile() = f and + startCall < another.getLocation().getStartLine() and + arg.getLocation().getStartLine() > another.getLocation().getStartLine() + ) + ) and + // function call ends after preprocessor directive + endCall > preprocEndLine ) and result = p ) diff --git a/c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.md b/c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.md new file mode 100644 index 0000000000..3f0eb24400 --- /dev/null +++ b/c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.md @@ -0,0 +1,377 @@ +# SIG30-C: Call only asynchronous-safe functions within signal handlers + +This query implements the CERT-C rule SIG30-C: + +> Call only asynchronous-safe functions within signal handlers + + +## Description + +Call only [asynchronous-safe functions](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safefunction) within signal handlers. For [strictly conforming](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-strictlyconforming) programs, only the C standard library functions `abort()`, `_Exit()`, `quick_exit()`, and `signal()` can be safely called from within a signal handler. + +The C Standard, 7.14.1.1, paragraph 5 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states that if the signal occurs other than as the result of calling the `abort()` or `raise()` function, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) if + +> ...the signal handler calls any function in the standard library other than the `abort` function, the `_Exit` function, the `quick_exit` function, or the `signal` function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. + + +Implementations may define a list of additional asynchronous-safe functions. These functions can also be called within a signal handler. This restriction applies to library functions as well as application-defined functions. + +According to the C Rationale, 7.14.1.1 \[[C99 Rationale 2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-C992003)\], + +> When a signal occurs, the normal flow of control of a program is interrupted. If a signal occurs that is being trapped by a signal handler, that handler is invoked. When it is finished, execution continues at the point at which the signal occurred. This arrangement can cause problems if the signal handler invokes a library function that was being executed at the time of the signal. + + +In general, it is not safe to invoke I/O functions from within signal handlers. Programmers should ensure a function is included in the list of an implementation's asynchronous-safe functions for all implementations the code will run on before using them in signal handlers. + +## Noncompliant Code Example + +In this noncompliant example, the C standard library functions `fputs()` and `free()` are called from the signal handler via the function `log_message()`. Neither function is [asynchronous-safe](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safefunction). + +```cpp +#include +#include +#include + +enum { MAXLINE = 1024 }; +char *info = NULL; + +void log_message(void) { + fputs(info, stderr); +} + +void handler(int signum) { + log_message(); + free(info); + info = NULL; +} + +int main(void) { + if (signal(SIGINT, handler) == SIG_ERR) { + /* Handle error */ + } + info = (char *)malloc(MAXLINE); + if (info == NULL) { + /* Handle Error */ + } + + while (1) { + /* Main loop program code */ + + log_message(); + + /* More program code */ + } + return 0; +} + +``` + +## Compliant Solution + +Signal handlers should be as concise as possible—ideally by unconditionally setting a flag and returning. This compliant solution sets a flag of type `volatile sig_atomic_t` and returns; the `log_message()` and `free()` functions are called directly from `main()`: + +```cpp +#include +#include +#include + +enum { MAXLINE = 1024 }; +volatile sig_atomic_t eflag = 0; +char *info = NULL; + +void log_message(void) { + fputs(info, stderr); +} + +void handler(int signum) { + eflag = 1; +} + +int main(void) { + if (signal(SIGINT, handler) == SIG_ERR) { + /* Handle error */ + } + info = (char *)malloc(MAXLINE); + if (info == NULL) { + /* Handle error */ + } + + while (!eflag) { + /* Main loop program code */ + + log_message(); + + /* More program code */ + } + + log_message(); + free(info); + info = NULL; + + return 0; +} + +``` + +## Noncompliant Code Example (longjmp()) + +Invoking the `longjmp()` function from within a signal handler can lead to [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) if it results in the invocation of any non-[asynchronous-safe](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safe) functions. Consequently, neither `longjmp()` nor the POSIX `siglongjmp()` functions should ever be called from within a signal handler. + +This noncompliant code example is similar to a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in an old version of Sendmail \[[VU \#834865](http://www.kb.cert.org/vuls/id/834865)\]. The intent is to execute code in a `main()` loop, which also logs some data. Upon receiving a `SIGINT`, the program transfers out of the loop, logs the error, and terminates. + +However, an attacker can [exploit](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-exploit) this noncompliant code example by generating a `SIGINT` just before the second `if` statement in `log_message()`. The result is that `longjmp()` transfers control back to `main()`, where `log_message()` is called again. However, the first `if` statement would not be executed this time (because `buf` is not set to `NULL` as a result of the interrupt), and the program would write to the invalid memory location referenced by `buf0`. + +```cpp +#include +#include +#include + +enum { MAXLINE = 1024 }; +static jmp_buf env; + +void handler(int signum) { + longjmp(env, 1); +} + +void log_message(char *info1, char *info2) { + static char *buf = NULL; + static size_t bufsize; + char buf0[MAXLINE]; + + if (buf == NULL) { + buf = buf0; + bufsize = sizeof(buf0); + } + + /* + * Try to fit a message into buf, else reallocate + * it on the heap and then log the message. + */ + + /* Program is vulnerable if SIGINT is raised here */ + + if (buf == buf0) { + buf = NULL; + } +} + +int main(void) { + if (signal(SIGINT, handler) == SIG_ERR) { + /* Handle error */ + } + char *info1; + char *info2; + + /* info1 and info2 are set by user input here */ + + if (setjmp(env) == 0) { + while (1) { + /* Main loop program code */ + log_message(info1, info2); + /* More program code */ + } + } else { + log_message(info1, info2); + } + + return 0; +} + +``` + +## Compliant Solution + +In this compliant solution, the call to `longjmp()` is removed; the signal handler sets an error flag instead: + +```cpp +#include +#include + +enum { MAXLINE = 1024 }; +volatile sig_atomic_t eflag = 0; + +void handler(int signum) { + eflag = 1; +} + +void log_message(char *info1, char *info2) { + static char *buf = NULL; + static size_t bufsize; + char buf0[MAXLINE]; + + if (buf == NULL) { + buf = buf0; + bufsize = sizeof(buf0); + } + + /* + * Try to fit a message into buf, else reallocate + * it on the heap and then log the message. + */ + if (buf == buf0) { + buf = NULL; + } +} + +int main(void) { + if (signal(SIGINT, handler) == SIG_ERR) { + /* Handle error */ + } + char *info1; + char *info2; + + /* info1 and info2 are set by user input here */ + + while (!eflag) { + /* Main loop program code */ + log_message(info1, info2); + /* More program code */ + } + + log_message(info1, info2); + + return 0; +} +``` + +## Noncompliant Code Example (raise()) + +In this noncompliant code example, the `int_handler()` function is used to carry out tasks specific to `SIGINT` and then raises `SIGTERM`. However, there is a nested call to the `raise()` function, which is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +```cpp +#include +#include + +void term_handler(int signum) { + /* SIGTERM handler */ +} + +void int_handler(int signum) { + /* SIGINT handler */ + if (raise(SIGTERM) != 0) { + /* Handle error */ + } +} + +int main(void) { + if (signal(SIGTERM, term_handler) == SIG_ERR) { + /* Handle error */ + } + if (signal(SIGINT, int_handler) == SIG_ERR) { + /* Handle error */ + } + + /* Program code */ + if (raise(SIGINT) != 0) { + /* Handle error */ + } + /* More code */ + + return EXIT_SUCCESS; +} + +``` + +## Compliant Solution + +In this compliant solution, `int_handler()` invokes `term_handler()` instead of raising `SIGTERM`: + +```cpp +#include +#include + +void term_handler(int signum) { + /* SIGTERM handler */ +} + +void int_handler(int signum) { + /* SIGINT handler */ + /* Pass control to the SIGTERM handler */ + term_handler(SIGTERM); +} + +int main(void) { + if (signal(SIGTERM, term_handler) == SIG_ERR) { + /* Handle error */ + } + if (signal(SIGINT, int_handler) == SIG_ERR) { + /* Handle error */ + } + + /* Program code */ + if (raise(SIGINT) != 0) { + /* Handle error */ + } + /* More code */ + + return EXIT_SUCCESS; +} + +``` + +## Implementation Details + +**POSIX** + +The following table from the POSIX standard \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\] defines a set of functions that are [asynchronous-signal-safe](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safefunction). Applications may invoke these functions, without restriction, from a signal handler. + +
_Exit() fexecve() posix_trace_event() sigprocmask()
_exit() fork() pselect() sigqueue()
abort() fstat() pthread_kill() sigset()
accept() fstatat() pthread_self() sigsuspend()
access() fsync() pthread_sigmask() sleep()
aio_error() ftruncate() raise() sockatmark()
aio_return() futimens() read() socket()
aio_suspend() getegid() readlink() socketpair()
alarm() geteuid() readlinkat() stat()
bind() getgid() recv() symlink()
cfgetispeed() getgroups() recvfrom() symlinkat()
cfgetospeed() getpeername() recvmsg() tcdrain()
cfsetispeed() getpgrp() rename() tcflow()
cfsetospeed() getpid() renameat() tcflush()
chdir() getppid() rmdir() tcgetattr()
chmod() getsockname() select() tcgetpgrp()
chown() getsockopt() sem_post() tcsendbreak()
clock_gettime() getuid() send() tcsetattr()
close() kill() sendmsg() tcsetpgrp()
connect() link() sendto() time()
creat() linkat() setgid() timer_getoverrun()
dup() listen() setpgid() timer_gettime()
dup2() lseek() setsid() timer_settime()
execl() lstat() setsockopt() times()
execle() mkdir() setuid() umask()
execv() mkdirat() shutdown() uname()
execve() mkfifo() sigaction() unlink()
faccessat() mkfifoat() sigaddset() unlinkat()
fchdir() mknod() sigdelset() utime()
fchmod() mknodat() sigemptyset() utimensat()
fchmodat() open() sigfillset() utimes()
fchown() openat() sigismember() wait()
fchownat() pause() signal() waitpid()
fcntl() pipe() sigpause() write()
fdatasync() poll() sigpending()
+All functions not listed in this table are considered to be unsafe with respect to signals. In the presence of signals, all POSIX functions behave as defined when called from or interrupted by a signal handler, with a single exception: when a signal interrupts an unsafe function and the signal handler calls an unsafe function, the behavior is undefined. + + +The C Standard, 7.14.1.1, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states + +> If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function. + + +However, in the description of `signal()`, POSIX \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\] states + +> This restriction does not apply to POSIX applications, as POSIX.1-2008 requires `raise()` to be async-signal-safe. + + +See also [undefined behavior 131.](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_131) + +**OpenBSD** + +The OpenBSD [signal()](http://www.openbsd.org/cgi-bin/man.cgi?query=signal) manual page lists a few additional functions that are asynchronous-safe in OpenBSD but "probably not on other systems" \[[OpenBSD](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-OpenBSD)\], including `snprintf()`, `vsnprintf()`, and `syslog_r()` but only when the `syslog_data struct` is initialized as a local variable. + +## Risk Assessment + +Invoking functions that are not [asynchronous-safe](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safefunction) from within a signal handler is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
SIG30-C High Likely Medium P18 L1
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 signal-handler-unsafe-call Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-SIG30
CodeSonar 7.2p0 BADFUNC.SIGNAL Use of signal
Compass/ROSE Can detect violations of the rule for single-file programs
Helix QAC 2022.4 C2028, C2030
LDRA tool suite 9.7.1 88 D, 89 D Partially implemented
Parasoft C/C++test 2022.2 CERT_C-SIG30-a Properly define signal handlers
PC-lint Plus 1.4 2670, 2761 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule SIG30-C Checks for function called from signal handler not asynchronous-safe (rule fully covered)
PRQA QA-C 9.7 2028, 2030
RuleChecker 22.04 signal-handler-unsafe-call Partially checked
Splint 3.1.1
+ + +## Related Vulnerabilities + +For an overview of software vulnerabilities resulting from improper signal handling, see Michal Zalewski's paper "Delivering Signals for Fun and Profit" \[[Zalewski 2001](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Zalewski01)\]. + +CERT Vulnerability Note [VU \#834865](http://www.kb.cert.org/vuls/id/834865), "Sendmail signal I/O race condition," describes a vulnerability resulting from a violation of this rule. Another notable case where using the `longjmp()` function in a signal handler caused a serious vulnerability is [wu-ftpd 2.4](http://seclists.org/bugtraq/1997/Jan/0011.html) \[[Greenman 1997](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Greenman97)\]. The effective user ID is set to 0 in one signal handler. If a second signal interrupts the first, a call is made to `longjmp()`, returning the program to the main thread but without lowering the user's privileges. These escalated privileges can be used for further exploitation. + +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+SIG30-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:2013 Calling functions in the C Standard Library other than abort , _Exit , and signal from within a signal handler \[asyncsig\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-479 , Signal Handler Use of a Non-reentrant Function 2017-07-10: CERT: Exact
+ + +## Bibliography + +
\[ C99 Rationale 2003 \] Subclause 5.2.3, "Signals and Interrupts" Subclause 7.14.1.1, "The signal Function"
\[ Dowd 2006 \] Chapter 13, "Synchronization and State"
\[ Greenman 1997 \]
\[ IEEE Std 1003.1:2013 \] XSH, System Interfaces, longjmp XSH, System Interfaces, raise
\[ ISO/IEC 9899:2011 \] 7.14.1.1, "The signal Function"
\[ OpenBSD \] signal() Man Page
\[ VU \#834865 \]
\[ Zalewski 2001 \] "Delivering Signals for Fun and Profit"
+adjust column widths + + +## Implementation notes + +None + +## References + +* CERT-C: [SIG30-C: Call only asynchronous-safe functions within signal handlers](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql b/c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql new file mode 100644 index 0000000000..e5dc33f817 --- /dev/null +++ b/c/cert/src/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql @@ -0,0 +1,126 @@ +/** + * @id c/cert/call-only-async-safe-functions-within-signal-handlers + * @name SIG30-C: Call only asynchronous-safe functions within signal handlers + * @description Call only asynchronous-safe functions within signal handlers. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/sig30-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.Signal +import semmle.code.cpp.dataflow.DataFlow + +/** + * Does not access an external variable except + * to assign a value to a volatile static variable of `sig_atomic_t` type + */ +class AsyncSafeVariableAccess extends VariableAccess { + AsyncSafeVariableAccess() { + this.getTarget() instanceof StackVariable + or + this.getTarget().(StaticStorageDurationVariable).getType().(SigAtomicType).isVolatile() and + this.isModified() + } +} + +abstract class AsyncSafeFunction extends Function { } + +/** + * C standard library asynchronous-safe functions + */ +class CAsyncSafeFunction extends AsyncSafeFunction { + //tion, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler + CAsyncSafeFunction() { this.hasGlobalName(["abort", "_Exit", "quick_exit", "signal"]) } +} + +/** + * POSIX defined asynchronous-safe functions + */ +class PosixAsyncSafeFunction extends AsyncSafeFunction { + PosixAsyncSafeFunction() { + this.hasGlobalName([ + "_Exit", "_exit", "abort", "accept", "access", "aio_error", "aio_return", "aio_suspend", + "alarm", "bind", "cfgetispeed", "cfgetospeed", "cfsetispeed", "cfsetospeed", "chdir", + "chmod", "chown", "clock_gettime", "close", "connect", "creat", "dup", "dup2", "execl", + "execle", "execv", "execve", "faccessat", "fchdir", "fchmod", "fchmodat", "fchown", + "fchownat", "fcntl", "fdatasync", "fexecve", "fork", "fstat", "fstatat", "fsync", + "ftruncate", "futimens", "getegid", "geteuid", "getgid", "getgroups", "getpeername", + "getpgrp", "getpid", "getppid", "getsockname", "getsockopt", "getuid", "kill", "link", + "linkat", "listen", "lseek", "lstat", "mkdir", "mkdirat", "mkfifo", "mkfifoat", "mknod", + "mknodat", "open", "openat", "pause", "pipe", "poll", "posix_trace_event", "pselect", + "pthread_kill", "pthread_self", "pthread_sigmask", "raise", "read", "readlink", + "readlinkat", "recv", "recvfrom", "recvmsg", "rename", "renameat", "rmdir", "select", + "sem_post", "send", "sendmsg", "sendto", "setgid", "setpgid", "setsid", "setsockopt", + "setuid", "shutdown", "sigaction", "sigaddset", "sigdelset", "sigemptyset", "sigfillset", + "sigismember", "signal", "sigpause", "sigpending", "sigprocmask", "sigqueue", "sigset", + "sigsuspend", "sleep", "sockatmark", "socket", "socketpair", "stat", "symlink", "symlinkat", + "tcdrain", "tcflow", "tcflush", "tcgetattr", "tcgetpgrp", "tcsendbreak", "tcsetattr", + "tcsetpgrp", "time", "timer_getoverrun", "timer_gettime", "timer_settime", "times", "umask", + "uname", "unlink", "unlinkat", "utime", "utimensat", "utimes", "wait", "waitpid", "write" + ]) + } +} + +/** + * Application defined asynchronous-safe functions + */ +class ApplicationAsyncSafeFunction extends AsyncSafeFunction { + pragma[nomagic] + ApplicationAsyncSafeFunction() { + // Application-defined + this.hasDefinition() and + exists(this.getFile().getRelativePath()) and + // Only references async-safe variables + not exists(VariableAccess va | + this = va.getEnclosingFunction() and not va instanceof AsyncSafeVariableAccess + ) and + // Only calls async-safe functions + not exists(Function f | this.calls(f) and not f instanceof AsyncSafeFunction) + } +} + +/** + * Call to function `raise` within a signal handler with mismatching signals + * ``` + * void int_handler(int signum) { + * raise(SIGTERM); + * } + * int main(void) { + * signal(SIGINT, int_handler); + * } + * ``` + */ +class AsyncUnsafeRaiseCall extends FunctionCall { + AsyncUnsafeRaiseCall() { + this.getTarget().hasGlobalName("raise") and + exists(SignalHandler handler | + handler = this.getEnclosingFunction() and + not handler.getRegistration().getArgument(0).getValue() = this.getArgument(0).getValue() and + not DataFlow::localFlow(DataFlow::parameterNode(handler.getParameter(0)), + DataFlow::exprNode(this.getArgument(0))) + ) + } +} + +from FunctionCall fc, SignalHandler handler +where + not isExcluded(fc, SignalHandlersPackage::callOnlyAsyncSafeFunctionsWithinSignalHandlersQuery()) and + handler = fc.getEnclosingFunction() and + ( + not fc.getTarget() instanceof AsyncSafeFunction + or + fc instanceof AsyncUnsafeRaiseCall + ) +select fc, "Asynchronous-unsafe function calls within a $@ can lead to undefined behavior.", + handler.getRegistration(), "signal handler" diff --git a/c/cert/src/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.md b/c/cert/src/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.md new file mode 100644 index 0000000000..5f1f815d23 --- /dev/null +++ b/c/cert/src/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.md @@ -0,0 +1,183 @@ +# SIG31-C: Do not access shared objects in signal handlers + +This query implements the CERT-C rule SIG31-C: + +> Do not access shared objects in signal handlers + + +## Description + +Accessing or modifying shared objects in signal handlers can result in race conditions that can leave data in an inconsistent state. The two exceptions (C Standard, 5.1.2.3, paragraph 5) to this rule are the ability to read from and write to lock-free atomic objects and variables of type `volatile sig_atomic_t`. Accessing any other type of object from a signal handler is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 131](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_131).) + +The need for the `volatile` keyword is described in [DCL22-C. Use volatile for data that cannot be cached](https://wiki.sei.cmu.edu/confluence/display/c/DCL22-C.+Use+volatile+for+data+that+cannot+be+cached). + +The type `sig_atomic_t` is the integer type of an object that can be accessed as an atomic entity even in the presence of asynchronous interrupts. The type of `sig_atomic_t` is [implementation-defined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation-definedbehavior), though it provides some guarantees. Integer values ranging from `SIG_ATOMIC_MIN` through `SIG_ATOMIC_MAX`, inclusive, may be safely stored to a variable of the type. In addition, when `sig_atomic_t` is a signed integer type, `SIG_ATOMIC_MIN` must be no greater than `−127` and `SIG_ATOMIC_MAX` no less than `127`. Otherwise, `SIG_ATOMIC_MIN` must be `0` and `SIG_ATOMIC_MAX` must be no less than `255`. The macros `SIG_ATOMIC_MIN` and `SIG_ATOMIC_MAX` are defined in the header ``. + +According to the C99 Rationale \[[C99 Rationale 2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-C992003)\], other than calling a limited, prescribed set of library functions, + +> the C89 Committee concluded that about the only thing a [strictly conforming](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-strictlyconforming) program can do in a signal handler is to assign a value to a `volatile static` variable which can be written uninterruptedly and promptly return. + + +However, this issue was discussed at the April 2008 meeting of ISO/IEC WG14, and it was agreed that there are no known [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) in which it would be an error to read a value from a `volatile sig_atomic_t` variable, and the original intent of the committee was that both reading and writing variables of `volatile sig_atomic_t` would be strictly conforming. + +The signal handler may also call a handful of functions, including `abort().` (See [SIG30-C. Call only asynchronous-safe functions within signal handlers](https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers) for more information.) + +## Noncompliant Code Example + +In this noncompliant code example, `err_msg` is updated to indicate that the `SIGINT` signal was delivered. The `err_msg` variable is a character pointer and not a variable of type `volatile sig_atomic_t`. + +```cpp +#include +#include +#include + +enum { MAX_MSG_SIZE = 24 }; +char *err_msg; + +void handler(int signum) { + strcpy(err_msg, "SIGINT encountered."); +} + +int main(void) { + signal(SIGINT, handler); + + err_msg = (char *)malloc(MAX_MSG_SIZE); + if (err_msg == NULL) { + /* Handle error */ + } + strcpy(err_msg, "No errors yet."); + /* Main code loop */ + return 0; +} + +``` + +## Compliant Solution (Writing volatile sig_atomic_t) + +For maximum portability, signal handlers should only unconditionally set a variable of type `volatile sig_atomic_t` and return, as in this compliant solution: + +```cpp +#include +#include +#include + +enum { MAX_MSG_SIZE = 24 }; +volatile sig_atomic_t e_flag = 0; + +void handler(int signum) { + e_flag = 1; +} + +int main(void) { + char *err_msg = (char *)malloc(MAX_MSG_SIZE); + if (err_msg == NULL) { + /* Handle error */ + } + + signal(SIGINT, handler); + strcpy(err_msg, "No errors yet."); + /* Main code loop */ + if (e_flag) { + strcpy(err_msg, "SIGINT received."); + } + return 0; +} + +``` + +## Compliant Solution (Lock-Free Atomic Access) + +Signal handlers can refer to objects with static or thread storage durations that are lock-free atomic objects, as in this compliant solution: + +```cpp +#include +#include +#include +#include + +#ifdef __STDC_NO_ATOMICS__ +#error "Atomics are not supported" +#elif ATOMIC_INT_LOCK_FREE == 0 +#error "int is never lock-free" +#endif + +atomic_int e_flag = ATOMIC_VAR_INIT(0); + +void handler(int signum) { + e_flag = 1; +} + +int main(void) { + enum { MAX_MSG_SIZE = 24 }; + char err_msg[MAX_MSG_SIZE]; +#if ATOMIC_INT_LOCK_FREE == 1 + if (!atomic_is_lock_free(&e_flag)) { + return EXIT_FAILURE; + } +#endif + if (signal(SIGINT, handler) == SIG_ERR) { + return EXIT_FAILURE; + } + strcpy(err_msg, "No errors yet."); + /* Main code loop */ + if (e_flag) { + strcpy(err_msg, "SIGINT received."); + } + return EXIT_SUCCESS; +} + +``` + +## Exceptions + +**SIG31-C-EX1:** The C Standard, 7.14.1.1 paragraph 5 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], makes a special exception for `errno` when a valid call to the `signal()` function results in a `SIG_ERR` return, allowing `errno` to take an indeterminate value. (See [ERR32-C. Do not rely on indeterminate values of errno](https://wiki.sei.cmu.edu/confluence/display/c/SIG31-C.+Do+not+access+shared+objects+in+signal+handlers#).) + +## Risk Assessment + +Accessing or modifying shared objects in signal handlers can result in accessing data in an inconsistent state. Michal Zalewski's paper "Delivering Signals for Fun and Profit" \[[Zalewski 2001](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Zalewski01)\] provides some examples of [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) that can result from violating this and other signal-handling rules. + +
Rule Severity Likelihood Remediation Cost Priority Level
SIG31-C High Likely High P9 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 signal-handler-shared-access Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-SIG31
CodeSonar 7.2p0 CONCURRENCY.DATARACE Data race
Compass/ROSE Can detect violations of this rule for single-file programs
Helix QAC 2022.4 C2029, C2030 C++3854, C++3855
LDRA tool suite 9.7.1 87 D Fully implemented
Parasoft C/C++test 2022.2 CERT_C-SIG31-a Properly define signal handlers
PC-lint Plus 1.4 2765 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule SIG31-C Checks for shared data access within signal handler (rule partially covered)
PRQA QA-C 9.7 2029, 2030
RuleChecker 22.04 signal-handler-shared-access 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+SIG31-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:2013 Accessing shared objects in signal handlers \[accsig\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-662 , Improper Synchronization 2017-07-10: CERT: Rule subset of CWE
CWE 2.11 CWE-828 , Signal Handler with Functionality that is not Asynchronous-Safe 2017-10-30:MITRE:Unspecified Relationship 2018-10-19: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-662 and SIG31-C** + +CWE-662 = Union( SIG31-C, list) where list = + +* Improper synchronization of shared objects between threads +* Improper synchronization of files between programs (enabling TOCTOU race conditions +**CWE-828 and SIG31-C** + +CWE-828 = SIG31-C + non-async-safe things besides shared objects. + +## Bibliography + +
\[ C99 Rationale 2003 \] 5.2.3, "Signals and Interrupts"
\[ ISO/IEC 9899:2011 \] Subclause 7.14.1.1, "The signal Function"
\[ Zalewski 2001 \]
+ + +## Implementation notes + +The implementation does not verify the correct usage of `atomic_is_lock_free`. + +## References + +* CERT-C: [SIG31-C: Do not access shared objects in signal handlers](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.ql b/c/cert/src/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.ql new file mode 100644 index 0000000000..eaa0a446b5 --- /dev/null +++ b/c/cert/src/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.ql @@ -0,0 +1,51 @@ +/** + * @id c/cert/do-not-access-shared-objects-in-signal-handlers + * @name SIG31-C: Do not access shared objects in signal handlers + * @description Do not access shared objects in signal handlers. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/sig31-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.Signal + +/** + * Does not an access an external variable except + * to assign a value to a volatile static variable of sig_atomic_t type + */ +class UnsafeSharedVariableAccess extends VariableAccess { + UnsafeSharedVariableAccess() { + // excluding `volatile sig_atomic_t` type + not this.getType().(SigAtomicType).isVolatile() and + exists(Variable target | target = this.getTarget() | + // static or thread local storage duration + ( + target instanceof StaticStorageDurationVariable or + target.isThreadLocal() + ) and + // excluding lock-free atomic objects + not exists(MacroInvocation mi, VariableAccess va | va.getTarget() = target | + mi.getMacroName() = "atomic_is_lock_free" and + mi.getExpr().getChild(0) = va.getEnclosingElement*() + ) + ) + } +} + +from UnsafeSharedVariableAccess va, SignalHandler handler +where + not isExcluded(va, SignalHandlersPackage::doNotAccessSharedObjectsInSignalHandlersQuery()) and + handler = va.getEnclosingFunction() +select va, "Shared object access within a $@ can lead to undefined behavior.", + handler.getRegistration(), "signal handler" diff --git a/c/cert/src/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.md b/c/cert/src/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.md new file mode 100644 index 0000000000..98683e5d9b --- /dev/null +++ b/c/cert/src/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.md @@ -0,0 +1,143 @@ +# SIG34-C: Do not call signal() from within interruptible signal handlers + +This query implements the CERT-C rule SIG34-C: + +> Do not call signal() from within interruptible signal handlers + + +## Description + +A signal handler should not reassert its desire to handle its own signal. This is often done on *nonpersistent* platforms—that is, platforms that, upon receiving a signal, reset the handler for the signal to SIG_DFL before calling the bound signal handler. Calling` signal()` under these conditions presents a race condition. (See [SIG01-C. Understand implementation-specific details regarding signal handler persistence](https://wiki.sei.cmu.edu/confluence/display/c/SIG01-C.+Understand+implementation-specific+details+regarding+signal+handler+persistence).) + +A signal handler may call `signal()` only if it does not need to be [asynchronous-safe](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-asynchronous-safefunction) (that is, if all relevant signals are masked so that the handler cannot be interrupted). + +## Noncompliant Code Example (POSIX) + +On nonpersistent platforms, this noncompliant code example contains a race window, starting when the host environment resets the signal and ending when the handler calls `signal()`. During that time, a second signal sent to the program will trigger the default signal behavior, consequently defeating the persistent behavior implied by the call to `signal()` from within the handler to reassert the binding. + +If the environment is persistent (that is, it does not reset the handler when the signal is received), the `signal()` call from within the `handler()` function is redundant. + +```cpp +#include + +void handler(int signum) { + if (signal(signum, handler) == SIG_ERR) { + /* Handle error */ + } + /* Handle signal */ +} + +void func(void) { + if (signal(SIGUSR1, handler) == SIG_ERR) { + /* Handle error */ + } +} +``` + +## Compliant Solution (POSIX) + +Calling the `signal()` function from within the signal handler to reassert the binding is unnecessary for persistent platforms, as in this compliant solution: + +```cpp +#include + +void handler(int signum) { + /* Handle signal */ +} + +void func(void) { + if (signal(SIGUSR1, handler) == SIG_ERR) { + /* Handle error */ + } +} +``` + +## Compliant Solution (POSIX) + +POSIX defines the `sigaction()` function, which assigns handlers to signals in a similar manner to `signal()` but allows the caller to explicitly set persistence. Consequently, the `sigaction()` function can be used to eliminate the race window on nonpersistent platforms, as in this compliant solution: + +```cpp +#include +#include + +void handler(int signum) { + /* Handle signal */ +} + +void func(void) { + struct sigaction act; + act.sa_handler = handler; + act.sa_flags = 0; + if (sigemptyset(&act.sa_mask) != 0) { + /* Handle error */ + } + if (sigaction(SIGUSR1, &act, NULL) != 0) { + /* Handle error */ + } +} +``` +Although the handler in this example does not call `signal()`, it could do so safely because the signal is masked and the handler cannot be interrupted. If the same handler is installed for more than one signal, the signals must be masked explicitly in `act.sa_mask` to ensure that the handler cannot be interrupted because the system masks only the signal being delivered. + +POSIX recommends that new applications should use `sigaction()` rather than `signal()`. The `sigaction()` function is not defined by the C Standard and is not supported on some platforms, including Windows. + +## Compliant Solution (Windows) + +There is no safe way to implement persistent signal-handler behavior on Windows platforms, and it should not be attempted. If a design depends on this behavior, and the design cannot be altered, it may be necessary to claim a deviation from this rule after completing an appropriate risk analysis. + +The reason for this is that Windows is a nonpersistent platform as discussed above. Just before calling the current handler function, Windows resets the handler for the next occurrence of the same signal to `SIG_DFL`. If the handler calls `signal()` to reinstall itself, there is still a race window. A signal might occur between the start of the handler and the call to `signal()`, which would invoke the default behavior instead of the desired handler. + +## Exceptions + +**SIG34-C-EX1:** For implementations with persistent signal handlers, it is safe for a handler to modify the behavior of its own signal. Behavior modifications include ignoring the signal, resetting to the default behavior, and having the signal handled by a different handler. A handler reasserting its binding is also safe but unnecessary. + +The following code example resets a signal handler to the system's default behavior: + +```cpp +#include + +void handler(int signum) { +#if !defined(_WIN32) + if (signal(signum, SIG_DFL) == SIG_ERR) { + /* Handle error */ + } +#endif + /* Handle signal */ +} + +void func(void) { + if (signal(SIGUSR1, handler) == SIG_ERR) { + /* Handle error */ + } +} +``` + +## Risk Assessment + +Two signals in quick succession can trigger a race condition on nonpersistent platforms, causing the signal's default behavior despite a handler's attempt to override it. + +
Rule Severity Likelihood Remediation Cost Priority Level
SIG34-C Low Unlikely Low P3 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 signal-handler-signal-call Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-SIG34
CodeSonar 7.2p0 BADFUNC.SIGNAL Use of signal
Compass/ROSE Can detect violations of this rule. However, false positives may occur on systems with persistent handlers
Helix QAC 2022.4 C5021 C++5022
Klocwork 2022.4 MISRA.STDLIB.SIGNAL
LDRA tool suite 9.7.1 97 D Fully implemented
Parasoft C/C++test 2022.2 CERT_C-SIG34-a Properly define signal handlers
PC-lint Plus 1.4 2762, 2763 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule SIG34-C Checks for signal call from within signal handler (rule partially covered)
PRQA QA-C 9.7 5021 Partially implemented
PRQA QA-C++ 4.4 5022
RuleChecker 22.04 signal-handler-signal-call 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+SIG34-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
CERT C Secure Coding Standard SIG01-C. Understand implementation-specific details regarding signal handler persistence Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Calling signal from interruptible signal handlers \[sigcall\] Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Implementation notes + +None + +## References + +* CERT-C: [SIG34-C: Do not call signal() from within interruptible signal handlers](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.ql b/c/cert/src/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.ql new file mode 100644 index 0000000000..0586c40c36 --- /dev/null +++ b/c/cert/src/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.ql @@ -0,0 +1,29 @@ +/** + * @id c/cert/do-not-call-signal-from-interruptible-signal-handlers + * @name SIG34-C: Do not call signal() from within interruptible signal handlers + * @description Do not call signal() from within interruptible signal handlers. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/sig34-c + * correctness + * security + * 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.Signal + +from FunctionCall signal +where + not isExcluded(signal, + SignalHandlersPackage::doNotCallSignalFromInterruptibleSignalHandlersQuery()) and + signal = any(SignalHandler handler).getReassertingCall() +select signal, + "Reasserting handler bindings introduces a race condition on nonpersistent platforms and is redundant otherwise." diff --git a/c/cert/src/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.md b/c/cert/src/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.md new file mode 100644 index 0000000000..70a90d5327 --- /dev/null +++ b/c/cert/src/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.md @@ -0,0 +1,119 @@ +# SIG35-C: Do not return from a computational exception signal handler + +This query implements the CERT-C rule SIG35-C: + +> Do not return from a computational exception signal handler + + +## Description + +According to the C Standard, 7.14.1.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], if a signal handler returns when it has been entered as a result of a computational exception (that is, with the value of its argument of `SIGFPE`, `SIGILL`, `SIGSEGV`, or any other implementation-defined value corresponding to such an exception) returns, then the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 130](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-130).) + +The Portable Operating System Interface (POSIX®), Base Specifications, Issue 7 \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\], adds `SIGBUS` to the list of computational exception signal handlers: + +> The behavior of a process is undefined after it returns normally from a signal-catching function for a `SIGBUS`, `SIGFPE`, `SIGILL`, or `SIGSEGV` signal that was not generated by `kill()`, `sigqueue()`, or `raise()`. + + +Do not return from `SIGFPE`, `SIGILL`, `SIGSEGV`, or any other implementation-defined value corresponding to a computational exception, such as `SIGBUS` on POSIX systems, regardless of how the signal was generated. + +## Noncompliant Code Example + +In this noncompliant code example, the division operation has undefined behavior if `denom` equals `0.` (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)) and may result in a `SIGFPE` signal to the program.) + +```cpp +#include +#include +#include +#include + +volatile sig_atomic_t denom; + +void sighandle(int s) { + /* Fix the offending volatile */ + if (denom == 0) { + denom = 1; + } +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + return 0; + } + + char *end = NULL; + long temp = strtol(argv[1], &end, 10); + + if (end == argv[1] || 0 != *end || + ((LONG_MIN == temp || LONG_MAX == temp) && errno == ERANGE)) { + /* Handle error */ + } + + denom = (sig_atomic_t)temp; + signal(SIGFPE, sighandle); + + long result = 100 / (long)denom; + return 0; +} + +``` +When compiled with some implementations, this noncompliant code example will loop infinitely if given the input `0`. It illustrates that even when a `SIGFPE` handler attempts to fix the error condition while obeying all other rules of signal handling, the program still does not behave as expected. + +## Compliant Solution + +The only portably safe way to leave a `SIGFPE`, `SIGILL`, or `SIGSEGV` handler is to invoke `abort()`, `quick_exit()`, or `_Exit()`. In the case of `SIGFPE`, the default action is [abnormal termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination), so no user-defined handler is required: + +```cpp +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + if (argc < 2) { + return 0; + } + + char *end = NULL; + long denom = strtol(argv[1], &end, 10); + + if (end == argv[1] || 0 != *end || + ((LONG_MIN == denom || LONG_MAX == denom) && errno == ERANGE)) { + /* Handle error */ + } + + long result = 100 / denom; + return 0; +} +``` +**Implementation Details** + +Some implementations define useful behavior for programs that return from one or more of these signal handlers. For example, Solaris provides the `sigfpe()` function specifically to set a `SIGFPE` handler that a program may safely return from. [Oracle](http://docs.oracle.com/) also provides platform-specific computational exceptions for the `SIGTRAP`, `SIGBUS`, and `SIGEMT` signals. Finally, GNU libsigsegv takes advantage of the ability to return from a `SIGSEGV` handler to implement page-level memory management in user mode. + +## Risk Assessment + +Returning from a computational exception signal handler is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
SIG35-C Low Unlikely High P1 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-SIG35
CodeSonar 7.2p0 LANG.STRUCT.RFCESH Return from Computational Exception Signal Handler
Helix QAC 2022.4 DF4846, DF4847, DF4848
Klocwork 2022.4 CERT.STDLIB.SIGNAL
LDRA tool suite 9.7.1 44 S Enhanced enforcement
Parasoft C/C++test 2022.2 CERT_C-SIG35-a Do not return from a computational exception signal handler
PC-lint Plus 1.4 2671, 2764 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule SIG35-C Checks for return from computational exception signal handler (rule fully covered)
+ + +## 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+SIG35-C). + +## Bibliography + +
\[ IEEE Std 1003.1:2013 \] 2.4.1, Signal Generation and Delivery
\[ ISO/IEC 9899:2011 \] Subclause 7.14.1.1, "The signal Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [SIG35-C: Do not return from a computational exception signal handler](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.ql b/c/cert/src/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.ql new file mode 100644 index 0000000000..bd65019f98 --- /dev/null +++ b/c/cert/src/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.ql @@ -0,0 +1,48 @@ +/** + * @id c/cert/do-not-return-from-a-computational-exception-handler + * @name SIG35-C: Do not return from a computational exception signal handler + * @description Do not return from a computational exception signal handler. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/sig35-c + * correctness + * 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.c.Signal +import semmle.code.cpp.dataflow.DataFlow + +/** + * CFG nodes preceeding a `ReturnStmt` + */ +ControlFlowNode reachesReturn(ReturnStmt return) { + result = return + or + exists(ControlFlowNode mid | + result = mid.getAPredecessor() and + mid = reachesReturn(return) and + // stop recursion on calls to `abort`, `_Exit` and "quick_exit" + not result instanceof AbortCall + ) +} + +from ReturnStmt return, ComputationalExceptionSignal e +where + not isExcluded(return, SignalHandlersPackage::doNotReturnFromAComputationalExceptionHandlerQuery()) and + exists(SignalHandler handler | + handler = return.getEnclosingFunction() and + // computational exception handler + DataFlow::localExprFlow(e.getExpr(), handler.getRegistration().getArgument(0)) and + // control flow reaches a return statement + reachesReturn(return) = handler.getBlock() + ) +select return, "Do not return from a $@ signal handler.", e, "computational exception" diff --git a/c/cert/src/rules/STR30-C/DoNotAttemptToModifyStringLiterals.ql b/c/cert/src/rules/STR30-C/DoNotAttemptToModifyStringLiterals.ql index b26cc1a040..397e1bfc9e 100644 --- a/c/cert/src/rules/STR30-C/DoNotAttemptToModifyStringLiterals.ql +++ b/c/cert/src/rules/STR30-C/DoNotAttemptToModifyStringLiterals.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/str30-c * correctness * security + * 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 */ @@ -39,12 +44,8 @@ class ModifiesFirstArgFunction extends BufferWrite, FunctionCall { * literal that is assigned to a non modifiable type or wherein the string * literal arises as a argument to a function that may modify its argument. */ -class ImplicitOrExplicitStringLiteralModifiedConfiguration extends DataFlow::Configuration { - ImplicitOrExplicitStringLiteralModifiedConfiguration() { - this = "ImplicitOrExplicitStringLiteralModifiedConfiguration" - } - - override predicate isSource(DataFlow::Node node) { +module ImplicitOrExplicitStringLiteralModifiedConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { // usage through variables exists(Variable v | v.getAnAssignedValue() = node.asExpr() and @@ -65,7 +66,7 @@ class ImplicitOrExplicitStringLiteralModifiedConfiguration extends DataFlow::Con ) } - override predicate isSink(DataFlow::Node node) { + predicate isSink(DataFlow::Node node) { // it's either a buffer write of some kind that we // know about exists(BufferWrite bw | bw.getDest() = node.asExpr()) @@ -77,6 +78,9 @@ class ImplicitOrExplicitStringLiteralModifiedConfiguration extends DataFlow::Con } } +module ImplicitOrExplicitStringLiteralModifiedFlow = + DataFlow::Global; + class MaybeReturnsStringLiteralFunctionCall extends FunctionCall { MaybeReturnsStringLiteralFunctionCall() { getTarget().getName() in [ @@ -144,11 +148,12 @@ class ImplicitStringLiteralBase extends Expr { } } -from Expr literal, Expr literalWrite, ImplicitOrExplicitStringLiteralModifiedConfiguration config +from Expr literal, Expr literalWrite where not isExcluded(literal, Strings1Package::doNotAttemptToModifyStringLiteralsQuery()) and not isExcluded(literalWrite, Strings1Package::doNotAttemptToModifyStringLiteralsQuery()) and - config.hasFlow(DataFlow::exprNode(literal), DataFlow::exprNode(literalWrite)) + ImplicitOrExplicitStringLiteralModifiedFlow::flow(DataFlow::exprNode(literal), + DataFlow::exprNode(literalWrite)) select literalWrite, "This operation may write to a string that may be a string literal that was $@.", literal, "created here" diff --git a/c/cert/src/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.ql b/c/cert/src/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.ql index 24e55f80d3..437b13f7f9 100644 --- a/c/cert/src/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.ql +++ b/c/cert/src/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.ql @@ -10,13 +10,17 @@ * @tags external/cert/id/str31-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 semmle.code.cpp.dataflow.TaintTracking -import semmle.code.cpp.dataflow.internal.TaintTrackingUtil import codingstandards.cpp.PossiblyUnsafeStringOperation /** diff --git a/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.md b/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.md index 62ebcc2db9..cfe024adc7 100644 --- a/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.md +++ b/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.md @@ -271,7 +271,7 @@ CWE-123 – STR31-C = ## Implementation notes -None +Wide character types are not handled correctly on the `aarch64le` architecture. This can lead to false negative alerts. ## References diff --git a/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.ql b/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.ql index a65e7958be..723c8ee0ea 100644 --- a/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.ql +++ b/c/cert/src/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/str32-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 */ @@ -16,8 +21,8 @@ import cpp import codingstandards.c.cert import codingstandards.cpp.Naming import semmle.code.cpp.dataflow.TaintTracking -import semmle.code.cpp.dataflow.internal.TaintTrackingUtil import codingstandards.cpp.PossiblyUnsafeStringOperation +import semmle.code.cpp.valuenumbering.GlobalValueNumbering /** * Models a function that is part of the standard library that expects a @@ -44,32 +49,90 @@ class ExpectsNullTerminatedStringAsArgumentFunctionCall extends FunctionCall { Expr getAnExpectingExpr() { result = e } } -from ExpectsNullTerminatedStringAsArgumentFunctionCall fc, Expr e, Expr target -where - target = fc.getAnExpectingExpr() and - not isExcluded(fc, Strings1Package::nonNullTerminatedToFunctionThatExpectsAStringQuery()) and - ( - exists(PossiblyUnsafeStringOperation op | - // don't report violations of the same function call. - not op = fc and - e = op and - TaintTracking::localTaint(DataFlow::exprNode(op.getAnArgument()), DataFlow::exprNode(target)) +class PossiblyUnsafeStringOperationSource extends Source { + PossiblyUnsafeStringOperation op; + + PossiblyUnsafeStringOperationSource() { this.asExpr() = op.getAnArgument() } + + PossiblyUnsafeStringOperation getOp() { result = op } +} + +class CharArraySource extends Source { + CharArrayInitializedWithStringLiteral op; + + CharArraySource() { + op.getContainerLength() <= op.getStringLiteralLength() and + this.asExpr() = op + } +} + +abstract class Source extends DataFlow::Node { } + +class Sink extends DataFlow::Node { + Sink() { + exists(ExpectsNullTerminatedStringAsArgumentFunctionCall fc | + fc.getAnExpectingExpr() = this.asExpr() ) - or - exists(CharArrayInitializedWithStringLiteral op | - e = op and - op.getContainerLength() <= op.getStringLiteralLength() and - TaintTracking::localTaint(DataFlow::exprNode(op), DataFlow::exprNode(target)) + } +} + +module MyFlowConfiguration implements DataFlow::ConfigSig { + predicate isSink(DataFlow::Node sink) { + sink instanceof Sink and + //don't report violations of the same function call + not sink instanceof Source + } + + predicate isSource(DataFlow::Node source) { source instanceof Source } + + predicate isAdditionalFlowStep(DataFlow::Node innode, DataFlow::Node outnode) { + exists(FunctionCall realloc, ReallocFunction fn | + fn.getACallToThisFunction() = realloc and + realloc.getArgument(0) = innode.asExpr() and + realloc = outnode.asExpr() ) - ) and - // don't report cases flowing to this node where there is a flow from a - // literal assignment of a null terminator - not exists(AssignExpr aexp | + } +} + +class ReallocFunction extends AllocationFunction { + ReallocFunction() { exists(this.getReallocPtrArg()) } +} + +/** + * Determines if the string is acceptably null terminated + * The only condition we accept as a guarantee to null terminate is: + * `str[size_expr] = '\0';` + * where we do not check the value of the `size_expr` used + */ +predicate isGuarded(Expr guarded, Expr source) { + exists(AssignExpr aexp | aexp.getLValue() instanceof ArrayExpr and aexp.getRValue() instanceof Zero and - TaintTracking::localTaint(DataFlow::exprNode(aexp.getRValue()), DataFlow::exprNode(target)) and - // this must be AFTER the operation causing the non-null termination to be valid. - aexp.getAPredecessor*() = e + // this must be AFTER the operation causing the non-null termination + aexp.getAPredecessor+() = source and + //this guards anything after it + aexp.getASuccessor+() = guarded and + // no reallocs exist after this because they will be conservatively assumed to make the buffer smaller and remove the likliehood of this properly terminating + not exists(ReallocFunction realloc, FunctionCall fn | + fn = realloc.getACallToThisFunction() and + globalValueNumber(aexp.getLValue().(ArrayExpr).getArrayBase()) = + globalValueNumber(fn.getArgument(0)) and + aexp.getASuccessor+() = fn + ) ) +} + +module MyFlow = TaintTracking::Global; + +from + DataFlow::Node source, DataFlow::Node sink, ExpectsNullTerminatedStringAsArgumentFunctionCall fc, + Expr e +where + MyFlow::flow(source, sink) and + sink.asExpr() = fc.getAnExpectingExpr() and + not isGuarded(sink.asExpr(), source.asExpr()) and + if source instanceof PossiblyUnsafeStringOperationSource + then e = source.(PossiblyUnsafeStringOperationSource).getOp() + else e = source.asExpr() select fc, "String modified by $@ is passed to function expecting a null-terminated string.", e, "this expression" diff --git a/c/cert/src/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.ql b/c/cert/src/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.ql index b0d4088f9f..d814951b37 100644 --- a/c/cert/src/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.ql +++ b/c/cert/src/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.ql @@ -9,23 +9,21 @@ * @tags external/cert/id/str34-c * correctness * security + * 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.commons.CommonType +import codingstandards.cpp.rules.castcharbeforeconvertingtolargersizes.CastCharBeforeConvertingToLargerSizes -from Cast c -where - not isExcluded(c, Strings3Package::castCharBeforeConvertingToLargerSizesQuery()) and - // find cases where there is a conversion happening wherein the - // base type is a char - c.getExpr().getType() instanceof CharType and - not c.getExpr().getType() instanceof UnsignedCharType and - // it's a bigger type - c.getType().getSize() > c.getExpr().getType().getSize() and - // and it's some kind of integer type - c.getType() instanceof IntegralType -select c.getExpr(), - "Expression not converted to `unsigned char` before converting to a larger integer type." +class CastCharBeforeConvertingToLargerSizesQuery extends CastCharBeforeConvertingToLargerSizesSharedQuery +{ + CastCharBeforeConvertingToLargerSizesQuery() { + this = Strings3Package::castCharBeforeConvertingToLargerSizesQuery() + } +} diff --git a/c/cert/src/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.ql b/c/cert/src/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.ql index cb742859cc..a29dbd34b9 100644 --- a/c/cert/src/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.ql +++ b/c/cert/src/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/str37-c * correctness * security + * 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 */ @@ -16,14 +21,11 @@ import cpp import codingstandards.c.cert import codingstandards.cpp.CharFunctions -from FunctionCall fc, Expr arg +from UseOfToOrIsChar useOfCharAPI, Expr arg where - not isExcluded(fc, Strings2Package::toCharacterHandlingFunctionsRepresentableAsUCharQuery()) and - // examine all impacted functions - fc.getTarget() instanceof CToOrIsCharFunction and - arg = fc.getArgument(0).getFullyConverted() and - // report on cases where either the explicit or implicit cast - // on the parameter type is not unsigned - not arg.(CStyleCast).getExpr().getType() instanceof UnsignedCharType -select fc, "$@ to character-handling function may not be representable as an unsigned char.", arg, - "Argument" + not isExcluded(useOfCharAPI, + Strings2Package::toCharacterHandlingFunctionsRepresentableAsUCharQuery()) and + arg = useOfCharAPI.getConvertedArgument() and + not arg.getType() instanceof UnsignedCharType +select useOfCharAPI, + "$@ to character-handling function may not be representable as an unsigned char.", arg, "Argument" diff --git a/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.md b/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.md index 3c08369681..504bd764ac 100644 --- a/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.md +++ b/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.md @@ -131,7 +131,7 @@ Search for vulnerabilities resulting from the violation of this rule on the [CER ## Implementation notes -None +Wide character types are not handled correctly on the `aarch64le` architecture. This can lead to false negative alerts. ## References diff --git a/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.ql b/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.ql index efc8889e16..58b2b1c7dd 100644 --- a/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.ql +++ b/c/cert/src/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/str38-c * correctness * security + * external/cert/severity/high + * external/cert/likelihood/likely + * external/cert/remediation-cost/low + * external/cert/priority/p27 + * external/cert/level/l1 * external/cert/obligation/rule */ @@ -63,6 +68,5 @@ where c instanceof WideToNarrowCast and actual = "wide" and expected = "narrow" ) select call, - "Call to function $@ with a " + actual + " character string $@ where a " + expected + - " character string $@ is expected.", call.getTarget(), call.getTarget().getName(), arg, - "argument", p, "parameter" + "Call to function `" + call.getTarget().getName() + "` with a " + actual + + " character string $@ where a " + expected + " character string is expected.", arg, "argument" diff --git a/c/cert/test/codeql-pack.lock.yml b/c/cert/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/c/cert/test/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/test/qlpack.yml b/c/cert/test/qlpack.yml index 69f673f348..9f5f21ba1b 100644 --- a/c/cert/test/qlpack.yml +++ b/c/cert/test/qlpack.yml @@ -1,4 +1,6 @@ -name: cert-c-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: cert-c-coding-standards -extractor: cpp \ No newline at end of file +name: codeql/cert-c-coding-standards-tests +version: 2.49.0-dev +extractor: cpp +license: MIT +dependencies: + codeql/cert-c-coding-standards: '*' diff --git a/c/cert/test/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.expected b/c/cert/test/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.expected new file mode 100644 index 0000000000..fe7ac757a6 --- /dev/null +++ b/c/cert/test/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.expected @@ -0,0 +1,11 @@ +| test.c:8:3:8:11 | ... + ... | Buffer accesses offset 404 which is greater than the fixed size 400 of the $@. | test.c:8:3:8:5 | arr | buffer | +| test.c:16:3:16:13 | ... + ... | Buffer access may be to a negative index in the buffer. | test.c:16:3:16:5 | arr | buffer | +| test.c:21:5:21:15 | ... + ... | Buffer access may be to a negative index in the buffer. | test.c:21:5:21:7 | arr | buffer | +| test.c:41:17:41:30 | ... + ... | Buffer access may be to a negative index in the buffer. | test.c:41:17:41:22 | buffer | buffer | +| test.c:45:17:45:30 | ... + ... | Buffer may access up to offset 101*1 which is greater than the fixed size 100 of the $@. | test.c:45:17:45:22 | buffer | buffer | +| test.c:55:5:55:13 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:55:5:55:9 | ptr16 | buffer | +| test.c:57:5:57:14 | ... + ... | Buffer accesses offset 22 which is greater than the fixed size 20 of the $@. | test.c:57:5:57:9 | ptr16 | buffer | +| test.c:58:5:58:14 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:58:5:58:9 | ptr16 | buffer | +| test.c:63:3:63:9 | access to array | Buffer access may be to a negative index in the buffer. | test.c:63:3:63:5 | arr | buffer | +| test.c:65:3:65:9 | access to array | Buffer accesses offset 44 which is greater than the fixed size 40 of the $@. | test.c:65:3:65:5 | arr | buffer | +| test.c:66:3:66:10 | access to array | Buffer access may be to a negative index in the buffer. | test.c:66:3:66:5 | arr | buffer | diff --git a/c/cert/test/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.qlref b/c/cert/test/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.qlref new file mode 100644 index 0000000000..a6e032ec87 --- /dev/null +++ b/c/cert/test/rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.qlref @@ -0,0 +1 @@ +rules/ARR30-C/DoNotFormOutOfBoundsPointersOrArraySubscripts.ql \ No newline at end of file diff --git a/c/cert/test/rules/ARR30-C/test.c b/c/cert/test/rules/ARR30-C/test.c new file mode 100644 index 0000000000..4d3f077c33 --- /dev/null +++ b/c/cert/test/rules/ARR30-C/test.c @@ -0,0 +1,67 @@ + + +enum { ARRAY_SIZE = 100 }; + +static int arr[ARRAY_SIZE]; + +void test_fixed_wrong(void) { + arr + 101; // NON_COMPLIANT +} + +void test_fixed_right(void) { + arr + 2; // COMPLIANT +} + +void test_no_check(int index) { + arr + index; // NON_COMPLIANT +} + +void test_invalid_check(int index) { + if (index < ARRAY_SIZE) { + arr + index; // NON_COMPLIANT - `index` could be negative + } +} + +void test_valid_check(int index) { + if (index > 0 && index < ARRAY_SIZE) { + arr + index; // COMPLIANT - `index` cannot be negative + } +} + +void test_valid_check_by_type(unsigned int index) { + if (index < ARRAY_SIZE) { + arr + index; // COMPLIANT - `index` cannot be be negative + } +} + +void test_local_buffer_invalid_check(int index) { + char buffer[ARRAY_SIZE]; + + if (index < ARRAY_SIZE) { + char *ptr = buffer + index; // NON_COMPLIANT - `index` could be negative + } + + if (index >= 0 && index < ARRAY_SIZE + 2) { + char *ptr = buffer + index; // NON_COMPLIANT - `index` could be too large + } + + if (index >= 0 && index < ARRAY_SIZE) { + char *ptr = buffer + index; // COMPLIANT + } +} + +void test_dereference_pointer_arithmetic_const(void) { + short ptr16[10]; + *(ptr16 - 1); // NON_COMPLIANT - offset is negative + *(ptr16 + 5); // COMPLIANT + *(ptr16 + 11); // NON_COMPLIANT - offset is too large + *(ptr16 - 11); // NON_COMPLIANT - offset is negative +} + +void test_array_expr_const(void) { + int arr[10]; + arr[-1]; // NON_COMPLIANT - offset is negative + arr[5]; // COMPLIANT + arr[11]; // NON_COMPLIANT - offset is too large + arr[-11]; // NON_COMPLIANT - offset is negative +} \ No newline at end of file diff --git a/c/cert/test/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.expected b/c/cert/test/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.expected new file mode 100644 index 0000000000..1617571bbe --- /dev/null +++ b/c/cert/test/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.expected @@ -0,0 +1,43 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (VariableLengthArraySizeNotInValidRange.ql:110,11-19) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (VariableLengthArraySizeNotInValidRange.ql:93,5-18) +| test.c:14:8:14:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:15:8:15:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:16:8:16:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:18:8:18:8 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:20:8:20:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:25:8:25:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:27:8:27:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:28:8:28:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:29:8:29:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:30:8:30:8 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:33:10:33:10 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:35:10:35:10 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:41:10:41:10 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:50:10:50:10 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:51:10:51:10 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:56:7:56:7 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:57:7:57:7 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:58:7:58:7 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:61:9:61:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:63:9:63:9 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:67:9:67:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:68:9:68:9 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:69:9:69:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:73:9:73:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:74:9:74:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:75:9:75:9 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:79:11:79:11 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:86:9:86:9 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:93:9:93:9 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:100:7:100:7 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:104:15:104:15 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:112:9:112:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:117:9:117:9 | VLA declaration | Variable-length array total size may be excessively large. | +| test.c:118:9:118:9 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:119:9:119:9 | VLA declaration | Variable-length array total size may be excessively large. | +| test.c:120:9:120:9 | VLA declaration | Variable-length array total size may be excessively large. | +| test.c:123:11:123:11 | VLA declaration | Variable-length array total size may be excessively large. | +| test.c:124:11:124:11 | VLA declaration | Variable-length array total size may be excessively large. | +| test.c:125:11:125:11 | VLA declaration | Variable-length array dimension size may be in an invalid range. | +| test.c:131:11:131:11 | VLA declaration | Variable-length array size derives from an overflowing or wrapping expression. | +| test.c:135:11:135:11 | VLA declaration | Variable-length array total size may be excessively large. | diff --git a/c/cert/test/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.qlref b/c/cert/test/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.qlref new file mode 100644 index 0000000000..0c6a4bd08d --- /dev/null +++ b/c/cert/test/rules/ARR32-C/VariableLengthArraySizeNotInValidRange.qlref @@ -0,0 +1 @@ +rules/ARR32-C/VariableLengthArraySizeNotInValidRange.ql \ No newline at end of file diff --git a/c/cert/test/rules/ARR32-C/test.c b/c/cert/test/rules/ARR32-C/test.c new file mode 100644 index 0000000000..80a43cdfac --- /dev/null +++ b/c/cert/test/rules/ARR32-C/test.c @@ -0,0 +1,143 @@ +#include +#include +#include + +// arbitrary excessive stack alloc size: USHRT_MAX bytes +#define VLA_MAX_SIZE USHRT_MAX + +void test_vla_constants(void) { + size_t uninitialized; + size_t zero = 0; + size_t two = 2; + size_t max_num = VLA_MAX_SIZE / sizeof(char); + + char vla0[uninitialized]; // NON_COMPLIANT - uninitialized + char vla1[zero]; // NON_COMPLIANT - zero-sized array + char vla2[zero * two]; // NON_COMPLIANT - zero-sized array + char vla3[zero + two]; // COMPLIANT + char vla4[zero - two]; // NON_COMPLIANT - wrap-around + char vla5[max_num]; // COMPLIANT + char vla6[max_num + two]; // NON_COMPLIANT - too large + char vla7[max_num + 1 - two]; // COMPLIANT; +} + +void test_vla_bounds8(uint8_t num8, int8_t snum8) { + char vla0[num8]; // NON_COMPLIANT - size could be `0` + char vla1[num8 + 1]; // COMPLIANT + char vla2[num8 - 1]; // NON_COMPLIANT - wrap-around + char vla3[snum8]; // NON_COMPLIANT - unbounded + char vla4[snum8 + 1]; // NON_COMPLIANT - unbounded + char vla5[snum8 - 1]; // NON_COMPLIANT - unbounded + + if (num8 == 0) { + char vla6[num8]; // NON_COMPLIANT - size is 0 + char vla7[num8 + 1]; // COMPLIANT - size is 1 + char vla8[num8 - 1]; // NON_COMPLIANT - wrap-around + } + + if (num8 > 0) { + char vla6[num8]; // COMPLIANT + char vla7[num8 + 1]; // COMPLIANT + char vla8[num8 - 1]; // NON_COMPLIANT - unbounded + } +} + +void test_overflowed_size(int8_t ssize) { + if (ssize > 1) { + int8_t tmp = ssize * 2; + char vla0[ssize]; // COMPLIANT + char vla1[ssize * 2]; // COMPLIANT - type promotion + char vla2[tmp]; // NON_COMPLIANT - potential overflow + char vla3[++ssize]; // NON_COMPLIANT - potential overflow + } +} + +void test_vla_bounds(size_t num) { + int vla0[num]; // NON_COMPLIANT - size could be greater than max + int vla1[num + 1]; // NON_COMPLIANT - unbounded + int vla2[num - 1]; // NON_COMPLIANT - unbounded + + if (num == 0) { + int vla6[num]; // NON_COMPLIANT - size is 0 + int vla7[num + 1]; // COMPLIANT - size is 1 + int vla8[num - 1]; // NON_COMPLIANT - unbounded + } + + if (num > 0) { + int vla6[num]; // NON_COMPLIANT - size could be greater than max + int vla7[num + 1]; // NON_COMPLIANT - unbounded + int vla8[num - 1]; // NON_COMPLIANT - unbounded + } + + if (VLA_MAX_SIZE / sizeof(int) >= num) { + int vla6[num]; // NON_COMPLIANT - size could be 0 + int vla7[num + 1]; // NON_COMPLIANT - size greater than max + int vla8[num - 1]; // NON_COMPLIANT - unbounded + + if (num >= 100) { + int vla9[num]; // COMPLIANT + int vla10[num + 1]; // NON_COMPLIANT - unbounded + int vla11[num - 1]; // COMPLIANT + } + } + + size_t num2 = num + num; + if (num2 > 0 && num2 < 100) { + int vla12[num2]; // NON_COMPLIANT - bad post-check + } + + signed int num3 = INT_MAX; + num3++; + num3 += INT_MAX; + if (num3 > 0 && num3 < 100) { + int vla13[num3]; // NON_COMPLIANT - overflowed + } + + int num4; + if (num > 2) { + num4 = num - 2; // potentially changed value + } + int vla14[num4]; // NON_COMPLIANT - unbounded +} + +void test_vla_typedef(size_t x, size_t y) { + typedef int VLA[x][y]; // NON_COMPLIANT + // ... + // (void)sizeof(VLA); +} + +void test_multidimensional_vla(size_t n, size_t m) { + + if (VLA_MAX_SIZE / sizeof(int) >= (n * m)) { // wrapping check + int vla0[n][m]; // NON_COMPLIANT - size too large + } + + if (m > 0 && n > 0 && VLA_MAX_SIZE / sizeof(int) >= n && + VLA_MAX_SIZE / sizeof(int) >= m && VLA_MAX_SIZE / sizeof(int) >= n * m) { + int vla1[n][m]; // COMPLIANT[FALSE_POSITIVE] + int vla2[n - 1][m - 1]; // NON_COMPLIANT - unbounded + int vla3[n][n]; // NON_COMPLIANT - n*n not checked + int vla4[n][n][n]; // NON_COMPLIANT - unbounded + + if (VLA_MAX_SIZE / (sizeof(int) * n) >= n * n) { + int vla5[n][n][n]; // COMPLIANT[FALSE_POSITIVE] + int vla6[m][m][m]; // NON_COMPLIANT - size too large + int vla7[n][n][n + 1]; // NON_COMPLIANT - size too large + } + + if (m > 0 && m <= 100 && n > 0 && n <= 100) { + int vla08[n][m]; // COMPLIANT + int vla09[n][n]; // COMPLIANT + int vla10[n][n - 98][n]; // NON_COMPLIANT - unbounded + if (n == 100) { + int vla11[n][n - 98][n]; // COMPLIANT + } + int vla12[n][m][n]; // NON_COMPLIANT + } + } +} + +void test_fvla(int size, + int data[size][size]) { // COMPLIANT - not an actual VLA + return; +} \ No newline at end of file diff --git a/c/cert/test/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.testref b/c/cert/test/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.testref new file mode 100644 index 0000000000..067c1c5965 --- /dev/null +++ b/c/cert/test/rules/ARR36-C/DoNotRelatePointersThatDoNotReferToTheSameArray.testref @@ -0,0 +1 @@ +c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql \ No newline at end of file diff --git a/c/cert/test/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.testref b/c/cert/test/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.testref new file mode 100644 index 0000000000..bbe3b3db8a --- /dev/null +++ b/c/cert/test/rules/ARR36-C/DoNotSubtractPointersThatDoNotReferToTheSameArray.testref @@ -0,0 +1 @@ +c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql \ No newline at end of file diff --git a/c/cert/test/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.expected b/c/cert/test/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.expected new file mode 100644 index 0000000000..fb0074e0e6 --- /dev/null +++ b/c/cert/test/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.expected @@ -0,0 +1,48 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql:28,60-68) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql:29,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql:41,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql:49,26-34) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql:70,3-11) +edges +| test.c:14:38:14:39 | p1 | test.c:18:10:18:11 | v1 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:19:10:19:11 | v2 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:20:10:20:11 | p1 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:21:10:21:11 | p1 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:22:9:22:10 | p1 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:23:13:23:14 | p1 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:24:9:24:10 | p1 | provenance | | +| test.c:14:38:14:39 | p1 | test.c:25:9:25:10 | p1 | provenance | | +| test.c:51:30:51:38 | & ... | test.c:14:38:14:39 | p1 | provenance | | +nodes +| test.c:14:38:14:39 | p1 | semmle.label | p1 | +| test.c:18:10:18:11 | v1 | semmle.label | v1 | +| test.c:19:10:19:11 | v2 | semmle.label | v2 | +| test.c:20:10:20:11 | p1 | semmle.label | p1 | +| test.c:21:10:21:11 | p1 | semmle.label | p1 | +| test.c:22:9:22:10 | p1 | semmle.label | p1 | +| test.c:23:13:23:14 | p1 | semmle.label | p1 | +| test.c:24:9:24:10 | p1 | semmle.label | p1 | +| test.c:25:9:25:10 | p1 | semmle.label | p1 | +| test.c:39:11:39:19 | & ... | semmle.label | & ... | +| test.c:40:10:40:18 | & ... | semmle.label | & ... | +| test.c:42:10:42:15 | & ... | semmle.label | & ... | +| test.c:43:10:43:15 | & ... | semmle.label | & ... | +| test.c:44:10:44:15 | & ... | semmle.label | & ... | +| test.c:46:10:46:15 | & ... | semmle.label | & ... | +| test.c:51:30:51:38 | & ... | semmle.label | & ... | +subpaths +#select +| test.c:18:10:18:11 | v1 | test.c:51:30:51:38 | & ... | test.c:18:10:18:11 | v1 | Pointer arithmetic on non-array object pointer. | +| test.c:19:10:19:11 | v2 | test.c:51:30:51:38 | & ... | test.c:19:10:19:11 | v2 | Pointer arithmetic on non-array object pointer. | +| test.c:20:10:20:11 | p1 | test.c:51:30:51:38 | & ... | test.c:20:10:20:11 | p1 | Pointer arithmetic on non-array object pointer. | +| test.c:21:10:21:11 | p1 | test.c:51:30:51:38 | & ... | test.c:21:10:21:11 | p1 | Pointer arithmetic on non-array object pointer. | +| test.c:22:9:22:10 | p1 | test.c:51:30:51:38 | & ... | test.c:22:9:22:10 | p1 | Pointer arithmetic on non-array object pointer. | +| test.c:23:13:23:14 | p1 | test.c:51:30:51:38 | & ... | test.c:23:13:23:14 | p1 | Pointer arithmetic on non-array object pointer. | +| test.c:24:9:24:10 | p1 | test.c:51:30:51:38 | & ... | test.c:24:9:24:10 | p1 | Pointer arithmetic on non-array object pointer. | +| test.c:25:9:25:10 | p1 | test.c:51:30:51:38 | & ... | test.c:25:9:25:10 | p1 | Pointer arithmetic on non-array object pointer. | +| test.c:39:11:39:19 | & ... | test.c:39:11:39:19 | & ... | test.c:39:11:39:19 | & ... | Pointer arithmetic on non-array object pointer. | +| test.c:40:10:40:18 | & ... | test.c:40:10:40:18 | & ... | test.c:40:10:40:18 | & ... | Pointer arithmetic on non-array object pointer. | +| test.c:42:10:42:15 | & ... | test.c:42:10:42:15 | & ... | test.c:42:10:42:15 | & ... | Pointer arithmetic on non-array object pointer. | +| test.c:43:10:43:15 | & ... | test.c:43:10:43:15 | & ... | test.c:43:10:43:15 | & ... | Pointer arithmetic on non-array object pointer. | +| test.c:44:10:44:15 | & ... | test.c:44:10:44:15 | & ... | test.c:44:10:44:15 | & ... | Pointer arithmetic on non-array object pointer. | +| test.c:46:10:46:15 | & ... | test.c:46:10:46:15 | & ... | test.c:46:10:46:15 | & ... | Pointer arithmetic on non-array object pointer. | diff --git a/c/cert/test/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.qlref b/c/cert/test/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.qlref new file mode 100644 index 0000000000..badf328837 --- /dev/null +++ b/c/cert/test/rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.qlref @@ -0,0 +1 @@ +rules/ARR37-C/DoNotUsePointerArithmeticOnNonArrayObjectPointers.ql \ No newline at end of file diff --git a/c/cert/test/rules/ARR37-C/test.c b/c/cert/test/rules/ARR37-C/test.c new file mode 100644 index 0000000000..28ccd243a3 --- /dev/null +++ b/c/cert/test/rules/ARR37-C/test.c @@ -0,0 +1,52 @@ +struct s1 { + int f1; + int f2; + int f3; + int f4[2]; +}; + +struct s2 { + int f1; + int f2; + int data[]; +}; + +void test_ptr_arithmetic_nested(int *p1) { + // path-dependent + int *v1 = p1; + int *v2 = p1; + (void)(v1++); + (void)(v2--); + (void)(p1 + 1); + (void)(p1 - 1); + (void)p1[1]; + (void)(1 [p1]); + (void)p1[*p1 + 0]; + (void)p1[0 + 1]; + (void)p1[0]; // COMPLIANT +} + +void test(struct s1 p1, struct s1 *p2, struct s2 *p3) { + struct s1 v1[3]; + struct s2 v2[3]; + + (void)*(v1 + 2); // COMPLIANT + (void)*(v1 + 2); // COMPLIANT + + (void)v1[2]; // COMPLIANT + (void)v2[2]; // COMPLIANT + + (void)((&v1[0].f1)[1]); // NON_COMPLIANT + (void)(&v1[0].f1 + v1[1].f1); // NON_COMPLIANT + + (void)(&p1.f1)[1]; // NON_COMPLIANT + (void)(&p1.f1 + 1); // NON_COMPLIANT + (void)(&p1.f2 - 1); // NON_COMPLIANT + + (void)(&p1.f1 + p1.f1); // NON_COMPLIANT + (void)(p1.f4 + 1); // COMPLIANT + (void)(&p1.f4 + 1); // COMPLIANT + + test_ptr_arithmetic_nested((int *)&v1[0].f4); // COMPLIANT + test_ptr_arithmetic_nested(&v1[0].f1); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.expected b/c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.expected new file mode 100644 index 0000000000..8cb1ff79d0 --- /dev/null +++ b/c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.expected @@ -0,0 +1,75 @@ +| test.c:40:3:40:8 | call to strchr | The $@ passed to strchr might not be null-terminated. | test.c:40:10:40:16 | ca5_bad | argument | test.c:40:10:40:16 | ca5_bad | | +| test.c:42:3:42:8 | call to strchr | The $@ passed to strchr is 5 bytes, but an offset of 5 bytes is used to access it. | test.c:42:10:42:21 | ... + ... | read buffer | test.c:42:10:42:21 | ... + ... | | +| test.c:46:5:46:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:46:22:46:28 | test1 | read buffer | test.c:46:12:46:19 | ca5_good | write buffer | +| test.c:53:5:53:10 | call to strcpy | The $@ passed to strcpy might not be null-terminated. | test.c:53:24:53:30 | ca5_bad | argument | test.c:53:24:53:30 | ca5_bad | | +| test.c:54:5:54:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:54:24:54:31 | ca6_good | read buffer | test.c:54:12:54:19 | call to get_ca_5 | write buffer | +| test.c:59:5:59:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:59:22:59:29 | ca6_good | read buffer | test.c:59:12:59:19 | ca5_good | write buffer | +| test.c:62:5:62:10 | call to strcpy | The $@ passed to strcpy might not be null-terminated. | test.c:62:22:62:28 | ca6_bad | argument | test.c:62:22:62:28 | ca6_bad | | +| test.c:62:5:62:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:62:22:62:28 | ca6_bad | read buffer | test.c:62:12:62:19 | ca5_good | write buffer | +| test.c:65:5:65:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:65:21:65:28 | ca6_good | read buffer | test.c:65:12:65:18 | ca5_bad | write buffer | +| test.c:71:5:71:10 | call to strcpy | The $@ passed to strcpy might not be null-terminated. | test.c:71:21:71:27 | ca5_bad | argument | test.c:71:21:71:27 | ca5_bad | | +| test.c:77:5:77:10 | call to strcpy | The $@ passed to strcpy might not be null-terminated. | test.c:77:24:77:30 | ca5_bad | argument | test.c:77:24:77:30 | ca5_bad | | +| test.c:80:5:80:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:80:24:80:31 | ca6_good | read buffer | test.c:80:12:80:19 | call to get_ca_5 | write buffer | +| test.c:103:5:103:11 | call to strncpy | The size of the $@ passed to strncpy is 5 bytes, but the $@ is 6 bytes. | test.c:103:13:103:19 | ca5_bad | write buffer | test.c:103:32:103:32 | 6 | size argument | +| test.c:127:5:127:10 | call to memcpy | The $@ passed to memcpy is accessed at an excessive offset of 1 element(s) from the $@. | test.c:127:12:127:13 | p2 | write buffer | test.c:120:21:120:26 | call to strlen | allocation size base | +| test.c:153:5:153:10 | call to strcat | The $@ passed to strcat might not be null-terminated. | test.c:153:12:153:15 | buf1 | argument | test.c:153:12:153:15 | buf1 | | +| test.c:158:5:158:10 | call to strcat | The size of the $@ passed to strcat is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:158:24:158:30 | 12345 | read buffer | test.c:158:12:158:19 | call to get_ca_5 | write buffer | +| test.c:160:5:160:10 | call to strcat | The size of the $@ passed to strcat is 5 bytes, but the size of the $@ is only 4 bytes. | test.c:160:28:160:33 | 1234 | read buffer | test.c:160:12:160:25 | ... + ... | write buffer | +| test.c:183:5:183:11 | call to wcsncat | The size of the $@ passed to wcsncat is 24 bytes, but the size of the $@ is only 5 bytes. | test.c:183:25:183:32 | 12345 | read buffer | test.c:183:13:183:20 | call to get_ca_5 | write buffer | +| test.c:184:5:184:11 | call to wcsncat | The size of the $@ passed to wcsncat is 20 bytes, but the size of the $@ is only 5 bytes. | test.c:184:25:184:31 | 1234 | read buffer | test.c:184:13:184:20 | call to get_ca_5 | write buffer | +| test.c:185:5:185:11 | call to wcsncat | The size of the $@ passed to wcsncat is 20 bytes, but the size of the $@ is only 1 bytes. | test.c:185:29:185:35 | 1234 | read buffer | test.c:185:13:185:26 | ... + ... | write buffer | +| test.c:186:5:186:11 | call to wcsncat | The size of the $@ passed to wcsncat is 12 bytes, but the size of the $@ is only 5 bytes. | test.c:186:25:186:29 | 12 | read buffer | test.c:186:13:186:20 | call to get_ca_5 | write buffer | +| test.c:191:5:191:10 | call to strcmp | The $@ passed to strcmp might not be null-terminated. | test.c:191:22:191:28 | ca5_bad | argument | test.c:191:22:191:28 | ca5_bad | | +| test.c:193:5:193:10 | call to strcmp | The $@ passed to strcmp might not be null-terminated. | test.c:193:12:193:18 | ca5_bad | argument | test.c:193:12:193:18 | ca5_bad | | +| test.c:202:5:202:11 | call to strncmp | The size of the $@ passed to strncmp is 5 bytes, but the $@ is 6 bytes. | test.c:202:13:202:20 | ca5_good | write buffer | test.c:202:32:202:32 | 6 | size argument | +| test.c:202:5:202:11 | call to strncmp | The size of the $@ passed to strncmp is 5 bytes, but the $@ is 6 bytes. | test.c:202:23:202:29 | ca5_bad | read buffer | test.c:202:32:202:32 | 6 | size argument | +| test.c:213:5:213:9 | call to fgets | The size of the $@ passed to fgets is 128 bytes, but the $@ is 129 bytes. | test.c:213:11:213:13 | buf | write buffer | test.c:213:16:213:30 | ... + ... | size argument | +| test.c:216:5:216:9 | call to fgets | The size of the $@ passed to fgets is 127 bytes, but the $@ is 128 bytes. | test.c:216:11:216:17 | ... + ... | write buffer | test.c:216:20:216:30 | sizeof() | size argument | +| test.c:222:5:222:10 | call to fgetws | The size of the $@ passed to fgetws is 512 bytes, but the $@ is 2048 bytes. | test.c:222:12:222:15 | wbuf | write buffer | test.c:222:18:222:29 | sizeof() | size argument | +| test.c:225:5:225:10 | call to fgetws | The size of the $@ passed to fgetws is 512 bytes, but the $@ is 516 bytes. | test.c:225:12:225:15 | wbuf | write buffer | test.c:225:18:225:49 | ... + ... | size argument | +| test.c:228:5:228:10 | call to fgetws | The size of the $@ passed to fgetws is 508 bytes, but the $@ is 512 bytes. | test.c:228:12:228:19 | ... + ... | write buffer | test.c:228:22:228:49 | ... / ... | size argument | +| test.c:237:5:237:12 | call to mbstowcs | The size of the $@ passed to mbstowcs is 512 bytes, but the $@ is 2048 bytes. | test.c:237:14:237:17 | wbuf | write buffer | test.c:237:26:237:37 | sizeof() | size argument | +| test.c:239:5:239:12 | call to mbstowcs | The $@ passed to mbstowcs might not be null-terminated. | test.c:239:20:239:23 | buf2 | argument | test.c:239:20:239:23 | buf2 | | +| test.c:249:5:249:12 | call to wcstombs | The size of the $@ passed to wcstombs is 128 bytes, but the $@ is 512 bytes. | test.c:249:14:249:16 | buf | write buffer | test.c:249:25:249:36 | sizeof() | size argument | +| test.c:249:5:249:12 | call to wcstombs | The size of the $@ passed to wcstombs is 512 bytes, but the size of the $@ is only 128 bytes. | test.c:249:19:249:22 | wbuf | read buffer | test.c:249:14:249:16 | buf | write buffer | +| test.c:252:5:252:12 | call to wcstombs | The size of the $@ passed to wcstombs is 127 bytes, but the $@ is 128 bytes. | test.c:252:14:252:20 | ... + ... | write buffer | test.c:252:33:252:43 | sizeof() | size argument | +| test.c:252:5:252:12 | call to wcstombs | The size of the $@ passed to wcstombs is 508 bytes, but the size of the $@ is only 127 bytes. | test.c:252:23:252:30 | ... + ... | read buffer | test.c:252:14:252:20 | ... + ... | write buffer | +| test.c:261:5:261:10 | call to mbtowc | The size of the $@ passed to mbtowc is 2 bytes, but the $@ is 3 bytes. | test.c:261:16:261:18 | buf | read buffer | test.c:261:21:261:35 | ... + ... | size argument | +| test.c:269:5:269:9 | call to mblen | The size of the $@ passed to mblen is 3 bytes, but the $@ is 4 bytes. | test.c:269:11:269:13 | buf | read buffer | test.c:269:16:269:30 | ... + ... | size argument | +| test.c:270:5:270:9 | call to mblen | The size of the $@ passed to mblen is 5 bytes, but the $@ is 6 bytes. | test.c:270:19:270:24 | call to malloc | read buffer | test.c:270:30:270:44 | ... * ... | size argument | +| test.c:278:5:278:10 | call to memchr | The size of the $@ passed to memchr is 128 bytes, but the $@ is 129 bytes. | test.c:278:12:278:14 | buf | read buffer | test.c:278:20:278:34 | ... + ... | size argument | +| test.c:279:5:279:10 | call to memset | The size of the $@ passed to memset is 128 bytes, but the $@ is 129 bytes. | test.c:279:12:279:14 | buf | write buffer | test.c:279:20:279:34 | ... + ... | size argument | +| test.c:281:5:281:10 | call to memchr | The $@ passed to memchr is null. | test.c:281:12:281:15 | 0 | argument | test.c:281:12:281:15 | 0 | | +| test.c:288:5:288:12 | call to strftime | The size of the $@ passed to strftime is 128 bytes, but the $@ is 129 bytes. | test.c:288:14:288:16 | buf | write buffer | test.c:288:19:288:33 | ... + ... | size argument | +| test.c:290:5:290:12 | call to strftime | The size of the $@ passed to strftime is 127 bytes, but the $@ is 128 bytes. | test.c:290:14:290:20 | ... + ... | write buffer | test.c:290:23:290:33 | sizeof() | size argument | +| test.c:299:5:299:12 | call to wcsftime | The size of the $@ passed to wcsftime is 512 bytes, but the $@ is 520 bytes. | test.c:299:14:299:17 | wbuf | write buffer | test.c:299:20:299:53 | ... + ... | size argument | +| test.c:305:5:305:12 | call to wcsftime | The size of the $@ passed to wcsftime is 508 bytes, but the $@ is 512 bytes. | test.c:305:14:305:21 | ... + ... | write buffer | test.c:305:24:305:53 | ... / ... | size argument | +| test.c:307:5:307:12 | call to wcsftime | The size of the $@ passed to wcsftime is 512 bytes, but the $@ is 2048 bytes. | test.c:307:14:307:17 | wbuf | write buffer | test.c:307:20:307:31 | sizeof() | size argument | +| test.c:315:5:315:11 | call to strxfrm | The size of the $@ passed to strxfrm is 64 bytes, but the $@ is 65 bytes. | test.c:315:13:315:15 | buf | write buffer | test.c:315:25:315:39 | ... + ... | size argument | +| test.c:317:5:317:11 | call to strxfrm | The $@ passed to strxfrm might not be null-terminated. | test.c:317:22:317:25 | buf2 | argument | test.c:317:22:317:25 | buf2 | | +| test.c:326:5:326:11 | call to wcsxfrm | The size of the $@ passed to wcsxfrm is 256 bytes, but the $@ is 260 bytes. | test.c:326:13:326:16 | wbuf | write buffer | test.c:326:27:326:60 | ... + ... | size argument | +| test.c:338:5:338:12 | call to snprintf | The size of the $@ passed to snprintf is 64 bytes, but the $@ is 65 bytes. | test.c:338:14:338:16 | buf | write buffer | test.c:338:19:338:33 | ... + ... | size argument | +| test.c:346:5:346:11 | call to setvbuf | The size of the $@ passed to setvbuf is 64 bytes, but the $@ is 65 bytes. | test.c:346:16:346:18 | buf | read buffer | test.c:346:29:346:43 | ... + ... | size argument | +| test.c:348:5:348:11 | call to setvbuf | The size of the $@ passed to setvbuf is 63 bytes, but the $@ is 64 bytes. | test.c:348:16:348:22 | ... + ... | read buffer | test.c:348:33:348:43 | sizeof() | size argument | +| test.c:362:5:362:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the $@ is 65 bytes. | test.c:362:12:362:14 | buf | write buffer | test.c:362:23:362:37 | ... + ... | size argument | +| test.c:362:5:362:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the $@ is 65 bytes. | test.c:362:17:362:20 | buf2 | read buffer | test.c:362:23:362:37 | ... + ... | size argument | +| test.c:364:5:364:10 | call to memcpy | The size of the $@ passed to memcpy is 63 bytes, but the $@ is 64 bytes. | test.c:364:12:364:18 | ... + ... | write buffer | test.c:364:27:364:37 | sizeof() | size argument | +| test.c:364:5:364:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the size of the $@ is only 63 bytes. | test.c:364:21:364:24 | buf2 | read buffer | test.c:364:12:364:18 | ... + ... | write buffer | +| test.c:365:5:365:10 | call to memcpy | The size of the $@ passed to memcpy is 63 bytes, but the $@ is 128 bytes. | test.c:365:17:365:24 | ... + ... | read buffer | test.c:365:27:365:41 | ... * ... | size argument | +| test.c:365:5:365:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the $@ is 128 bytes. | test.c:365:12:365:14 | buf | write buffer | test.c:365:27:365:41 | ... * ... | size argument | +| test.c:374:5:374:11 | call to wmemcpy | The size of the $@ passed to wmemcpy is 256 bytes, but the $@ is 512 bytes. | test.c:374:22:374:27 | wbuf64 | read buffer | test.c:375:13:375:45 | ... / ... | size argument | +| test.c:377:5:377:11 | call to wmemcpy | The size of the $@ passed to wmemcpy is 252 bytes, but the $@ is 256 bytes. | test.c:377:13:377:22 | ... + ... | write buffer | test.c:378:13:378:44 | ... / ... | size argument | +| test.c:377:5:377:11 | call to wmemcpy | The size of the $@ passed to wmemcpy is 256 bytes, but the size of the $@ is only 252 bytes. | test.c:377:25:377:30 | wbuf64 | read buffer | test.c:377:13:377:22 | ... + ... | write buffer | +| test.c:379:5:379:11 | call to wmemcpy | The size of the $@ passed to wmemcpy is 252 bytes, but the $@ is 256 bytes. | test.c:379:13:379:22 | ... + ... | write buffer | test.c:380:13:380:44 | ... / ... | size argument | +| test.c:379:5:379:11 | call to wmemcpy | The size of the $@ passed to wmemcpy is 252 bytes, but the $@ is 256 bytes. | test.c:379:25:379:34 | ... + ... | read buffer | test.c:380:13:380:44 | ... / ... | size argument | +| test.c:401:5:401:11 | call to bsearch | The $@ passed to bsearch is null. | test.c:401:19:401:22 | 0 | argument | test.c:401:19:401:22 | 0 | | +| test.c:411:5:411:9 | call to qsort | The size of the $@ passed to qsort is 40 bytes, but the $@ is 44 bytes. | test.c:411:11:411:13 | arr | write buffer | test.c:411:16:411:44 | ... + ... | size argument | +| test.c:425:3:425:7 | call to fread | The size of the $@ passed to fread is 64 bytes, but the $@ is 65 bytes. | test.c:425:9:425:11 | buf | write buffer | test.c:425:31:425:31 | 1 | size argument | +| test.c:427:3:427:7 | call to fread | The $@ passed to fread is 64 bytes, but an offset of 64 bytes is used to access it. | test.c:427:9:427:15 | ... + ... | write buffer | test.c:427:9:427:15 | ... + ... | | +| test.c:427:3:427:7 | call to fread | The size of the $@ passed to fread is 0 bytes, but the $@ is 64 bytes. | test.c:427:9:427:15 | ... + ... | write buffer | test.c:427:31:427:31 | 1 | size argument | +| test.c:428:3:428:7 | call to fread | The size of the $@ passed to fread is 64 bytes, but the $@ is 128 bytes. | test.c:428:9:428:11 | buf | write buffer | test.c:428:31:428:31 | 1 | size argument | +| test.c:430:3:430:8 | call to fwrite | The size of the $@ passed to fwrite is 64 bytes, but the $@ is 65 bytes. | test.c:430:10:430:12 | buf | read buffer | test.c:430:32:430:32 | 1 | size argument | +| test.c:432:3:432:8 | call to fwrite | The $@ passed to fwrite is 64 bytes, but an offset of 64 bytes is used to access it. | test.c:432:10:432:16 | ... + ... | read buffer | test.c:432:10:432:16 | ... + ... | | +| test.c:432:3:432:8 | call to fwrite | The size of the $@ passed to fwrite is 0 bytes, but the $@ is 64 bytes. | test.c:432:10:432:16 | ... + ... | read buffer | test.c:432:32:432:32 | 1 | size argument | +| test.c:433:3:433:8 | call to fwrite | The size of the $@ passed to fwrite is 64 bytes, but the $@ is 128 bytes. | test.c:433:10:433:12 | buf | read buffer | test.c:433:32:433:32 | 1 | size argument | +| test.c:464:3:464:8 | call to memcpy | The $@ passed to memcpy is accessed at an excessive offset of 1 element(s) from the $@. | test.c:464:10:464:10 | p | write buffer | test.c:462:21:462:41 | ... * ... | allocation size base | diff --git a/c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.qlref b/c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.qlref new file mode 100644 index 0000000000..ac4dcf9bf7 --- /dev/null +++ b/c/cert/test/rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.qlref @@ -0,0 +1 @@ +rules/ARR38-C/LibraryFunctionArgumentOutOfBounds.ql \ No newline at end of file diff --git a/c/cert/test/rules/ARR38-C/test.c b/c/cert/test/rules/ARR38-C/test.c new file mode 100644 index 0000000000..872b3455e9 --- /dev/null +++ b/c/cert/test/rules/ARR38-C/test.c @@ -0,0 +1,467 @@ +#include +#include +#include +#include +#include + +char *get_ca_5(void) { + void *ptr = malloc(5 * sizeof(char)); + memset(ptr, 0, 5 * sizeof(char)); + return (char *)ptr; +} + +int compare(void *a, void *b) {} + +void test_strings_loop(void) { + char ca5[5] = "test"; // ok + char buf5[5] = {0}; + + for (int i = 0; i < 5; i++) { + strcpy(buf5, ca5); // COMPLIANT + strcpy(buf5 + i, ca5); // NON_COMPLIANT[FALSE_NEGATIVE] + strncpy(buf5, ca5, i); // COMPLIANT + strncpy(buf5, ca5, i + 1); // NON_COMPLIANT[FALSE_NEGATIVE] + } +} + +void test_strings(int flow, int unk_size) { + char ca5_good[5] = "test"; // ok + char ca5_bad[5] = "test1"; // no null terminator + char ca6_good[6] = "test1"; // ok + char ca6_bad[6] = "test12"; // no null terminator + + wchar_t wa5_good[5] = L"test"; // ok + wchar_t wa5_bad[5] = L"test1"; // no null terminator + wchar_t wa6_good[6] = L"test"; // ok + wchar_t wa6_bad[6] = L"test12"; // no null terminator + + // strchr + strchr(ca5_good, 't'); // COMPLIANT + strchr(ca5_bad, 't'); // NON_COMPLIANT + strchr(ca5_good + 4, 't'); // COMPLIANT + strchr(ca5_good + 5, 't'); // NON_COMPLIANT + + if (flow) { + // strcpy from literal + strcpy(ca5_good, "test1"); // NON_COMPLIANT + strcpy(ca5_bad, "test"); // COMPLIANT + } + + if (flow) { + // strcpy to char buffer indirect + strcpy(get_ca_5(), ca5_good); // COMPLIANT + strcpy(get_ca_5(), ca5_bad); // NON_COMPLIANT + strcpy(get_ca_5(), ca6_good); // NON_COMPLIANT + } + + // strcpy between string buffers (must be null-terminated) + if (flow) { + strcpy(ca5_good, ca6_good); + } // NON_COMPLIANT + if (flow) { + strcpy(ca5_good, ca6_bad); + } // NON_COMPLIANT + if (flow) { + strcpy(ca5_bad, ca6_good); + } // NON_COMPLIANT + if (flow) { + strcpy(ca6_bad, ca5_good); + } // COMPLIANT + if (flow) { + strcpy(ca6_bad, ca5_bad); + } // NON_COMPLIANT + if (flow) { + strcpy(get_ca_5(), ca5_good); + } // COMPLIANT + if (flow) { + strcpy(get_ca_5(), ca5_bad); + } // NON_COMPLIANT + if (flow) { + strcpy(get_ca_5(), ca6_good); + } // NON_COMPLIANT + if (flow) { + strcpy(ca5_good, get_ca_5()); + } // NON_COMPLIANT[FALSE_NEGATIVE] + + // strncpy between char buffers (does not have to be null-terminated) + if (flow) { + strncpy(ca5_good, ca6_good, 4); + } // COMPLIANT + if (flow) { + strncpy(ca5_good, ca6_good, 5); + } // COMPLIANT + if (flow) { + strncpy(ca5_good, ca6_bad, 4); + } // COMPLIANT + if (flow) { + strncpy(ca5_good, ca5_good, 5); + } // COMPLIANT + if (flow) { + strncpy(ca5_bad, ca5_bad, 5); + } // COMPLIANT + if (flow) { + strncpy(ca5_bad, ca5_good, 6); + } // NON_COMPLIANT + if (flow) { + strncpy(ca6_bad, ca5_good, 5); + } // COMPLIANT + if (flow) { + strncpy(ca6_bad, ca5_good, 6); + } // COMPLIANT + if (flow) { + strncpy(ca5_good + 1, ca5_good + 2, 3); + } // COMPLIANT + if (flow) { + strncpy(ca5_good + 1, ca5_good + 2, 2); + } // COMPLIANT + + // wrong allocation size + char *p1 = malloc(strlen(ca5_good) + 1); + char *p2 = malloc(strlen(ca5_good)); + + // memcpy with strings and strlen + if (flow) { + memcpy(p1, ca5_good, strlen(ca5_good) + 1); + } // COMPLIANT + if (flow) { + memcpy(p2, ca5_good, strlen(ca5_good) + 1); + } // NON_COMPLIANT + if (flow) { + memcpy(p2 + 1, ca5_good, strlen(ca5_good) - 1); + } // COMPLIANT + if (flow) { + memcpy(p1, ca5_good, strlen(ca5_good)); + } // COMPLIANT - but not terminated + if (flow) { + memcpy(p2, ca5_good, strlen(ca5_good)); + } // COMPLIANT - but not terminated + + // strcat + if (flow) { + char buf0[10]; // memset after first use + char buf1[10]; // no memset + char buf2[10]; // memset before first use + char buf3[10] = {'\0'}; + char buf4[10] = "12345"; + + strcat(buf0, " "); // NON_COMPLIANT[FALSE_NEGATIVE] - not null terminated at + // initialization + + memset(buf0, 0, sizeof(buf0)); // COMPLIANT + memset(buf2, 0, sizeof(buf2)); // COMPLIANT + + strcat(buf1, " "); // NON_COMPLIANT - not null terminated + strcat(buf2, " "); // COMPLIANT + strcat(buf3, " "); // COMPLIANT + strcat(buf4, "12345"); // NON_COMPLIANT + + strcat(get_ca_5(), "12345"); // NON_COMPLIANT + strcat(get_ca_5(), "1234"); // COMPLIANT + strcat(get_ca_5() + 1, "1234"); // NON_COMPLIANT + } + + // wcsncat + if (flow) { + wchar_t buf0[10]; // memset after first use + wchar_t buf1[10]; // no memset + wchar_t buf2[10]; // memset before first use + wchar_t buf3[10] = {L'\0'}; + wchar_t buf4[10] = L"12345"; + + wcsncat(buf0, L" ", + 1); // NON_COMPLIANT[FALSE_NEGATIVE] - not null terminated at + // initialization + + memset(buf0, 0, sizeof(buf0)); // COMPLIANT + memset(buf2, 0, sizeof(buf2)); // COMPLIANT + + wcsncat(buf1, L" ", 1); // NON_COMPLIANT - not null terminated + wcsncat(buf2, L" ", 1); // COMPLIANT + wcsncat(buf3, L" ", 1); // COMPLIANT + wcsncat(buf4, L"12345", 5); // NON_COMPLIANT[FALSE_NEGATIVE] + + wcsncat(get_ca_5(), L"12345", 5); // NON_COMPLIANT + wcsncat(get_ca_5(), L"1234", 4); // NON_COMPLIANT + wcsncat(get_ca_5() + 1, L"1234", 4); // NON_COMPLIANT + wcsncat(get_ca_5(), L"12", 2); // NON_COMPLIANT + } + + // strcmp + if (flow) { + strcmp(ca5_good, ca5_bad); // NON_COMPLIANT + strcmp(ca5_good, ca5_good); // COMPLIANT + strcmp(ca5_bad, ca5_good); // NON_COMPLIANT + strcmp(ca5_good, ca6_good); // COMPLIANT + strcmp(ca6_good, ca5_good); // COMPLIANT + } + + // strncmp + if (flow) { + strncmp(ca5_good, ca5_bad, 4); // COMPLIANT + strncmp(ca5_good, ca5_bad, 5); // COMPLIANT + strncmp(ca5_good, ca5_bad, 6); // NON_COMPLIANT + } +} + +void test_wrong_buf_size(void) { + + // fgets + { + char buf[128]; + fgets(buf, sizeof(buf), stdin); // COMPLIANT + fgets(buf, sizeof(buf) - 1, stdin); // COMPLIANT + fgets(buf, sizeof(buf) + 1, stdin); // NON_COMPLIANT + fgets(buf, 0, stdin); // COMPLIANT + fgets(buf + 1, sizeof(buf) - 1, stdin); // COMPLIANT + fgets(buf + 1, sizeof(buf), stdin); // NON_COMPLIANT + } + + // fgetws + { + wchar_t wbuf[128]; + fgetws(wbuf, sizeof(wbuf), stdin); // NON_COMPLIANT + fgetws(wbuf, sizeof(wbuf) / sizeof(*wbuf), stdin); // COMPLIANT + fgetws(wbuf, sizeof(wbuf) / sizeof(*wbuf) - 1, stdin); // COMPLIANT + fgetws(wbuf, sizeof(wbuf) / sizeof(*wbuf) + 1, stdin); // NON_COMPLIANT + fgetws(wbuf, 0, stdin); // COMPLIANT + fgetws(wbuf + 1, sizeof(wbuf) / sizeof(*wbuf) - 2, stdin); // COMPLIANT + fgetws(wbuf + 1, sizeof(wbuf) / sizeof(*wbuf), stdin); // NON_COMPLIANT + } + + // mbstowcs + { + char buf1[128] = {0}; + char buf2[128]; + wchar_t wbuf[128]; + + mbstowcs(wbuf, buf1, sizeof(wbuf)); // NON_COMPLIANT - count too large + mbstowcs(wbuf, buf1, sizeof(buf1)); // COMPLIANT - but wrong arithmetic + mbstowcs(wbuf, buf2, + sizeof(wbuf) / + sizeof(wchar_t)); // NON_COMPLIANT - not null-terminated + mbstowcs(wbuf, buf1, sizeof(wbuf) / sizeof(wchar_t)); // COMPLIANT + } + + // wcstombs + { + char buf[128]; + wchar_t wbuf[128] = {0}; + wcstombs(buf, wbuf, sizeof(wbuf)); // NON_COMPLIANT - count too large + wcstombs(buf, wbuf, sizeof(buf)); // COMPLIANT + wcstombs(buf + 1, wbuf + 1, sizeof(buf) - 1); // COMPLIANT + wcstombs(buf + 1, wbuf + 1, sizeof(buf)); // NON_COMPLIANT + } + + // mbtowc + { + wchar_t c; + char buf[2] = {0}; + mbtowc(&c, buf, sizeof(buf)); // COMPLIANT + mbtowc(&c, buf, sizeof(buf) - 1); // COMPLIANT + mbtowc(&c, buf, sizeof(buf) + 1); // NON_COMPLIANT + mbtowc(NULL, NULL, 0); // COMPLIANT - exception + } + + // mblen + { + char buf[3] = {0}; + mblen(buf, sizeof(buf)); // COMPLIANT + mblen(buf, sizeof(buf) + 1); // NON_COMPLIANT + mblen((char *)malloc(5), sizeof(buf) * 2); // NON_COMPLIANT + mblen(NULL, 0); // COMPLIANT - exception + } + + // memchr, memset + { + char buf[128]; + memchr(buf, 0, sizeof(buf)); // COMPLIANT + memchr(buf, 0, sizeof(buf) + 1); // NON_COMPLIANT + memset(buf, 0, sizeof(buf) + 1); // NON_COMPLIANT + memchr(buf, 0, sizeof(buf) - 1); // COMPLIANT + memchr(NULL, 0, sizeof(buf)); // NON_COMPLIANT + } + + // strftime + { + char buf[128]; + strftime(buf, sizeof(buf), "%Y-%m-%d", NULL); // COMPLIANT + strftime(buf, sizeof(buf) + 1, "%Y-%m-%d", NULL); // NON_COMPLIANT + strftime(buf, sizeof(buf) - 1, "%Y-%m-%d", NULL); // COMPLIANT + strftime(buf + 1, sizeof(buf), "%Y-%m-%d", NULL); // NON_COMPLIANT + } + + // wcsftime + { + wchar_t wbuf[128] = {0}; + wchar_t format_bad[8] = L"%Y-%m-%d"; + wcsftime(wbuf, sizeof(wbuf) / sizeof(wchar_t), L"%Y-%m-%d", + NULL); // COMPLIANT + wcsftime(wbuf, sizeof(wbuf) / sizeof(wchar_t) + 2, L"%Y-%m-%d", + NULL); // NON_COMPLIANT + wcsftime(wbuf, sizeof(wbuf) / sizeof(wchar_t) - 2, L"%Y-%m-%d", + NULL); // COMPLIANT + wcsftime(wbuf, sizeof(wbuf) / sizeof(wchar_t), format_bad, + NULL); // NON_COMPLIANT + wcsftime(wbuf + 1, sizeof(wbuf) / sizeof(wchar_t), L"%Y-%m-%d", + NULL); // NON_COMPLIANT + wcsftime(wbuf, sizeof(wbuf), L"%Y-%m-%d", NULL); // NON_COMPLIANT + } + + // strxfrm + { + char buf[64]; + char buf2[128]; + strxfrm(buf, "abc", sizeof(buf)); // COMPLIANT + strxfrm(buf, "abc", sizeof(buf) + 1); // NON_COMPLIANT + strxfrm(buf, "abc", sizeof(buf) - 1); // COMPLIANT + strxfrm(buf + 1, buf2, + sizeof(buf) - 1); // NON_COMPLIANT - not null terminated + } + + // wcsxfrm + { + wchar_t wbuf[64]; + wchar_t wbuf2[128]; + wcsxfrm(wbuf, L"abc", sizeof(wbuf) / sizeof(wchar_t)); // COMPLIANT + wcsxfrm(wbuf, L"abc", sizeof(wbuf) / sizeof(wchar_t) + 1); // NON_COMPLIANT + wcsxfrm(wbuf, L"abc", sizeof(wbuf) / sizeof(wchar_t) - 1); // COMPLIANT + wcsxfrm(wbuf + 1, wbuf2, sizeof(wbuf) / sizeof(wchar_t) - 1); // COMPLIANT + } + + // snprintf (and vsnprintf, swprintf, vswprintf) + { + char str_bad[2] = "12"; + char buf[64]; + snprintf(buf, sizeof(buf), "%s", ""); // COMPLIANT + snprintf(buf, sizeof(buf), "%s", + str_bad); // NON_COMPLIANT[FALSE_NEGATIVE] - not checked + snprintf(buf, sizeof(buf) + 1, "test"); // NON_COMPLIANT + } + + // setvbuf + { + FILE *f; + char buf[64]; + setvbuf(f, buf, _IOFBF, sizeof(buf)); // COMPLIANT + setvbuf(f, buf, _IOFBF, sizeof(buf) + 1); // NON_COMPLIANT + setvbuf(f, buf, _IOFBF, sizeof(buf) - 1); // COMPLIANT + setvbuf(f, buf + 1, _IOFBF, sizeof(buf)); // NON_COMPLIANT + setvbuf(f, NULL, _IOFBF, 0); // COMPLIANT - exception + } + + // "memcpy", "wmemcpy", "memmove", "wmemmove", "memcmp", "wmemcmp" + + // memcpy + { + char buf[64]; + char buf2[64]; + wchar_t wbuf[64]; + wchar_t wbuf2[64]; + + memcpy(buf, buf2, sizeof(buf)); // COMPLIANT + memcpy(buf, buf2, sizeof(buf) + 1); // NON_COMPLIANT + memcpy(buf, buf2, sizeof(buf) - 1); // COMPLIANT + memcpy(buf + 1, buf2, sizeof(buf)); // NON_COMPLIANT + memcpy(buf, buf2 + 1, sizeof(buf) * 2); // NON_COMPLIANT + } + + // wmemcpy + { + wchar_t wbuf128[128]; + wchar_t wbuf64[64]; + + wmemcpy(wbuf128, wbuf64, sizeof(wbuf64) / sizeof(wchar_t)); // COMPLIANT + wmemcpy(wbuf128, wbuf64, + sizeof(wbuf128) / sizeof(wchar_t)); // NON_COMPLIANT + wmemcpy(wbuf128, wbuf64, sizeof(wbuf64) / sizeof(wchar_t) - 1); // COMPLIANT + wmemcpy(wbuf64 + 1, wbuf64, + sizeof(wbuf64) / sizeof(wchar_t)); // NON_COMPLIANT + wmemcpy(wbuf64 + 1, wbuf64 + 1, + sizeof(wbuf64) / sizeof(wchar_t)); // NON_COMPLIANT + wmemcpy(wbuf64 + 1, wbuf64 + 1, + sizeof(wbuf64) / sizeof(wchar_t) - 1); // NON_COMPLIANT + wmemcpy(wbuf64 + 1, wbuf64 + 1, + sizeof(wbuf64) / sizeof(wchar_t) - 2); // COMPLIANT + } + + // bsearch + { + int arr[10]; + int key = 0; + bsearch(&key, arr, sizeof(arr) / sizeof(int), sizeof(int), + compare); // COMPLIANT + bsearch(&key, arr, sizeof(arr) / sizeof(int) + 1, sizeof(int), + compare); // NON_COMPLIANT + bsearch(&key, arr, sizeof(arr) / sizeof(int) - 1, sizeof(int), + compare); // COMPLIANT + bsearch(&key, arr + 1, sizeof(arr) / sizeof(int) - 1, sizeof(int), + compare); // NON_COMPLIANT + bsearch(NULL, arr, sizeof(arr) / sizeof(int), sizeof(int), + compare); // NON_COMPLIANT + bsearch(&key, NULL, sizeof(arr) / sizeof(int), sizeof(int), + compare); // NON_COMPLIANT + bsearch(&key, arr, sizeof(arr) / sizeof(int), sizeof(int), + NULL); // NON_COMPLIANT + } + + // qsort + { + int arr[10]; + qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), compare); // COMPLIANT + qsort(arr, sizeof(arr) / sizeof(int) + 1, sizeof(int), + compare); // NON_COMPLIANT + qsort(arr, sizeof(arr) / sizeof(int) - 1, sizeof(int), + compare); // COMPLIANT + qsort(arr + 1, sizeof(arr) / sizeof(int) - 1, sizeof(int), + compare); // NON_COMPLIANT + qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), NULL); // NON_COMPLIANT + } +} + +void test_fread_fwrite_static(char *file_name) { + FILE *f = fopen(file_name, "r"); + char buf[64]; + fread(buf, sizeof(buf), 1, f); // COMPLIANT + fread(buf, sizeof(buf) + 1, 1, f); // NON_COMPLIANT + fread(buf, sizeof(buf) - 1, 1, f); // COMPLIANT + fread(buf + 1, sizeof(buf), 1, f); // NON_COMPLIANT + fread(buf, sizeof(buf) * 2, 1, f); // NON_COMPLIANT + fwrite(buf, sizeof(buf), 1, f); // COMPLIANT + fwrite(buf, sizeof(buf) + 1, 1, f); // NON_COMPLIANT + fwrite(buf, sizeof(buf) - 1, 1, f); // COMPLIANT + fwrite(buf + 1, sizeof(buf), 1, f); // NON_COMPLIANT + fwrite(buf, sizeof(buf) * 2, 1, f); // NON_COMPLIANT + fclose(f); +} + +void test_read_file(const char *file_name) { + FILE *f = fopen(file_name, "rb"); + + fseek(f, 0, SEEK_END); + long len = ftell(f); + rewind(f); + + char *buf = malloc(len + 1); + + // not correct behaviour below but suffices to test overflow + rewind(f); + fread(buf + 1, len - 1, 1, f); // COMPLIANT + rewind(f); + fread(buf + 1, len, 1, f); // COMPLIANT + rewind(f); + fread(buf + 1, len + 1, 1, f); // COMPLIANT + rewind(f); + fread(buf + 1, len + 2, 1, f); // COMPLIANT + rewind(f); + fread(buf + 1, len + 3, 1, f); // NON_COMPLIANT + + fclose(f); +} + +void test_equivalent_expressions(void *in, int x, int y, int a, int b) { + short *p = malloc(x * y * sizeof(short)); + memcpy(p, in, x * y * sizeof(short)); // COMPLIANT + memcpy(p, in, x * y * sizeof(short) + 1); // NON_COMPLIANT + memcpy(p, in, x * y * sizeof(short) - 1); // COMPLIANT + memcpy(p, in, a * b * sizeof(short) + 1); // COMPLIANT - unknown +} \ No newline at end of file diff --git a/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected index 1d3f5dcf13..0a6471deac 100644 --- a/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected +++ b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected @@ -1,9 +1,13 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAddOrSubtractAScaledIntegerToAPointer.ql:77,56-64) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAddOrSubtractAScaledIntegerToAPointer.ql:78,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAddOrSubtractAScaledIntegerToAPointer.ql:80,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAddOrSubtractAScaledIntegerToAPointer.ql:89,45-53) edges -| test.c:7:13:7:14 | p1 | test.c:9:9:9:10 | p1 | -| test.c:16:19:16:41 | ... - ... | test.c:18:26:18:31 | offset | -| test.c:16:19:16:41 | ... - ... | test.c:29:6:29:11 | offset | -| test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | -| test.c:29:6:29:11 | offset | test.c:7:13:7:14 | p1 | +| test.c:7:13:7:14 | p1 | test.c:9:9:9:10 | p1 | provenance | | +| test.c:16:19:16:41 | ... - ... | test.c:18:26:18:31 | offset | provenance | | +| test.c:16:19:16:41 | ... - ... | test.c:29:6:29:11 | offset | provenance | | +| test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | provenance | | +| test.c:29:6:29:11 | offset | test.c:7:13:7:14 | p1 | provenance | | nodes | test.c:7:13:7:14 | p1 | semmle.label | p1 | | test.c:9:9:9:10 | p1 | semmle.label | p1 | diff --git a/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.clang b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.clang new file mode 100644 index 0000000000..17f9312a38 --- /dev/null +++ b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.clang @@ -0,0 +1,23 @@ +edges +| test.c:7:13:7:14 | p1 | test.c:9:9:9:10 | p1 | +| test.c:16:19:16:41 | __builtin_offsetof | test.c:18:26:18:31 | offset | +| test.c:16:19:16:41 | __builtin_offsetof | test.c:29:6:29:11 | offset | +| test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | +| test.c:29:6:29:11 | offset | test.c:7:13:7:14 | p1 | +nodes +| test.c:7:13:7:14 | p1 | semmle.label | p1 | +| test.c:9:9:9:10 | p1 | semmle.label | p1 | +| test.c:16:19:16:41 | __builtin_offsetof | semmle.label | __builtin_offsetof | +| test.c:17:17:17:26 | sizeof() | semmle.label | sizeof() | +| test.c:18:26:18:31 | offset | semmle.label | offset | +| test.c:23:9:23:12 | size | semmle.label | size | +| test.c:25:9:25:18 | sizeof() | semmle.label | sizeof() | +| test.c:27:17:27:26 | sizeof() | semmle.label | sizeof() | +| test.c:29:6:29:11 | offset | semmle.label | offset | +subpaths +#select +| test.c:9:9:9:10 | p1 | test.c:16:19:16:41 | __builtin_offsetof | test.c:9:9:9:10 | p1 | Scaled integer used in pointer arithmetic. | +| test.c:18:26:18:31 | offset | test.c:16:19:16:41 | __builtin_offsetof | test.c:18:26:18:31 | offset | Scaled integer used in pointer arithmetic. | +| test.c:23:9:23:12 | size | test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | Scaled integer used in pointer arithmetic. | +| test.c:25:9:25:18 | sizeof() | test.c:25:9:25:18 | sizeof() | test.c:25:9:25:18 | sizeof() | Scaled integer used in pointer arithmetic. | +| test.c:27:17:27:26 | sizeof() | test.c:27:17:27:26 | sizeof() | test.c:27:17:27:26 | sizeof() | Scaled integer used in pointer arithmetic. | diff --git a/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.gcc b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.gcc new file mode 100644 index 0000000000..17f9312a38 --- /dev/null +++ b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.gcc @@ -0,0 +1,23 @@ +edges +| test.c:7:13:7:14 | p1 | test.c:9:9:9:10 | p1 | +| test.c:16:19:16:41 | __builtin_offsetof | test.c:18:26:18:31 | offset | +| test.c:16:19:16:41 | __builtin_offsetof | test.c:29:6:29:11 | offset | +| test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | +| test.c:29:6:29:11 | offset | test.c:7:13:7:14 | p1 | +nodes +| test.c:7:13:7:14 | p1 | semmle.label | p1 | +| test.c:9:9:9:10 | p1 | semmle.label | p1 | +| test.c:16:19:16:41 | __builtin_offsetof | semmle.label | __builtin_offsetof | +| test.c:17:17:17:26 | sizeof() | semmle.label | sizeof() | +| test.c:18:26:18:31 | offset | semmle.label | offset | +| test.c:23:9:23:12 | size | semmle.label | size | +| test.c:25:9:25:18 | sizeof() | semmle.label | sizeof() | +| test.c:27:17:27:26 | sizeof() | semmle.label | sizeof() | +| test.c:29:6:29:11 | offset | semmle.label | offset | +subpaths +#select +| test.c:9:9:9:10 | p1 | test.c:16:19:16:41 | __builtin_offsetof | test.c:9:9:9:10 | p1 | Scaled integer used in pointer arithmetic. | +| test.c:18:26:18:31 | offset | test.c:16:19:16:41 | __builtin_offsetof | test.c:18:26:18:31 | offset | Scaled integer used in pointer arithmetic. | +| test.c:23:9:23:12 | size | test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | Scaled integer used in pointer arithmetic. | +| test.c:25:9:25:18 | sizeof() | test.c:25:9:25:18 | sizeof() | test.c:25:9:25:18 | sizeof() | Scaled integer used in pointer arithmetic. | +| test.c:27:17:27:26 | sizeof() | test.c:27:17:27:26 | sizeof() | test.c:27:17:27:26 | sizeof() | Scaled integer used in pointer arithmetic. | diff --git a/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.qcc b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.qcc new file mode 100644 index 0000000000..17f9312a38 --- /dev/null +++ b/c/cert/test/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.expected.qcc @@ -0,0 +1,23 @@ +edges +| test.c:7:13:7:14 | p1 | test.c:9:9:9:10 | p1 | +| test.c:16:19:16:41 | __builtin_offsetof | test.c:18:26:18:31 | offset | +| test.c:16:19:16:41 | __builtin_offsetof | test.c:29:6:29:11 | offset | +| test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | +| test.c:29:6:29:11 | offset | test.c:7:13:7:14 | p1 | +nodes +| test.c:7:13:7:14 | p1 | semmle.label | p1 | +| test.c:9:9:9:10 | p1 | semmle.label | p1 | +| test.c:16:19:16:41 | __builtin_offsetof | semmle.label | __builtin_offsetof | +| test.c:17:17:17:26 | sizeof() | semmle.label | sizeof() | +| test.c:18:26:18:31 | offset | semmle.label | offset | +| test.c:23:9:23:12 | size | semmle.label | size | +| test.c:25:9:25:18 | sizeof() | semmle.label | sizeof() | +| test.c:27:17:27:26 | sizeof() | semmle.label | sizeof() | +| test.c:29:6:29:11 | offset | semmle.label | offset | +subpaths +#select +| test.c:9:9:9:10 | p1 | test.c:16:19:16:41 | __builtin_offsetof | test.c:9:9:9:10 | p1 | Scaled integer used in pointer arithmetic. | +| test.c:18:26:18:31 | offset | test.c:16:19:16:41 | __builtin_offsetof | test.c:18:26:18:31 | offset | Scaled integer used in pointer arithmetic. | +| test.c:23:9:23:12 | size | test.c:17:17:17:26 | sizeof() | test.c:23:9:23:12 | size | Scaled integer used in pointer arithmetic. | +| test.c:25:9:25:18 | sizeof() | test.c:25:9:25:18 | sizeof() | test.c:25:9:25:18 | sizeof() | Scaled integer used in pointer arithmetic. | +| test.c:27:17:27:26 | sizeof() | test.c:27:17:27:26 | sizeof() | test.c:27:17:27:26 | sizeof() | Scaled integer used in pointer arithmetic. | diff --git a/c/cert/test/rules/ARR39-C/test.c b/c/cert/test/rules/ARR39-C/test.c index 937dce9cf5..db8c54fa14 100644 --- a/c/cert/test/rules/ARR39-C/test.c +++ b/c/cert/test/rules/ARR39-C/test.c @@ -27,4 +27,5 @@ void f1() { (void)v1[10 / sizeof(v1)]; // NON_COMPLIANT v4 += offset; // COMPLIANT f2(offset, 2); -} \ No newline at end of file +} +// \ No newline at end of file diff --git a/c/cert/test/rules/CON30-C/CleanUpThreadSpecificStorage.expected b/c/cert/test/rules/CON30-C/CleanUpThreadSpecificStorage.expected index e03b665a1c..f3ea87136a 100644 --- a/c/cert/test/rules/CON30-C/CleanUpThreadSpecificStorage.expected +++ b/c/cert/test/rules/CON30-C/CleanUpThreadSpecificStorage.expected @@ -1,3 +1,9 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CleanUpThreadSpecificStorage.ql:25,46-54) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CleanUpThreadSpecificStorage.ql:26,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CleanUpThreadSpecificStorage.ql:35,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CleanUpThreadSpecificStorage.ql:45,35-43) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CleanUpThreadSpecificStorage.ql:53,36-44) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CleanUpThreadSpecificStorage.ql:55,36-44) | test.c:27:3:27:12 | call to tss_create | Resources used by thread specific storage may not be cleaned up. | | test.c:49:3:49:12 | call to tss_create | Resources used by thread specific storage may not be cleaned up. | | test.c:71:3:71:12 | call to tss_create | Resources used by thread specific storage may not be cleaned up. | diff --git a/c/cert/test/rules/CON34-C/AppropriateThreadObjectStorageDurations.expected b/c/cert/test/rules/CON34-C/AppropriateThreadObjectStorageDurations.expected index c3cdc8bd7b..2cd844f81b 100644 --- a/c/cert/test/rules/CON34-C/AppropriateThreadObjectStorageDurations.expected +++ b/c/cert/test/rules/CON34-C/AppropriateThreadObjectStorageDurations.expected @@ -1,3 +1,16 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:35,14-22) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:37,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:39,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:42,45-53) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:52,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:52,58-66) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:53,42-50) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:56,9-17) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:56,34-42) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:57,9-17) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:57,34-42) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:42,9-22) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (AppropriateThreadObjectStorageDurations.ql:52,7-20) | test.c:23:3:23:13 | call to thrd_create | $@ not declared with appropriate storage duration | test.c:23:24:23:29 | & ... | Shared object | | test.c:74:3:74:13 | call to thrd_create | $@ not declared with appropriate storage duration | test.c:74:24:74:24 | p | Shared object | | test.c:85:3:85:13 | call to thrd_create | $@ not declared with appropriate storage duration | test.c:85:24:85:24 | p | Shared object | diff --git a/c/cert/test/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.expected b/c/cert/test/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.expected index 95d0a20041..b2ac853fbf 100644 --- a/c/cert/test/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.expected +++ b/c/cert/test/rules/CON34-C/ThreadObjectStorageDurationsNotInitialized.expected @@ -1 +1,6 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ThreadObjectStorageDurationsNotInitialized.ql:32,38-46) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ThreadObjectStorageDurationsNotInitialized.ql:35,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ThreadObjectStorageDurationsNotInitialized.ql:35,30-38) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ThreadObjectStorageDurationsNotInitialized.ql:36,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ThreadObjectStorageDurationsNotInitialized.ql:36,30-38) | test.c:14:7:14:13 | call to tss_get | Call to a thread specific storage function from within a threaded context on an object that may not be owned by this thread. | diff --git a/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.qlref b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.qlref deleted file mode 100644 index 5daa5a5046..0000000000 --- a/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql \ No newline at end of file diff --git a/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.testref b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.testref new file mode 100644 index 0000000000..61fa88fd08 --- /dev/null +++ b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.testref @@ -0,0 +1 @@ +c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.ql \ No newline at end of file diff --git a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected index 377d6cc818..42d3ea924d 100644 --- a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected +++ b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected @@ -1,6 +1,6 @@ -| test.c:6:19:6:40 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:32:3:32:10 | ... += ... | expression | -| test.c:6:19:6:40 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:33:3:33:13 | ... = ... | expression | -| test.c:10:3:10:23 | atomic_store(a,b) | Atomic variable possibly referred to twice in an $@. | test.c:10:3:10:23 | atomic_store(a,b) | expression | -| test.c:11:3:11:35 | atomic_store_explicit(a,b,c) | Atomic variable possibly referred to twice in an $@. | test.c:11:3:11:35 | atomic_store_explicit(a,b,c) | expression | -| test.c:24:3:24:48 | atomic_compare_exchange_weak(a,b,c) | Atomic variable possibly referred to twice in an $@. | test.c:24:3:24:48 | atomic_compare_exchange_weak(a,b,c) | expression | -| test.c:25:3:26:45 | atomic_compare_exchange_weak_explicit(a,b,c,d,e) | Atomic variable possibly referred to twice in an $@. | test.c:25:3:26:45 | atomic_compare_exchange_weak_explicit(a,b,c,d,e) | expression | +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:33:3:33:10 | ... += ... | expression | +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:34:3:34:13 | ... = ... | expression | +| test.c:11:3:11:23 | atomic_store(object,desired) | Atomic variable possibly referred to twice in an $@. | test.c:11:3:11:23 | atomic_store(object,desired) | expression | +| test.c:12:3:12:23 | atomic_store_explicit | Atomic variable possibly referred to twice in an $@. | test.c:12:3:12:23 | atomic_store_explicit | expression | +| test.c:25:3:25:49 | atomic_compare_exchange_weak(object,expected,desired) | Atomic variable possibly referred to twice in an $@. | test.c:25:3:25:49 | atomic_compare_exchange_weak(object,expected,desired) | expression | +| test.c:26:3:26:39 | atomic_compare_exchange_weak_explicit | Atomic variable possibly referred to twice in an $@. | test.c:26:3:26:39 | atomic_compare_exchange_weak_explicit | expression | diff --git a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.clang b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.clang new file mode 100644 index 0000000000..42d3ea924d --- /dev/null +++ b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.clang @@ -0,0 +1,6 @@ +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:33:3:33:10 | ... += ... | expression | +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:34:3:34:13 | ... = ... | expression | +| test.c:11:3:11:23 | atomic_store(object,desired) | Atomic variable possibly referred to twice in an $@. | test.c:11:3:11:23 | atomic_store(object,desired) | expression | +| test.c:12:3:12:23 | atomic_store_explicit | Atomic variable possibly referred to twice in an $@. | test.c:12:3:12:23 | atomic_store_explicit | expression | +| test.c:25:3:25:49 | atomic_compare_exchange_weak(object,expected,desired) | Atomic variable possibly referred to twice in an $@. | test.c:25:3:25:49 | atomic_compare_exchange_weak(object,expected,desired) | expression | +| test.c:26:3:26:39 | atomic_compare_exchange_weak_explicit | Atomic variable possibly referred to twice in an $@. | test.c:26:3:26:39 | atomic_compare_exchange_weak_explicit | expression | diff --git a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.gcc b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.gcc new file mode 100644 index 0000000000..7a37b9424d --- /dev/null +++ b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.gcc @@ -0,0 +1,6 @@ +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(VALUE) | Atomic variable possibly referred to twice in an $@. | test.c:33:3:33:10 | ... += ... | expression | +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(VALUE) | Atomic variable possibly referred to twice in an $@. | test.c:34:3:34:13 | ... = ... | expression | +| test.c:11:3:11:23 | atomic_store(PTR,VAL) | Atomic variable possibly referred to twice in an $@. | test.c:11:3:11:23 | atomic_store(PTR,VAL) | expression | +| test.c:12:3:12:35 | atomic_store_explicit(PTR,VAL,MO) | Atomic variable possibly referred to twice in an $@. | test.c:12:3:12:35 | atomic_store_explicit(PTR,VAL,MO) | expression | +| test.c:25:3:25:49 | atomic_compare_exchange_weak(PTR,VAL,DES) | Atomic variable possibly referred to twice in an $@. | test.c:25:3:25:49 | atomic_compare_exchange_weak(PTR,VAL,DES) | expression | +| test.c:26:3:27:42 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | Atomic variable possibly referred to twice in an $@. | test.c:26:3:27:42 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | expression | diff --git a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.qcc b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.qcc new file mode 100644 index 0000000000..7a37b9424d --- /dev/null +++ b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected.qcc @@ -0,0 +1,6 @@ +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(VALUE) | Atomic variable possibly referred to twice in an $@. | test.c:33:3:33:10 | ... += ... | expression | +| test.c:7:18:7:39 | ATOMIC_VAR_INIT(VALUE) | Atomic variable possibly referred to twice in an $@. | test.c:34:3:34:13 | ... = ... | expression | +| test.c:11:3:11:23 | atomic_store(PTR,VAL) | Atomic variable possibly referred to twice in an $@. | test.c:11:3:11:23 | atomic_store(PTR,VAL) | expression | +| test.c:12:3:12:35 | atomic_store_explicit(PTR,VAL,MO) | Atomic variable possibly referred to twice in an $@. | test.c:12:3:12:35 | atomic_store_explicit(PTR,VAL,MO) | expression | +| test.c:25:3:25:49 | atomic_compare_exchange_weak(PTR,VAL,DES) | Atomic variable possibly referred to twice in an $@. | test.c:25:3:25:49 | atomic_compare_exchange_weak(PTR,VAL,DES) | expression | +| test.c:26:3:27:42 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | Atomic variable possibly referred to twice in an $@. | test.c:26:3:27:42 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | expression | diff --git a/c/cert/test/rules/CON40-C/test.c b/c/cert/test/rules/CON40-C/test.c index a72a9d4809..20a381acc3 100644 --- a/c/cert/test/rules/CON40-C/test.c +++ b/c/cert/test/rules/CON40-C/test.c @@ -1,10 +1,11 @@ #include #include -static bool fl1 = ATOMIC_VAR_INIT(false); -static bool fl2 = ATOMIC_VAR_INIT(false); -static bool fl3 = ATOMIC_VAR_INIT(false); -static bool fl4 = ATOMIC_VAR_INIT(false); +static _Atomic int fl1 = ATOMIC_VAR_INIT(false); +static _Atomic int fl2 = ATOMIC_VAR_INIT(false); +static int fl2a = ATOMIC_VAR_INIT(false); +static int fl3 = ATOMIC_VAR_INIT(false); +static int fl4 = ATOMIC_VAR_INIT(false); void f1() { atomic_store(&fl1, 0); // NON_COMPLIANT @@ -13,17 +14,17 @@ void f1() { void f2() { do { - } while (!atomic_compare_exchange_weak(&fl2, &fl2, &fl2)); // COMPLIANT + } while (!atomic_compare_exchange_weak(&fl2, &fl2a, fl2a)); // COMPLIANT do { - } while (!atomic_compare_exchange_weak_explicit(&fl2, &fl2, &fl2, &fl2, - &fl2)); // COMPLIANT + } while (!atomic_compare_exchange_weak_explicit(&fl2, &fl2a, fl2a, 0, + 0)); // COMPLIANT } void f3() { - atomic_compare_exchange_weak(&fl2, &fl2, &fl2); // NON_COMPLIANT - atomic_compare_exchange_weak_explicit(&fl2, &fl2, &fl2, &fl2, - &fl2); // NON_COMPLIANT + atomic_compare_exchange_weak(&fl2, &fl2a, fl2a); // NON_COMPLIANT + atomic_compare_exchange_weak_explicit(&fl2, &fl2a, fl2a, 0, + 0); // NON_COMPLIANT } void f4() { fl3 ^= true; } diff --git a/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected index e376acecbf..b1c224173e 100644 --- a/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected +++ b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected @@ -1,4 +1,4 @@ -| test.c:5:8:5:46 | atomic_compare_exchange_weak(a,b,c) | Function that can spuriously fail not wrapped in a loop. | -| test.c:9:3:9:41 | atomic_compare_exchange_weak(a,b,c) | Function that can spuriously fail not wrapped in a loop. | -| test.c:11:8:12:47 | atomic_compare_exchange_weak_explicit(a,b,c,d,e) | Function that can spuriously fail not wrapped in a loop. | -| test.c:16:3:16:56 | atomic_compare_exchange_weak_explicit(a,b,c,d,e) | Function that can spuriously fail not wrapped in a loop. | +| test.c:6:8:6:46 | atomic_compare_exchange_weak(object,expected,desired) | Function that can spuriously fail not wrapped in a loop. | +| test.c:10:3:10:41 | atomic_compare_exchange_weak(object,expected,desired) | Function that can spuriously fail not wrapped in a loop. | +| test.c:12:8:12:44 | atomic_compare_exchange_weak_explicit | Function that can spuriously fail not wrapped in a loop. | +| test.c:17:3:17:39 | atomic_compare_exchange_weak_explicit | Function that can spuriously fail not wrapped in a loop. | diff --git a/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.clang b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.clang new file mode 100644 index 0000000000..b1c224173e --- /dev/null +++ b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.clang @@ -0,0 +1,4 @@ +| test.c:6:8:6:46 | atomic_compare_exchange_weak(object,expected,desired) | Function that can spuriously fail not wrapped in a loop. | +| test.c:10:3:10:41 | atomic_compare_exchange_weak(object,expected,desired) | Function that can spuriously fail not wrapped in a loop. | +| test.c:12:8:12:44 | atomic_compare_exchange_weak_explicit | Function that can spuriously fail not wrapped in a loop. | +| test.c:17:3:17:39 | atomic_compare_exchange_weak_explicit | Function that can spuriously fail not wrapped in a loop. | diff --git a/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.gcc b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.gcc new file mode 100644 index 0000000000..56c78a0189 --- /dev/null +++ b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.gcc @@ -0,0 +1,4 @@ +| test.c:6:8:6:46 | atomic_compare_exchange_weak(PTR,VAL,DES) | Function that can spuriously fail not wrapped in a loop. | +| test.c:10:3:10:41 | atomic_compare_exchange_weak(PTR,VAL,DES) | Function that can spuriously fail not wrapped in a loop. | +| test.c:12:8:13:47 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | Function that can spuriously fail not wrapped in a loop. | +| test.c:17:3:17:56 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | Function that can spuriously fail not wrapped in a loop. | diff --git a/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.qcc b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.qcc new file mode 100644 index 0000000000..56c78a0189 --- /dev/null +++ b/c/cert/test/rules/CON41-C/WrapFunctionsThatCanFailSpuriouslyInLoop.expected.qcc @@ -0,0 +1,4 @@ +| test.c:6:8:6:46 | atomic_compare_exchange_weak(PTR,VAL,DES) | Function that can spuriously fail not wrapped in a loop. | +| test.c:10:3:10:41 | atomic_compare_exchange_weak(PTR,VAL,DES) | Function that can spuriously fail not wrapped in a loop. | +| test.c:12:8:13:47 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | Function that can spuriously fail not wrapped in a loop. | +| test.c:17:3:17:56 | atomic_compare_exchange_weak_explicit(PTR,VAL,DES,SUC,FAIL) | Function that can spuriously fail not wrapped in a loop. | diff --git a/c/cert/test/rules/CON41-C/test.c b/c/cert/test/rules/CON41-C/test.c index 58cb4d0c18..960fc00bfb 100644 --- a/c/cert/test/rules/CON41-C/test.c +++ b/c/cert/test/rules/CON41-C/test.c @@ -1,7 +1,8 @@ #include "stdatomic.h" void f1() { - int a, b, c; + _Atomic int a; + int b, c; if (!atomic_compare_exchange_weak(&a, &b, c)) { // NON_COMPLIANT (void)0; /* no-op */ } @@ -17,7 +18,8 @@ void f1() { } void f2() { - int a, b, c; + _Atomic int a; + int b, c; while (1 == 1) { if (!atomic_compare_exchange_weak(&a, &b, c)) { // COMPLIANT (void)0; /* no-op */ diff --git a/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.expected b/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.expected new file mode 100644 index 0000000000..a4359d7000 --- /dev/null +++ b/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.expected @@ -0,0 +1,7 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateStorageDurationsFunctionReturn.ql:33,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateStorageDurationsFunctionReturn.ql:37,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateStorageDurationsFunctionReturn.ql:50,6-14) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateStorageDurationsFunctionReturn.ql:50,26-34) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AppropriateStorageDurationsFunctionReturn.ql:56,3-11) +| test.c:3:10:3:10 | a | $@ with automatic storage may be accessible outside of its lifetime. | test.c:3:10:3:10 | a | a | +| test.c:15:4:15:8 | param [inner post update] | $@ with automatic storage may be accessible outside of its lifetime. | test.c:15:12:15:13 | a2 | a2 | diff --git a/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.qlref b/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.qlref new file mode 100644 index 0000000000..6541115217 --- /dev/null +++ b/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.qlref @@ -0,0 +1 @@ +rules/DCL30-C/AppropriateStorageDurationsFunctionReturn.ql \ No newline at end of file diff --git a/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.testref b/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.testref new file mode 100644 index 0000000000..e1ff9b5ae0 --- /dev/null +++ b/c/cert/test/rules/DCL30-C/AppropriateStorageDurationsStackAdressEscape.testref @@ -0,0 +1 @@ +c/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql \ No newline at end of file diff --git a/c/cert/test/rules/DCL30-C/test.c b/c/cert/test/rules/DCL30-C/test.c new file mode 100644 index 0000000000..f703f158c0 --- /dev/null +++ b/c/cert/test/rules/DCL30-C/test.c @@ -0,0 +1,34 @@ +char *f(void) { + char a[1]; + return a; // NON_COMPLIANT +} + +char f1(void) { + char a1[1]; + a1[0] = 'a'; + return a1[0]; // COMPLIANT +} + +void f2(char **param) { + char a2[1]; + a2[0] = 'a'; + *param = a2; // NON_COMPLIANT +} + +const char *g; +void f3(void) { + const char a3[] = "test"; + g = a3; // NON_COMPLIANT +} + +void f4(void) { + const char a4[] = "test"; + const char *p = a4; // COMPLIANT +} + +#include +void f5(void) { + const char a5[] = "test"; + g = a5; // COMPLIANT[FALSE_POSITIVE] + g = NULL; +} \ No newline at end of file diff --git a/c/cert/test/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.testref b/c/cert/test/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.testref new file mode 100644 index 0000000000..394150a10b --- /dev/null +++ b/c/cert/test/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.testref @@ -0,0 +1 @@ +c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql \ No newline at end of file diff --git a/c/cert/test/rules/DCL41-C/test.c b/c/cert/test/rules/DCL41-C/test.c index 2500c982f3..6b982c6d0b 100644 --- a/c/cert/test/rules/DCL41-C/test.c +++ b/c/cert/test/rules/DCL41-C/test.c @@ -23,6 +23,8 @@ void f1(int expr) { void f2(int expr) { switch (expr) { case 0: + 0; // Note: required because a "case" is a label, and not permitted on a + // declaration, so we need a no-op statement int i = 4; // COMPLIANT case 1: i = 6; // COMPLIANT diff --git a/c/cert/test/rules/ENV32-C/ExitHandlersMustReturnNormally.expected.qcc b/c/cert/test/rules/ENV32-C/ExitHandlersMustReturnNormally.expected.qcc new file mode 100644 index 0000000000..227c023432 --- /dev/null +++ b/c/cert/test/rules/ENV32-C/ExitHandlersMustReturnNormally.expected.qcc @@ -0,0 +1,13 @@ +edges +| test.c:8:6:8:13 | exit1bad | test.c:11:5:11:8 | call to exit | +| test.c:20:14:20:21 | exit1bad | test.c:8:6:8:13 | exit1bad | +| test.c:41:6:41:10 | exit2 | test.c:42:3:42:17 | call to siglongjmp | +| test.c:46:21:46:25 | exit2 | test.c:41:6:41:10 | exit2 | +| test.c:62:6:62:17 | exit3_helper | test.c:62:27:62:41 | call to siglongjmp | +| test.c:64:6:64:10 | exit3 | test.c:65:3:65:14 | call to exit3_helper | +| test.c:65:3:65:14 | call to exit3_helper | test.c:62:6:62:17 | exit3_helper | +| test.c:69:14:69:18 | exit3 | test.c:64:6:64:10 | exit3 | +#select +| test.c:8:6:8:13 | exit1bad | test.c:20:14:20:21 | exit1bad | test.c:11:5:11:8 | call to exit | The function is $@ and $@. It must instead terminate by returning. | test.c:20:14:20:21 | exit1bad | registered as `exit handler` | test.c:11:5:11:8 | call to exit | calls an `exit function` | +| test.c:41:6:41:10 | exit2 | test.c:46:21:46:25 | exit2 | test.c:42:3:42:17 | call to siglongjmp | The function is $@ and $@. It must instead terminate by returning. | test.c:46:21:46:25 | exit2 | registered as `exit handler` | test.c:42:3:42:17 | call to siglongjmp | calls an `exit function` | +| test.c:64:6:64:10 | exit3 | test.c:69:14:69:18 | exit3 | test.c:62:27:62:41 | call to siglongjmp | The function is $@ and $@. It must instead terminate by returning. | test.c:69:14:69:18 | exit3 | registered as `exit handler` | test.c:62:27:62:41 | call to siglongjmp | calls an `exit function` | diff --git a/c/cert/test/rules/ERR30-C/ErrnoNotSetToZero.expected b/c/cert/test/rules/ERR30-C/ErrnoNotSetToZero.expected new file mode 100644 index 0000000000..15c6ed77cb --- /dev/null +++ b/c/cert/test/rules/ERR30-C/ErrnoNotSetToZero.expected @@ -0,0 +1,3 @@ +| test.c:11:12:11:18 | call to strtoul | The value of `errno` may be different than `0` when this function is called. | +| test.c:30:12:30:18 | call to strtoul | The value of `errno` may be different than `0` when this function is called. | +| test.c:49:3:49:9 | call to strtoul | The value of `errno` may be different than `0` when this function is called. | diff --git a/c/cert/test/rules/ERR30-C/ErrnoNotSetToZero.qlref b/c/cert/test/rules/ERR30-C/ErrnoNotSetToZero.qlref new file mode 100644 index 0000000000..4edc949520 --- /dev/null +++ b/c/cert/test/rules/ERR30-C/ErrnoNotSetToZero.qlref @@ -0,0 +1 @@ +rules/ERR30-C/ErrnoNotSetToZero.ql \ No newline at end of file diff --git a/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.expected b/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.expected new file mode 100644 index 0000000000..125f55118b --- /dev/null +++ b/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.expected @@ -0,0 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ErrnoReadBeforeReturn.ql:46,7-15) +| test.c:69:7:69:11 | * ... | Do not read `errno` before checking the return value of function $@. | test.c:68:3:68:7 | call to ftell | call to ftell | +| test.c:69:7:69:11 | call to __errno_location | Do not read `errno` before checking the return value of function $@. | test.c:68:3:68:7 | call to ftell | call to ftell | +| test.c:70:5:70:10 | call to perror | Do not read `errno` before checking the return value of function $@. | test.c:68:3:68:7 | call to ftell | call to ftell | diff --git a/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.expected.qcc b/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.expected.qcc new file mode 100644 index 0000000000..55ce78368f --- /dev/null +++ b/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.expected.qcc @@ -0,0 +1,3 @@ +| test.c:69:7:69:11 | * ... | Do not read `errno` before checking the return value of function $@. | test.c:68:3:68:7 | call to ftell | call to ftell | +| test.c:69:7:69:11 | call to __get_errno_ptr | Do not read `errno` before checking the return value of function $@. | test.c:68:3:68:7 | call to ftell | call to ftell | +| test.c:70:5:70:10 | call to perror | Do not read `errno` before checking the return value of function $@. | test.c:68:3:68:7 | call to ftell | call to ftell | diff --git a/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.qlref b/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.qlref new file mode 100644 index 0000000000..8cc224bdb5 --- /dev/null +++ b/c/cert/test/rules/ERR30-C/ErrnoReadBeforeReturn.qlref @@ -0,0 +1 @@ +rules/ERR30-C/ErrnoReadBeforeReturn.ql \ No newline at end of file diff --git a/c/cert/test/rules/ERR30-C/FunctionCallBeforeErrnoCheck.expected b/c/cert/test/rules/ERR30-C/FunctionCallBeforeErrnoCheck.expected new file mode 100644 index 0000000000..0a4a8f52da --- /dev/null +++ b/c/cert/test/rules/ERR30-C/FunctionCallBeforeErrnoCheck.expected @@ -0,0 +1,2 @@ +| test.c:48:3:48:9 | call to strtoul | The value of `errno` is not checked after this call to `strtoul`. | +| test.c:60:12:60:18 | call to strtoul | The value of `errno` is not checked after this call to `strtoul`. | diff --git a/c/cert/test/rules/ERR30-C/FunctionCallBeforeErrnoCheck.qlref b/c/cert/test/rules/ERR30-C/FunctionCallBeforeErrnoCheck.qlref new file mode 100644 index 0000000000..c11e20831e --- /dev/null +++ b/c/cert/test/rules/ERR30-C/FunctionCallBeforeErrnoCheck.qlref @@ -0,0 +1 @@ +rules/ERR30-C/FunctionCallBeforeErrnoCheck.ql \ No newline at end of file diff --git a/c/cert/test/rules/ERR30-C/SetlocaleMightSetErrno.expected b/c/cert/test/rules/ERR30-C/SetlocaleMightSetErrno.expected new file mode 100644 index 0000000000..20a7ff60b1 --- /dev/null +++ b/c/cert/test/rules/ERR30-C/SetlocaleMightSetErrno.expected @@ -0,0 +1,3 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (SetlocaleMightSetErrno.ql:70,7-15) +| test.c:98:3:98:11 | call to setlocale | Do not read `errno` before checking the return value of a call to `setlocale`. | +| test.c:104:7:104:15 | call to setlocale | The value of `errno` may be different than `0` when `setlocale` is called. The following `errno` check might be invalid. | diff --git a/c/cert/test/rules/ERR30-C/SetlocaleMightSetErrno.qlref b/c/cert/test/rules/ERR30-C/SetlocaleMightSetErrno.qlref new file mode 100644 index 0000000000..8639ab7764 --- /dev/null +++ b/c/cert/test/rules/ERR30-C/SetlocaleMightSetErrno.qlref @@ -0,0 +1 @@ +rules/ERR30-C/SetlocaleMightSetErrno.ql \ No newline at end of file diff --git a/c/cert/test/rules/ERR30-C/test.c b/c/cert/test/rules/ERR30-C/test.c new file mode 100644 index 0000000000..790e9e78c2 --- /dev/null +++ b/c/cert/test/rules/ERR30-C/test.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +void f1(const char *c_str) { + unsigned long number; + char *endptr; + + number = strtoul(c_str, &endptr, 0); // NON_COMPLIANT + if (endptr == c_str || (number == ULONG_MAX && errno == ERANGE)) { + } +} + +void f2(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); // COMPLIANT + if (endptr == c_str || (number == ULONG_MAX && errno == ERANGE)) { + } +} + +void f1a(const char *c_str) { + unsigned long number; + char *endptr; + + number = strtoul(c_str, &endptr, 0); // NON_COMPLIANT + if (errno == ERANGE) { + } +} + +void f2a(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); // COMPLIANT + if (errno == ERANGE) { + } +} +void f2b(const char *c_str) { + char *endptr; + + errno = 0; + strtoul(c_str, &endptr, 0); // NON_COMPLIANT + strtoul(c_str, &endptr, 0); // NON_COMPLIANT + if (errno == ERANGE) { + } +} + +void helper() {} +void f2c(const char *c_str) { + unsigned long number; + char *endptr; + + errno = 0; + number = strtoul(c_str, &endptr, 0); // NON_COMPLIANT + helper(); + if (endptr == c_str || (number == ULONG_MAX && errno == ERANGE)) { + } +} + +void f3(FILE *fp) { + errno = 0; + ftell(fp); + if (errno) { // NON_COMPLIANT + perror("ftell"); // NON_COMPLIANT + } +} + +void f4(FILE *fp) { + if (ftell(fp) == -1) { + perror("ftell"); // COMPLIANT + } +} + +void f4b(FILE *fp) { + long l = ftell(fp); + if (l == -1) { + perror("ftell"); // COMPLIANT + } +} + +void f5() { + setlocale(LC_ALL, "en_US.UTF-8"); // COMPLIANT +} + +void f6() { + if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) { // COMPLIANT + } +} + +void f7() { + errno = 0; + setlocale(LC_ALL, "en_US.UTF-8"); // NON_COMPLIANT + if (errno != 0) { + } +} + +void f8() { + if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) { // NON_COMPLIANT + if (errno != 0) { + } + } +} + +void f9() { + errno = 0; + if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) { // COMPLIANT + if (errno != 0) { + } + } +} \ No newline at end of file diff --git a/c/cert/test/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.expected b/c/cert/test/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.expected new file mode 100644 index 0000000000..b79a17ca35 --- /dev/null +++ b/c/cert/test/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.expected @@ -0,0 +1,11 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotRelyOnIndeterminateValuesOfErrno.ql:56,7-15) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotRelyOnIndeterminateValuesOfErrno.ql:56,27-35) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotRelyOnIndeterminateValuesOfErrno.ql:57,9-17) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotRelyOnIndeterminateValuesOfErrno.ql:60,9-17) +| test.c:12:5:12:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:10:21:10:26 | call to signal | call to signal | +| test.c:30:5:30:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:26:21:26:26 | call to signal | call to signal | +| test.c:49:5:49:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:45:21:45:26 | call to signal | call to signal | +| test.c:56:5:56:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:54:21:54:26 | call to signal | call to signal | +| test.c:74:5:74:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:64:17:64:22 | call to signal | call to signal | +| test.c:74:5:74:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:66:17:66:22 | call to signal | call to signal | +| test.c:74:5:74:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:70:17:70:22 | call to signal | call to signal | diff --git a/c/cert/test/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.qlref b/c/cert/test/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.qlref new file mode 100644 index 0000000000..96c9e9a246 --- /dev/null +++ b/c/cert/test/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.qlref @@ -0,0 +1 @@ +rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql \ No newline at end of file diff --git a/c/cert/test/rules/ERR32-C/test.c b/c/cert/test/rules/ERR32-C/test.c new file mode 100644 index 0000000000..d6ed7eb1f5 --- /dev/null +++ b/c/cert/test/rules/ERR32-C/test.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +typedef void (*pfv)(int); + +void handler1(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler == SIG_ERR) { + perror(""); // NON_COMPLIANT + } +} + +void handler2a(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler != SIG_ERR) { + perror(""); // COMPLIANT + } else { + abort(); // COMPLIANT + } +} + +void handler2b(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler != SIG_ERR) { + perror(""); // COMPLIANT + } else { + perror(""); // NON_COMPLIANT + abort(); + } +} + +void handler3(int signum) { pfv old_handler = signal(signum, SIG_DFL); } + +void handler4(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler == SIG_ERR) { + _Exit(0); + } +} + +void handler5(int signum) { + pfv old_handler = signal(signum, SIG_DFL); + if (old_handler != SIG_ERR) { + perror(""); // COMPLIANT + } else { + perror(""); // NON_COMPLIANT + } +} + +int main(void) { + pfv old_handler = signal(SIGINT, handler1); + if (old_handler == SIG_ERR) { + perror(""); // NON_COMPLIANT + } + + old_handler = signal(SIGINT, handler2a); + if (old_handler == SIG_ERR) { + perror(""); // COMPLIANT + } + + old_handler = signal(SIGINT, handler2b); + + old_handler = signal(SIGINT, handler3); + + old_handler = signal(SIGINT, handler4); + + old_handler = signal(SIGINT, handler5); + + FILE *fp = fopen("something", "r"); + if (fp == NULL) { + perror("Error: "); // NON_COMPLIANT + } +} diff --git a/c/cert/test/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.expected b/c/cert/test/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.expected new file mode 100644 index 0000000000..f4006c013e --- /dev/null +++ b/c/cert/test/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.expected @@ -0,0 +1,14 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleStandardLibraryErrors.ql:459,5-13) +| test.c:18:3:18:11 | call to setlocale | Missing error detection for the call to function `setlocale`. | +| test.c:24:23:24:31 | call to setlocale | Missing error detection for the call to function `setlocale`. | +| test.c:29:22:29:27 | call to calloc | Missing error detection for the call to function `calloc`. | +| test.c:35:7:35:13 | call to realloc | Missing error detection for the call to function `realloc`. | +| test.c:46:3:46:7 | call to fseek | Missing error detection for the call to function `fseek`. | +| test.c:52:3:52:10 | call to snprintf | Missing error detection for the call to function `snprintf`. | +| test.c:60:3:60:9 | call to putchar | Missing error detection for the call to function `putchar`. | +| test.c:63:3:63:8 | call to printf | Missing error detection for the call to function `printf`. | +| test.c:74:22:74:30 | call to localtime | Missing error detection for the call to function `localtime`. | +| test.c:80:3:80:7 | call to mblen | Missing error detection for the call to function `mblen`. | +| test.c:97:5:97:9 | call to fputc | Missing error detection for the call to function `fputc`. | +| test.c:105:5:105:11 | call to getchar | Missing error detection for the call to function `getchar`. | +| test.c:127:7:127:14 | call to strtoull | Missing error detection for the call to function `strtoull`. | diff --git a/c/cert/test/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.qlref b/c/cert/test/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.qlref new file mode 100644 index 0000000000..da020bb97b --- /dev/null +++ b/c/cert/test/rules/ERR33-C/DetectAndHandleStandardLibraryErrors.qlref @@ -0,0 +1 @@ +rules/ERR33-C/DetectAndHandleStandardLibraryErrors.ql \ No newline at end of file diff --git a/c/cert/test/rules/ERR33-C/test.c b/c/cert/test/rules/ERR33-C/test.c new file mode 100644 index 0000000000..09bd83600c --- /dev/null +++ b/c/cert/test/rules/ERR33-C/test.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *p; +typedef struct { + char sig_desc[32]; +} signal_info; + +void f1(size_t n) { + setlocale(LC_CTYPE, "en_US.UTF-8"); // NON_COMPLIANT + + const char *save1 = setlocale(LC_CTYPE, "en_US.UTF-8"); // COMPLIANT + if (NULL == save1) { + } + + const char *save2 = setlocale(LC_CTYPE, "en_US.UTF-8"); // NON_COMPLIANT + if (save1 == save2) { + } + + signal_info *start = + (signal_info *)calloc(n, sizeof(signal_info)); // NON_COMPLIANT + + start = (signal_info *)calloc(n, sizeof(signal_info)); // COMPLIANT + if (start == NULL) { + } + + p = realloc(p, n); // NON_COMPLIANT + if (p == NULL) { + } + + void *q; + q = realloc(p, n); // COMPLIANT + if (q == NULL) { + } +} + +void f2(FILE *f, long o) { + fseek(f, o, SEEK_SET); // NON_COMPLIANT + + if (fseek(f, o, SEEK_SET) != 0) { // COMPLIANT + } + + char buf[40]; + snprintf(buf, sizeof(buf), ""); // NON_COMPLIANT + + int n = snprintf(buf, sizeof(buf), ""); // COMPLIANT + if (n < 0) { + } +} + +void f3() { + putchar('C'); // NON_COMPLIANT + (void)putchar('C'); // COMPLIANT + + printf(""); // NON_COMPLIANT + (void)printf(""); // COMPLIANT +} +void signal_handler(int signal) {} +void f4() { + FILE *f; + char a[10]; + char b[10]; + time_t time; + if (fprintf(f, "") < 0) { // COMPLIANT + } + struct tm *local = localtime(&time); // NON_COMPLIANT + if (strftime(b, 10, "", local) == 0) { // COMPLIANT + } + if (clock() == (clock_t)(-1)) { // COMPLIANT + } + mblen(NULL, 0); // COMPLIANT + mblen(a, 0); // NON_COMPLIANT + if (mblen(a, 0) == -1) { // COMPLIANT + } + if (ftell(f) == -1L) { // COMPLIANT + } + if (fread(b, 1, 1, f) == 32) { // COMPLIANT + } + if (fwrite("", 1, 1, f) == 32) { // COMPLIANT + } + if (wctob(0) == EOF) { // COMPLIANT + } + if (fputc(0, f) == EOF) { // COMPLIANT + } + do { + fputc(0, f); // COMPLIANT + } while (!ferror(f)); + do { + fputc(0, f); // NON_COMPLIANT + } while (!feof(f)); + if (fgetc(f) == EOF) { // COMPLIANT + } + do { + getchar(); // COMPLIANT + } while ((!feof(stdin) && !ferror(stdin))); + do { + getchar(); // NON_COMPLIANT + } while (!feof(stdin)); + if (aligned_alloc(0, 0) == NULL) { // COMPLIANT + } + if (signal(SIGINT, signal_handler) == SIG_ERR) { // COMPLIANT + } + cnd_t q; + if (cnd_broadcast(&q) == thrd_error) { // COMPLIANT + } + if (cnd_init(&q) == thrd_nomem) { // COMPLIANT + } + if (cnd_init(&q) == thrd_error) { // COMPLIANT + } + mtx_t mutex; + struct timespec ts; + if (cnd_timedwait(&q, &mutex, &ts) == thrd_timedout) { // COMPLIANT + } + if (cnd_timedwait(&q, &mutex, &ts) == thrd_error) { // COMPLIANT + } + char *endptr; + if (strtoumax("", &endptr, 0) == UINTMAX_MAX) { // COMPLIANT + } + if (strtoull("", &endptr, 0) == ULONG_MAX) { // NON_COMPLIANT + // =ULLONG_MAX not present in the test DB + } + if (strtoul("", &endptr, 0) == ULONG_MAX) { // COMPLIANT + } + if (btowc(0) == WEOF) { // COMPLIANT + } + if (fgetwc(f) == WEOF) { // COMPLIANT + } + if (strxfrm(a, b, 10) >= 32) { // COMPLIANT + } +} diff --git a/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected new file mode 100644 index 0000000000..403d211651 --- /dev/null +++ b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected @@ -0,0 +1,20 @@ +| test.c:17:7:17:13 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:20:7:20:12 | ... > ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:29:7:29:13 | ... == ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:32:7:32:16 | ... == ... | $@ compared to constant value. | test.c:5:7:5:8 | g2 | Function pointer variable g2 | +| test.c:35:7:35:15 | ... != ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:38:7:38:8 | f1 | $@ undergoes implicit constant comparison. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:41:7:41:8 | g1 | $@ undergoes implicit constant comparison. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:68:7:68:27 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:71:7:71:18 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:74:7:76:14 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:83:3:83:9 | ... == ... | $@ compared to constant value. | test.c:82:10:82:11 | l1 | Function pointer variable l1 | +| test.c:84:3:84:12 | ... == ... | $@ compared to constant value. | test.c:82:10:82:11 | l1 | Function pointer variable l1 | +| test.c:91:3:91:4 | g1 | $@ undergoes implicit constant comparison. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:96:7:96:18 | ... == ... | $@ compared to constant value. | test.c:9:9:9:10 | fp | Function pointer variable fp | +| test.c:102:7:102:22 | ... == ... | $@ compared to constant value. | test.c:14:11:14:21 | get_handler | Address of function get_handler | +| test.c:105:7:105:24 | ... == ... | $@ compared to constant value. | test.c:105:7:105:17 | call to get_handler | Expression with function pointer type | +| test.c:121:7:121:13 | ... != ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:133:7:133:13 | ... != ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:139:7:139:13 | ... == ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:149:8:149:9 | g1 | $@ undergoes implicit constant comparison. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | diff --git a/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref new file mode 100644 index 0000000000..7d99fa9879 --- /dev/null +++ b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref @@ -0,0 +1 @@ +rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP16-C/test.c b/c/cert/test/rules/EXP16-C/test.c new file mode 100644 index 0000000000..16dfea9bc2 --- /dev/null +++ b/c/cert/test/rules/EXP16-C/test.c @@ -0,0 +1,153 @@ +#include + +int f1(); +void (*g1)(void); +int (*g2)(int); +void *g3 = NULL; + +struct S { + int (*fp)(void); + int x; +}; + +typedef int (*handler_t)(void); +handler_t get_handler(void); + +void f2(void) { + if (f1 == 0) // NON-COMPLIANT + return; + + if (f1 > 0) // NON-COMPLIANT + return; + + if (f1() == 0) // COMPLIANT + return; + + if (f1() > 0) // COMPLIANT + return; + + if (g1 == 0) // NON-COMPLIANT + return; + + if (g2 == NULL) // NON-COMPLIANT + return; + + if (g1 != 0x0) // NON-COMPLIANT + return; + + if (f1) // NON-COMPLIANT - implicit comparison + return; + + if (g1) // NON-COMPLIANT - implicit comparison + return; +} + +void f3(void *p1) { + if (g1 == p1) // COMPLIANT - comparing to variable + return; + + if (g2 == g3) // COMPLIANT - comparing to variable + return; +} + +void f4(void) { + int (*l1)(void) = 0; + + if (f1 == f1) // COMPLIANT - comparing to constant value of same type + return; + + if (f1 == l1) // COMPLIANT - comparing to constant value of same type + return; + + if (f1 == (int (*)(void))0) // COMPLIANT - explicit cast + return; + + if (f1 == (int (*)(void))0) // COMPLIANT - explicit cast + return; + + if (f1 == (int (*)(int))0) // NON-COMPLIANT - explicit cast to wrong type + return; + + if (f1 == (int)0) // NON-COMPLIANT - cast to non-function pointer type + return; + + if (f1 == + (int)(int (*)(void)) + NULL) // NON-COMPLIANT - compliant cast subsumed by non-compliant cast + return; +} + +typedef void (*func_t)(void); +void f5(void) { + func_t l1 = g1; + l1 == 0; // NON-COMPLIANT + l1 == NULL; // NON-COMPLIANT + l1 == (func_t)0; // COMPLIANT - cast to function pointer type +} + +void f6(void) { + g1 + 0; // COMPLIANT - not a comparison + g1 == g2; // COMPLIANT - not comparing to constant + g1 ? 1 : 0; // NON-COMPLIANT - implicit comparison +} + +void f7(void) { + struct S s; + if (s.fp == NULL) // NON-COMPLIANT + f1(); + + if (s.fp() == NULL) // COMPLIANT + return; + + if (get_handler == 0) // NON-COMPLIANT - missing parentheses + return; + + if (get_handler() == 0) // NON-COMPLIANT + return; + + if (get_handler() == (handler_t)0) // COMPLIANT + return; + + if (get_handler()() == 0) // COMPLIANT + return; +} + +void f8(void) { + // Test instances of where the function pointer check is used to guard calls + // to that function. + + // Technically, this function may perhaps be set to NULL by the linker. But + // it is not a variable that should need to be null-checked at runtime. + if (f1 != 0) // NON-COMPLIANT + { + f1(); + } + + // Check guards a call, so it is compliant. + if (g1 != 0) // COMPLIANT + { + g1(); + } + + // Incorrect check, not compliant. + if (g1 != 0) // NON-COMPLIANT + { + f1(); + } + + // Incorrect check, not compliant. + if (g1 == 0) // NON-COMPLIANT + { + g1(); + } + + if (g1) // COMPLIANT + { + g1(); + } + + if (!g1) // NON-COMPLIANT + { + g1(); + } +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.expected b/c/cert/test/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.expected index 3ea1a05fd7..034f7e9366 100644 --- a/c/cert/test/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.expected +++ b/c/cert/test/rules/EXP30-C/DependenceOnOrderOfFunctionArgumentsForSideEffects.expected @@ -1 +1,25 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:28,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:28,59-67) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:31,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:31,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:35,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:35,59-67) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:44,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:44,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:44,53-61) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:47,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:47,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:56,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:56,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:63,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:63,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:75,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:75,55-63) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:28,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:31,7-20) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:35,7-20) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:47,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:56,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:63,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:75,5-18) | test.c:20:3:20:4 | call to f1 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.c:20:6:20:7 | call to f2 | call to f2 | test.c:20:12:20:13 | call to f3 | call to f3 | diff --git a/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected new file mode 100644 index 0000000000..f5ea6e8d4b --- /dev/null +++ b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected @@ -0,0 +1,5 @@ +| test.c:5:13:5:21 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | +| test.c:6:13:6:31 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | +| test.c:14:3:14:55 | ... = ... | Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer. | +| test.c:24:3:25:36 | ... = ... | Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer. | +| test.c:42:24:42:41 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | diff --git a/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref new file mode 100644 index 0000000000..90635c935e --- /dev/null +++ b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref @@ -0,0 +1 @@ +rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP32-C/test.c b/c/cert/test/rules/EXP32-C/test.c new file mode 100644 index 0000000000..5a688848a4 --- /dev/null +++ b/c/cert/test/rules/EXP32-C/test.c @@ -0,0 +1,45 @@ +volatile int *volatile_f(); + +void test_cast_away_volatile() { + volatile int *l1 = volatile_f(); // COMPLIANT + int *l2 = (int *)l1; // NON_COMPLIANT + int *l3 = (int *)volatile_f(); // NON_COMPLIANT + *l2; // Volatile object is accessed through a non-volatile pointer +} + +void test_volatile_lost_by_assignment() { + static volatile int val = 0; + static int *non_compliant_pointer; + static volatile int **compliant_pointer_to_pointer; + compliant_pointer_to_pointer = &non_compliant_pointer; // NON_COMPLIANT + *compliant_pointer_to_pointer = &val; + *non_compliant_pointer; // Volatile object is accessed through a non-volatile + // pointer +} + +void test_volatile_lost_by_assignment_and_cast() { + static volatile int val = 0; + static int *non_compliant_pointer; + static volatile int **compliant_pointer_to_pointer; + compliant_pointer_to_pointer = + (int **)&non_compliant_pointer; // NON_COMPLIANT + *compliant_pointer_to_pointer = &val; + *non_compliant_pointer; // Volatile object is accessed through a non-volatile + // pointer +} + +void test_volatile_not_lost_by_assignment_and_cast() { + static volatile int val = 0; + static volatile int *compliant_pointer; + static volatile int **compliant_pointer_to_pointer; + compliant_pointer_to_pointer = &compliant_pointer; // COMPLIANT + *compliant_pointer_to_pointer = &val; + *compliant_pointer; // Volatile object is accessed through a volatile pointer +} + +void test_volatile_lost_by_assignment_and_cast_2() { + volatile int *ptr = 0; + int *volatile ptr2 = (int *volatile)ptr; // NON_COMPLIANT + *ptr2; // Volatile object dereferenced through volatile pointer to + // non-volatile object +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP33-C/DoNotReadUninitializedMemory.testref b/c/cert/test/rules/EXP33-C/DoNotReadUninitializedMemory.testref new file mode 100644 index 0000000000..45f4b5df27 --- /dev/null +++ b/c/cert/test/rules/EXP33-C/DoNotReadUninitializedMemory.testref @@ -0,0 +1 @@ +c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP34-C/DoNotDereferenceNullPointers.testref b/c/cert/test/rules/EXP34-C/DoNotDereferenceNullPointers.testref new file mode 100644 index 0000000000..79cbfe9e9d --- /dev/null +++ b/c/cert/test/rules/EXP34-C/DoNotDereferenceNullPointers.testref @@ -0,0 +1 @@ +c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.expected b/c/cert/test/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.expected new file mode 100644 index 0000000000..3fb10f3267 --- /dev/null +++ b/c/cert/test/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.expected @@ -0,0 +1,4 @@ +| test.c:65:18:65:18 | a | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:65:9:65:14 | call to get_s1 | temporary object | +| test.c:67:18:67:19 | s1 | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:67:9:67:14 | call to get_s3 | temporary object | +| test.c:68:18:68:19 | i1 | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:68:9:68:14 | call to get_s3 | temporary object | +| test.c:69:18:69:21 | af12 | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:69:9:69:14 | call to get_s4 | temporary object | diff --git a/c/cert/test/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.qlref b/c/cert/test/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.qlref new file mode 100644 index 0000000000..a142303a4e --- /dev/null +++ b/c/cert/test/rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.qlref @@ -0,0 +1 @@ +rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP35-C/test.c b/c/cert/test/rules/EXP35-C/test.c new file mode 100644 index 0000000000..09e91089ab --- /dev/null +++ b/c/cert/test/rules/EXP35-C/test.c @@ -0,0 +1,71 @@ +#include + +typedef float AF12[12]; + +struct S1 { + char a[8]; +}; +struct S2 { + struct S1 *s1; +}; +struct S3 { + struct S1 s1; + int i1; +}; +struct S4 { + AF12 af12; +}; + +struct S5 { + int i1; + struct S1 *s1; +}; + +struct S1 get_s1(void) { + struct S1 s1; + return s1; +} + +struct S1 *get_s1_ptr(void) { + struct S1 *s1 = malloc(sizeof(struct S1)); + return s1; +} + +struct S2 get_s2(void) { + struct S2 s2; + return s2; +} + +struct S3 get_s3(void) { + struct S3 s3; + return s3; +} + +struct S4 get_s4(void) { + struct S4 s4; + return s4; +} + +struct S5 get_s5(void) { + struct S5 s5; + return s5; +} + +void test_field_access(void) { + struct S1 s1 = get_s1(); + struct S2 s2 = get_s2(); + struct S3 s3 = get_s3(); + struct S4 s4 = get_s4(); + + s1.a[0] = 'a'; // COMPLIANT + s2.s1->a[0] = 'a'; // COMPLIANT + s3.s1.a[0] = 'a'; // COMPLIANT + s4.af12[0] = 0.0f; // COMPLIANT + + (void)get_s1().a; // NON_COMPLIANT + (void)get_s2().s1->a; // COMPLIANT + (void)get_s3().s1.a; // NON_COMPLIANT + (void)get_s3().i1; // NON_COMPLIANT - even if scalar type accessed + (void)get_s4().af12; // NON_COMPLIANT + (void)get_s5().s1->a; // COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected new file mode 100644 index 0000000000..eb7642ae28 --- /dev/null +++ b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected @@ -0,0 +1,277 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:103,86-94) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:125,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:127,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:132,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:138,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:144,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:145,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:147,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:154,26-34) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCastPointerToMoreStrictlyAlignedPointerType.ql:169,44-52) +edges +| test.c:75:14:75:16 | & ... | test.c:76:11:76:12 | v1 | provenance | | +| test.c:75:14:75:16 | & ... | test.c:77:12:77:13 | v1 | provenance | | +| test.c:75:14:75:16 | & ... | test.c:78:10:78:11 | v1 | provenance | | +| test.c:75:14:75:16 | & ... | test.c:79:12:79:13 | v1 | provenance | | +| test.c:75:14:75:16 | & ... | test.c:80:11:80:12 | v1 | provenance | | +| test.c:75:14:75:16 | & ... | test.c:81:13:81:14 | v1 | provenance | | +| test.c:84:14:84:16 | & ... | test.c:85:11:85:12 | v2 | provenance | | +| test.c:84:14:84:16 | & ... | test.c:86:12:86:13 | v2 | provenance | | +| test.c:84:14:84:16 | & ... | test.c:87:10:87:11 | v2 | provenance | | +| test.c:84:14:84:16 | & ... | test.c:88:12:88:13 | v2 | provenance | | +| test.c:84:14:84:16 | & ... | test.c:89:11:89:12 | v2 | provenance | | +| test.c:84:14:84:16 | & ... | test.c:90:13:90:14 | v2 | provenance | | +| test.c:93:14:93:16 | & ... | test.c:94:11:94:12 | v3 | provenance | | +| test.c:93:14:93:16 | & ... | test.c:95:12:95:13 | v3 | provenance | | +| test.c:93:14:93:16 | & ... | test.c:96:10:96:11 | v3 | provenance | | +| test.c:93:14:93:16 | & ... | test.c:97:12:97:13 | v3 | provenance | | +| test.c:93:14:93:16 | & ... | test.c:98:11:98:12 | v3 | provenance | | +| test.c:93:14:93:16 | & ... | test.c:99:13:99:14 | v3 | provenance | | +| test.c:102:14:102:16 | & ... | test.c:103:11:103:12 | v4 | provenance | | +| test.c:102:14:102:16 | & ... | test.c:104:12:104:13 | v4 | provenance | | +| test.c:102:14:102:16 | & ... | test.c:105:10:105:11 | v4 | provenance | | +| test.c:102:14:102:16 | & ... | test.c:106:12:106:13 | v4 | provenance | | +| test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | provenance | | +| test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | provenance | | +| test.c:111:14:111:16 | & ... | test.c:112:11:112:12 | v5 | provenance | | +| test.c:111:14:111:16 | & ... | test.c:113:12:113:13 | v5 | provenance | | +| test.c:111:14:111:16 | & ... | test.c:114:10:114:11 | v5 | provenance | | +| test.c:111:14:111:16 | & ... | test.c:115:12:115:13 | v5 | provenance | | +| test.c:111:14:111:16 | & ... | test.c:116:11:116:12 | v5 | provenance | | +| test.c:111:14:111:16 | & ... | test.c:117:13:117:14 | v5 | provenance | | +| test.c:120:14:120:16 | & ... | test.c:121:11:121:12 | v6 | provenance | | +| test.c:120:14:120:16 | & ... | test.c:122:12:122:13 | v6 | provenance | | +| test.c:120:14:120:16 | & ... | test.c:123:10:123:11 | v6 | provenance | | +| test.c:120:14:120:16 | & ... | test.c:124:12:124:13 | v6 | provenance | | +| test.c:120:14:120:16 | & ... | test.c:125:11:125:12 | v6 | provenance | | +| test.c:120:14:120:16 | & ... | test.c:126:13:126:14 | v6 | provenance | | +| test.c:129:22:129:22 | v | test.c:130:17:130:17 | v | provenance | | +| test.c:135:21:135:23 | & ... | test.c:129:22:129:22 | v | provenance | | +| test.c:138:21:138:23 | & ... | test.c:129:22:129:22 | v | provenance | | +| test.c:166:24:166:29 | call to malloc | test.c:167:13:167:15 | & ... | provenance | | +| test.c:166:24:166:29 | call to malloc | test.c:168:16:168:17 | s1 | provenance | | +| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | provenance | | +| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | provenance | | +| test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | provenance | | +| test.c:174:13:174:14 | s2 | test.c:129:22:129:22 | v | provenance | | +| test.c:179:13:179:14 | s3 | test.c:129:22:129:22 | v | provenance | | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:184:11:184:12 | v1 | provenance | | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:185:10:185:11 | v1 | provenance | | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | provenance | | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:187:13:187:14 | v1 | provenance | | +| test.c:187:13:187:14 | v1 | test.c:129:22:129:22 | v | provenance | | +| test.c:189:14:189:26 | call to aligned_alloc | test.c:190:13:190:14 | v2 | provenance | | +| test.c:190:13:190:14 | v2 | test.c:129:22:129:22 | v | provenance | | +| test.c:222:8:222:9 | p2 | test.c:223:11:223:12 | v1 | provenance | | +| test.c:222:8:222:9 | p2 | test.c:224:12:224:13 | v1 | provenance | | +| test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | provenance | | +| test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | provenance | | +| test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | provenance | | +| test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | provenance | | +| test.c:238:13:238:14 | & ... | test.c:244:12:244:13 | ip | provenance | | +| test.c:241:15:241:18 | & ... | test.c:247:9:247:12 | & ... | provenance | | +| test.c:252:16:252:18 | & ... | test.c:254:11:254:13 | ps1 | provenance | | +| test.c:252:16:252:18 | & ... | test.c:256:10:256:12 | ps1 | provenance | | +nodes +| test.c:7:11:7:13 | & ... | semmle.label | & ... | +| test.c:8:12:8:14 | & ... | semmle.label | & ... | +| test.c:9:10:9:12 | & ... | semmle.label | & ... | +| test.c:10:11:10:13 | & ... | semmle.label | & ... | +| test.c:11:12:11:14 | & ... | semmle.label | & ... | +| test.c:12:13:12:15 | & ... | semmle.label | & ... | +| test.c:15:11:15:13 | & ... | semmle.label | & ... | +| test.c:16:12:16:14 | & ... | semmle.label | & ... | +| test.c:17:10:17:12 | & ... | semmle.label | & ... | +| test.c:18:11:18:13 | & ... | semmle.label | & ... | +| test.c:19:12:19:14 | & ... | semmle.label | & ... | +| test.c:20:13:20:15 | & ... | semmle.label | & ... | +| test.c:23:11:23:13 | & ... | semmle.label | & ... | +| test.c:24:12:24:14 | & ... | semmle.label | & ... | +| test.c:25:10:25:12 | & ... | semmle.label | & ... | +| test.c:26:12:26:14 | & ... | semmle.label | & ... | +| test.c:27:11:27:13 | & ... | semmle.label | & ... | +| test.c:28:13:28:15 | & ... | semmle.label | & ... | +| test.c:31:11:31:13 | & ... | semmle.label | & ... | +| test.c:32:12:32:14 | & ... | semmle.label | & ... | +| test.c:33:10:33:12 | & ... | semmle.label | & ... | +| test.c:34:12:34:14 | & ... | semmle.label | & ... | +| test.c:35:11:35:13 | & ... | semmle.label | & ... | +| test.c:36:13:36:15 | & ... | semmle.label | & ... | +| test.c:39:11:39:13 | & ... | semmle.label | & ... | +| test.c:40:12:40:14 | & ... | semmle.label | & ... | +| test.c:41:10:41:12 | & ... | semmle.label | & ... | +| test.c:42:12:42:14 | & ... | semmle.label | & ... | +| test.c:43:11:43:13 | & ... | semmle.label | & ... | +| test.c:44:13:44:15 | & ... | semmle.label | & ... | +| test.c:47:11:47:13 | & ... | semmle.label | & ... | +| test.c:48:12:48:14 | & ... | semmle.label | & ... | +| test.c:49:10:49:12 | & ... | semmle.label | & ... | +| test.c:50:12:50:14 | & ... | semmle.label | & ... | +| test.c:51:11:51:13 | & ... | semmle.label | & ... | +| test.c:52:13:52:15 | & ... | semmle.label | & ... | +| test.c:57:11:57:13 | & ... | semmle.label | & ... | +| test.c:58:12:58:14 | & ... | semmle.label | & ... | +| test.c:59:10:59:12 | & ... | semmle.label | & ... | +| test.c:60:12:60:14 | & ... | semmle.label | & ... | +| test.c:61:11:61:13 | & ... | semmle.label | & ... | +| test.c:62:13:62:15 | & ... | semmle.label | & ... | +| test.c:65:11:65:13 | & ... | semmle.label | & ... | +| test.c:66:12:66:14 | & ... | semmle.label | & ... | +| test.c:67:10:67:12 | & ... | semmle.label | & ... | +| test.c:68:12:68:14 | & ... | semmle.label | & ... | +| test.c:69:11:69:13 | & ... | semmle.label | & ... | +| test.c:70:13:70:15 | & ... | semmle.label | & ... | +| test.c:75:14:75:16 | & ... | semmle.label | & ... | +| test.c:75:14:75:16 | & ... | semmle.label | & ... | +| test.c:76:11:76:12 | v1 | semmle.label | v1 | +| test.c:77:12:77:13 | v1 | semmle.label | v1 | +| test.c:78:10:78:11 | v1 | semmle.label | v1 | +| test.c:79:12:79:13 | v1 | semmle.label | v1 | +| test.c:80:11:80:12 | v1 | semmle.label | v1 | +| test.c:81:13:81:14 | v1 | semmle.label | v1 | +| test.c:84:14:84:16 | & ... | semmle.label | & ... | +| test.c:84:14:84:16 | & ... | semmle.label | & ... | +| test.c:85:11:85:12 | v2 | semmle.label | v2 | +| test.c:86:12:86:13 | v2 | semmle.label | v2 | +| test.c:87:10:87:11 | v2 | semmle.label | v2 | +| test.c:88:12:88:13 | v2 | semmle.label | v2 | +| test.c:89:11:89:12 | v2 | semmle.label | v2 | +| test.c:90:13:90:14 | v2 | semmle.label | v2 | +| test.c:93:14:93:16 | & ... | semmle.label | & ... | +| test.c:93:14:93:16 | & ... | semmle.label | & ... | +| test.c:94:11:94:12 | v3 | semmle.label | v3 | +| test.c:95:12:95:13 | v3 | semmle.label | v3 | +| test.c:96:10:96:11 | v3 | semmle.label | v3 | +| test.c:97:12:97:13 | v3 | semmle.label | v3 | +| test.c:98:11:98:12 | v3 | semmle.label | v3 | +| test.c:99:13:99:14 | v3 | semmle.label | v3 | +| test.c:102:14:102:16 | & ... | semmle.label | & ... | +| test.c:102:14:102:16 | & ... | semmle.label | & ... | +| test.c:103:11:103:12 | v4 | semmle.label | v4 | +| test.c:104:12:104:13 | v4 | semmle.label | v4 | +| test.c:105:10:105:11 | v4 | semmle.label | v4 | +| test.c:106:12:106:13 | v4 | semmle.label | v4 | +| test.c:107:11:107:12 | v4 | semmle.label | v4 | +| test.c:108:13:108:14 | v4 | semmle.label | v4 | +| test.c:111:14:111:16 | & ... | semmle.label | & ... | +| test.c:111:14:111:16 | & ... | semmle.label | & ... | +| test.c:112:11:112:12 | v5 | semmle.label | v5 | +| test.c:113:12:113:13 | v5 | semmle.label | v5 | +| test.c:114:10:114:11 | v5 | semmle.label | v5 | +| test.c:115:12:115:13 | v5 | semmle.label | v5 | +| test.c:116:11:116:12 | v5 | semmle.label | v5 | +| test.c:117:13:117:14 | v5 | semmle.label | v5 | +| test.c:120:14:120:16 | & ... | semmle.label | & ... | +| test.c:120:14:120:16 | & ... | semmle.label | & ... | +| test.c:121:11:121:12 | v6 | semmle.label | v6 | +| test.c:122:12:122:13 | v6 | semmle.label | v6 | +| test.c:123:10:123:11 | v6 | semmle.label | v6 | +| test.c:124:12:124:13 | v6 | semmle.label | v6 | +| test.c:125:11:125:12 | v6 | semmle.label | v6 | +| test.c:126:13:126:14 | v6 | semmle.label | v6 | +| test.c:129:22:129:22 | v | semmle.label | v | +| test.c:130:17:130:17 | v | semmle.label | v | +| test.c:135:21:135:23 | & ... | semmle.label | & ... | +| test.c:135:21:135:23 | & ... | semmle.label | & ... | +| test.c:138:21:138:23 | & ... | semmle.label | & ... | +| test.c:138:21:138:23 | & ... | semmle.label | & ... | +| test.c:158:13:158:20 | & ... | semmle.label | & ... | +| test.c:161:13:161:20 | & ... | semmle.label | & ... | +| test.c:162:16:162:18 | & ... | semmle.label | & ... | +| test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | +| test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | +| test.c:167:13:167:15 | & ... | semmle.label | & ... | +| test.c:168:16:168:17 | s1 | semmle.label | s1 | +| test.c:169:13:169:14 | s1 | semmle.label | s1 | +| test.c:169:13:169:14 | s1 | semmle.label | s1 | +| test.c:172:11:172:12 | s2 | semmle.label | s2 | +| test.c:173:13:173:14 | s2 | semmle.label | s2 | +| test.c:174:13:174:14 | s2 | semmle.label | s2 | +| test.c:174:13:174:14 | s2 | semmle.label | s2 | +| test.c:177:11:177:12 | s3 | semmle.label | s3 | +| test.c:178:13:178:14 | s3 | semmle.label | s3 | +| test.c:179:13:179:14 | s3 | semmle.label | s3 | +| test.c:179:13:179:14 | s3 | semmle.label | s3 | +| test.c:183:14:183:26 | call to aligned_alloc | semmle.label | call to aligned_alloc | +| test.c:184:11:184:12 | v1 | semmle.label | v1 | +| test.c:185:10:185:11 | v1 | semmle.label | v1 | +| test.c:186:13:186:14 | v1 | semmle.label | v1 | +| test.c:187:13:187:14 | v1 | semmle.label | v1 | +| test.c:189:14:189:26 | call to aligned_alloc | semmle.label | call to aligned_alloc | +| test.c:190:13:190:14 | v2 | semmle.label | v2 | +| test.c:214:11:214:12 | p2 | semmle.label | p2 | +| test.c:215:12:215:13 | p2 | semmle.label | p2 | +| test.c:216:10:216:11 | p2 | semmle.label | p2 | +| test.c:217:11:217:12 | p2 | semmle.label | p2 | +| test.c:218:12:218:13 | p2 | semmle.label | p2 | +| test.c:219:13:219:14 | p2 | semmle.label | p2 | +| test.c:222:8:222:9 | p2 | semmle.label | p2 | +| test.c:222:8:222:9 | p2 | semmle.label | p2 | +| test.c:223:11:223:12 | v1 | semmle.label | v1 | +| test.c:224:12:224:13 | v1 | semmle.label | v1 | +| test.c:225:10:225:11 | v1 | semmle.label | v1 | +| test.c:226:12:226:13 | v1 | semmle.label | v1 | +| test.c:227:11:227:12 | v1 | semmle.label | v1 | +| test.c:228:13:228:14 | v1 | semmle.label | v1 | +| test.c:238:13:238:14 | & ... | semmle.label | & ... | +| test.c:240:16:240:19 | & ... | semmle.label | & ... | +| test.c:241:15:241:18 | & ... | semmle.label | & ... | +| test.c:241:15:241:18 | & ... | semmle.label | & ... | +| test.c:244:12:244:13 | ip | semmle.label | ip | +| test.c:246:9:246:12 | & ... | semmle.label | & ... | +| test.c:247:9:247:12 | & ... | semmle.label | & ... | +| test.c:252:16:252:18 | & ... | semmle.label | & ... | +| test.c:254:11:254:13 | ps1 | semmle.label | ps1 | +| test.c:255:11:255:13 | & ... | semmle.label | & ... | +| test.c:256:10:256:12 | ps1 | semmle.label | ps1 | +| test.c:257:10:257:12 | & ... | semmle.label | & ... | +subpaths +#select +| test.c:8:3:8:14 | (short *)... | test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:8:12:8:14 | & ... | address-of expression | +| test.c:9:3:9:12 | (int *)... | test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:9:10:9:12 | & ... | address-of expression | +| test.c:10:3:10:13 | (long *)... | test.c:10:11:10:13 | & ... | test.c:10:11:10:13 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:10:11:10:13 | & ... | address-of expression | +| test.c:11:3:11:14 | (float *)... | test.c:11:12:11:14 | & ... | test.c:11:12:11:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:11:12:11:14 | & ... | address-of expression | +| test.c:12:3:12:15 | (double *)... | test.c:12:13:12:15 | & ... | test.c:12:13:12:15 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:12:13:12:15 | & ... | address-of expression | +| test.c:17:3:17:12 | (int *)... | test.c:17:10:17:12 | & ... | test.c:17:10:17:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:17:10:17:12 | & ... | address-of expression | +| test.c:18:3:18:13 | (long *)... | test.c:18:11:18:13 | & ... | test.c:18:11:18:13 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:18:11:18:13 | & ... | address-of expression | +| test.c:19:3:19:14 | (float *)... | test.c:19:12:19:14 | & ... | test.c:19:12:19:14 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:19:12:19:14 | & ... | address-of expression | +| test.c:20:3:20:15 | (double *)... | test.c:20:13:20:15 | & ... | test.c:20:13:20:15 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:20:13:20:15 | & ... | address-of expression | +| test.c:27:3:27:13 | (long *)... | test.c:27:11:27:13 | & ... | test.c:27:11:27:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:27:11:27:13 | & ... | address-of expression | +| test.c:28:3:28:15 | (double *)... | test.c:28:13:28:15 | & ... | test.c:28:13:28:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:28:13:28:15 | & ... | address-of expression | +| test.c:35:3:35:13 | (long *)... | test.c:35:11:35:13 | & ... | test.c:35:11:35:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:35:11:35:13 | & ... | address-of expression | +| test.c:36:3:36:15 | (double *)... | test.c:36:13:36:15 | & ... | test.c:36:13:36:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:36:13:36:15 | & ... | address-of expression | +| test.c:61:3:61:13 | (long *)... | test.c:61:11:61:13 | & ... | test.c:61:11:61:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:61:11:61:13 | & ... | address-of expression | +| test.c:62:3:62:15 | (double *)... | test.c:62:13:62:15 | & ... | test.c:62:13:62:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:62:13:62:15 | & ... | address-of expression | +| test.c:77:3:77:13 | (short *)... | test.c:75:14:75:16 | & ... | test.c:77:12:77:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:78:3:78:11 | (int *)... | test.c:75:14:75:16 | & ... | test.c:78:10:78:11 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:79:3:79:13 | (float *)... | test.c:75:14:75:16 | & ... | test.c:79:12:79:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:80:3:80:12 | (long *)... | test.c:75:14:75:16 | & ... | test.c:80:11:80:12 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:81:3:81:14 | (double *)... | test.c:75:14:75:16 | & ... | test.c:81:13:81:14 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:87:3:87:11 | (int *)... | test.c:84:14:84:16 | & ... | test.c:87:10:87:11 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:88:3:88:13 | (float *)... | test.c:84:14:84:16 | & ... | test.c:88:12:88:13 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:89:3:89:12 | (long *)... | test.c:84:14:84:16 | & ... | test.c:89:11:89:12 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:90:3:90:14 | (double *)... | test.c:84:14:84:16 | & ... | test.c:90:13:90:14 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:98:3:98:12 | (long *)... | test.c:93:14:93:16 | & ... | test.c:98:11:98:12 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | +| test.c:99:3:99:14 | (double *)... | test.c:93:14:93:16 | & ... | test.c:99:13:99:14 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | +| test.c:107:3:107:12 | (long *)... | test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | +| test.c:108:3:108:14 | (double *)... | test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | +| test.c:130:10:130:17 | (int *)... | test.c:135:21:135:23 | & ... | test.c:130:17:130:17 | v | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:135:21:135:23 | & ... | address-of expression | +| test.c:130:10:130:17 | (int *)... | test.c:174:13:174:14 | s2 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:174:13:174:14 | s2 | pointer base type short | +| test.c:130:10:130:17 | (int *)... | test.c:179:13:179:14 | s3 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:179:13:179:14 | s3 | pointer base type short | +| test.c:130:10:130:17 | (int *)... | test.c:189:14:189:26 | call to aligned_alloc | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:189:14:189:26 | call to aligned_alloc | call to aligned_alloc | +| test.c:158:3:158:20 | (size_t *)... | test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:158:13:158:20 | & ... | address-of expression | +| test.c:162:3:162:18 | (S3 *)... | test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:162:16:162:18 | & ... | address-of expression | +| test.c:168:3:168:17 | (S3 *)... | test.c:166:24:166:29 | call to malloc | test.c:168:16:168:17 | s1 | Cast from pointer with 16-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:166:24:166:29 | call to malloc | call to malloc | +| test.c:173:3:173:14 | (size_t *)... | test.c:173:13:173:14 | s2 | test.c:173:13:173:14 | s2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:173:13:173:14 | s2 | pointer base type short | +| test.c:178:3:178:14 | (size_t *)... | test.c:178:13:178:14 | s3 | test.c:178:13:178:14 | s3 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:178:13:178:14 | s3 | pointer base type short | +| test.c:186:3:186:14 | (size_t *)... | test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:183:14:183:26 | call to aligned_alloc | call to aligned_alloc | +| test.c:216:3:216:11 | (int *)... | test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:216:10:216:11 | p2 | pointer base type short | +| test.c:217:3:217:12 | (long *)... | test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:217:11:217:12 | p2 | pointer base type short | +| test.c:218:3:218:13 | (float *)... | test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:218:12:218:13 | p2 | pointer base type short | +| test.c:219:3:219:14 | (double *)... | test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:219:13:219:14 | p2 | pointer base type short | +| test.c:225:3:225:11 | (int *)... | test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:226:3:226:13 | (float *)... | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:227:3:227:12 | (long *)... | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:228:3:228:14 | (double *)... | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:256:3:256:12 | (int *)... | test.c:252:16:252:18 | & ... | test.c:256:10:256:12 | ps1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:252:16:252:18 | & ... | address-of expression | +| test.c:257:3:257:12 | (int *)... | test.c:257:10:257:12 | & ... | test.c:257:10:257:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:257:10:257:12 | & ... | address-of expression | diff --git a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref new file mode 100644 index 0000000000..9e655176f7 --- /dev/null +++ b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref @@ -0,0 +1 @@ +rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP36-C/test.c b/c/cert/test/rules/EXP36-C/test.c new file mode 100644 index 0000000000..587bc6a183 --- /dev/null +++ b/c/cert/test/rules/EXP36-C/test.c @@ -0,0 +1,258 @@ +#include +#include +#include + +void test_direct_cast_alignment() { + char c1 = 1; // assuming 1-byte alignment + (char *)&c1; // COMPLIANT + (short *)&c1; // NON_COMPLIANT + (int *)&c1; // NON_COMPLIANT + (long *)&c1; // NON_COMPLIANT + (float *)&c1; // NON_COMPLIANT + (double *)&c1; // NON_COMPLIANT + + short s1 = 1; // assuming 2-byte alignment + (char *)&s1; // COMPLIANT + (short *)&s1; // COMPLIANT + (int *)&s1; // NON_COMPLIANT + (long *)&s1; // NON_COMPLIANT + (float *)&s1; // NON_COMPLIANT + (double *)&s1; // NON_COMPLIANT + + int i1 = 1; // assuming 4-byte alignment + (char *)&i1; // COMPLIANT + (short *)&i1; // COMPLIANT + (int *)&i1; // COMPLIANT + (float *)&i1; // COMPLIANT + (long *)&i1; // NON_COMPLIANT - assuming 8 byte alignment for longs + (double *)&i1; // NON_COMPLIANT + + float f1 = 1; // assuming 4-byte alignment + (char *)&f1; // COMPLIANT + (short *)&f1; // COMPLIANT + (int *)&f1; // COMPLIANT + (float *)&f1; // COMPLIANT + (long *)&f1; // NON_COMPLIANT + (double *)&f1; // NON_COMPLIANT + + long l1 = 1; // assuming 8-byte alignment + (char *)&l1; // COMPLIANT + (short *)&l1; // COMPLIANT + (int *)&l1; // COMPLIANT + (float *)&l1; // COMPLIANT + (long *)&l1; // COMPLIANT + (double *)&l1; // COMPLIANT + + double d1 = 1; // assuming 8-byte alignment + (char *)&d1; // COMPLIANT + (short *)&d1; // COMPLIANT + (int *)&d1; // COMPLIANT + (float *)&d1; // COMPLIANT + (long *)&d1; // COMPLIANT + (double *)&d1; // COMPLIANT +} + +void custom_aligned_types() { + alignas(int) char c1 = 1; + (char *)&c1; // COMPLIANT + (short *)&c1; // COMPLIANT + (int *)&c1; // COMPLIANT + (float *)&c1; // COMPLIANT + (long *)&c1; // NON_COMPLIANT + (double *)&c1; // NON_COMPLIANT + + alignas(32) char c2 = 1; + (char *)&c2; // COMPLIANT + (short *)&c2; // COMPLIANT + (int *)&c2; // COMPLIANT + (float *)&c2; // COMPLIANT + (long *)&c2; // COMPLIANT + (double *)&c2; // COMPLIANT +} + +void test_via_void_ptr_var_direct() { + char c1 = 1; + void *v1 = &c1; + (char *)v1; // COMPLIANT + (short *)v1; // NON_COMPLIANT + (int *)v1; // NON_COMPLIANT + (float *)v1; // NON_COMPLIANT + (long *)v1; // NON_COMPLIANT + (double *)v1; // NON_COMPLIANT + + short s1 = 1; + void *v2 = &s1; + (char *)v2; // COMPLIANT + (short *)v2; // COMPLIANT + (int *)v2; // NON_COMPLIANT + (float *)v2; // NON_COMPLIANT + (long *)v2; // NON_COMPLIANT + (double *)v2; // NON_COMPLIANT + + int i1 = 1; + void *v3 = &i1; + (char *)v3; // COMPLIANT + (short *)v3; // COMPLIANT + (int *)v3; // COMPLIANT + (float *)v3; // COMPLIANT + (long *)v3; // NON_COMPLIANT - assuming 8 byte alignment for longs + (double *)v3; // NON_COMPLIANT - but only on x64 + + float f1 = 1; + void *v4 = &f1; + (char *)v4; // COMPLIANT + (short *)v4; // COMPLIANT + (int *)v4; // COMPLIANT + (float *)v4; // COMPLIANT + (long *)v4; // NON_COMPLIANT - assuming 8 byte alignment for longs + (double *)v4; // NON_COMPLIANT + + long l1 = 1; + void *v5 = &l1; + (char *)v5; // COMPLIANT + (short *)v5; // COMPLIANT + (int *)v5; // COMPLIANT + (float *)v5; // COMPLIANT + (long *)v5; // COMPLIANT + (double *)v5; // COMPLIANT + + double d1 = 1; + void *v6 = &d1; + (char *)v6; // COMPLIANT + (short *)v6; // COMPLIANT + (int *)v6; // COMPLIANT + (float *)v6; // COMPLIANT + (long *)v6; // COMPLIANT + (double *)v6; // COMPLIANT +} + +int *cast_away(void *v) { + return (int *)v; // compliance depends on context +} + +void test_via_void_indirect() { + char c1 = 1; + cast_away((void *)&c1); // NON_COMPLIANT + + int i1 = 1; + cast_away((void *)&i1); // COMPLIANT +} + +struct S1 { + char c1; + unsigned char data[8]; +}; + +struct S2 { + char c1; + alignas(size_t) unsigned char data[8]; +}; + +struct S3 { + char c1; + alignas(64) unsigned char data[8]; +}; + +void test_struct_alignment() { + struct S1 s1; + (size_t *)&s1.data; // NON_COMPLIANT + + struct S2 s2; + (size_t *)&s2.data; // COMPLIANT + (struct S3 *)&s2; // NON_COMPLIANT +} + +void test_malloc_alignment_and_pointer_arithmetic() { + short *s1 = (short *)malloc(64); + (size_t *)&s1; // COMPLIANT + (struct S3 *)s1; // NON_COMPLIANT - over-aligned struct + cast_away(s1); // COMPLIANT + + short *s2 = s1 + 1; + (char *)s2; // COMPLIANT + (size_t *)s2; // NON_COMPLIANT + cast_away(s2); // NON_COMPLIANT + + short *s3 = &s1[1]; + (char *)s3; // COMPLIANT + (size_t *)s3; // NON_COMPLIANT + cast_away(s3); // NON_COMPLIANT +} + +void test_aligned_alloc_alignment() { + void *v1 = aligned_alloc(4, 8); + (char *)v1; // COMPLIANT + (int *)v1; // COMPLIANT + (size_t *)v1; // NON_COMPLIANT + cast_away(v1); // COMPLIANT + + void *v2 = aligned_alloc(2, 8); + cast_away(v2); // NON_COMPLIANT +} + +void test_standalone_pointer_cast_alignment(void *p1, short *p2) { + void *v1; + + // void* direct + (char *)p1; // COMPLIANT + (short *)p1; // COMPLIANT + (int *)p1; // COMPLIANT + (float *)p1; // COMPLIANT + (long *)p1; // COMPLIANT + (double *)p1; // COMPLIANT + + // void* indirect via void* + v1 = p1; // COMPLIANT + (char *)v1; // COMPLIANT + (short *)v1; // COMPLIANT + (int *)v1; // COMPLIANT + (float *)v1; // COMPLIANT + (long *)v1; // COMPLIANT + (double *)v1; // COMPLIANT + + // short* direct + (char *)p2; // COMPLIANT + (short *)p2; // COMPLIANT + (int *)p2; // NON_COMPLIANT + (long *)p2; // NON_COMPLIANT + (float *)p2; // NON_COMPLIANT + (double *)p2; // NON_COMPLIANT + + // short* indirect via void* + v1 = p2; // COMPLIANT + (char *)v1; // COMPLIANT + (short *)v1; // COMPLIANT + (int *)v1; // NON_COMPLIANT + (float *)v1; // NON_COMPLIANT + (long *)v1; // NON_COMPLIANT + (double *)v1; // NON_COMPLIANT +} + +void test_array_element_cast_alignment() { + char *acp[3]; + int *aip[3]; + + int i = 0; + char c = ' '; + char *cp = &c; // COMPLIANT + int *ip = &i; // COMPLIANT + + char **cpp = &acp; // COMPLIANT + int **ipp = &aip; // COMPLIANT + + acp[0] = cp; // COMPLIANT + acp[1] = ip; // NON_COMPLIANT[FALSE_NEGATIVE] + + cpp = &acp; // COMPLIANT + cpp = &ipp; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +void test_pointer_dereference_barrier() { + short s1 = 0; + short *ps1 = &s1; + *ps1 = 1; + (char *)ps1; // COMPLIANT + (char *)&s1; // COMPLIANT + (int *)ps1; // NON_COMPLIANT + (int *)&s1; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected b/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected index 4c18bb2672..8daaf8361a 100644 --- a/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected @@ -1,11 +1,15 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallFunctionPointerWithIncompatibleType.ql:45,54-62) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallFunctionPointerWithIncompatibleType.ql:46,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallFunctionPointerWithIncompatibleType.ql:50,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallFunctionPointerWithIncompatibleType.ql:55,43-51) edges -| test.c:48:68:48:70 | fns [f1] | test.c:49:3:49:5 | fns [f1] | -| test.c:49:3:49:5 | fns [f1] | test.c:49:8:49:9 | f1 | -| test.c:61:28:61:29 | f2 | test.c:62:3:62:11 | v1_called | -| test.c:73:3:73:5 | fns [post update] [f1] | test.c:75:45:75:48 | & ... [f1] | -| test.c:73:3:73:13 | ... = ... | test.c:73:3:73:5 | fns [post update] [f1] | -| test.c:73:12:73:13 | v2 | test.c:73:3:73:13 | ... = ... | -| test.c:75:45:75:48 | & ... [f1] | test.c:48:68:48:70 | fns [f1] | +| test.c:48:68:48:70 | fns [f1] | test.c:49:3:49:5 | fns [f1] | provenance | | +| test.c:49:3:49:5 | fns [f1] | test.c:49:8:49:9 | f1 | provenance | | +| test.c:61:28:61:29 | f2 | test.c:62:3:62:11 | v1_called | provenance | | +| test.c:73:3:73:5 | fns [post update] [f1] | test.c:75:45:75:48 | & ... [f1] | provenance | | +| test.c:73:3:73:13 | ... = ... | test.c:73:3:73:5 | fns [post update] [f1] | provenance | | +| test.c:73:12:73:13 | v2 | test.c:73:3:73:13 | ... = ... | provenance | | +| test.c:75:45:75:48 | & ... [f1] | test.c:48:68:48:70 | fns [f1] | provenance | | nodes | test.c:48:68:48:70 | fns [f1] | semmle.label | fns [f1] | | test.c:49:3:49:5 | fns [f1] | semmle.label | fns [f1] | diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected.clang b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected.clang new file mode 100644 index 0000000000..64026d92ea --- /dev/null +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected.clang @@ -0,0 +1,3 @@ +| test.c:83:12:83:16 | call to atan2 | Argument $@ in call to atan2 is incompatible with parameter double __y. | test.c:83:18:83:18 | c | c | +| test.c:93:3:93:12 | call to test_func1 | Argument $@ in call to test_func1 is incompatible with parameter short p1. | test.c:93:14:93:15 | p1 | p1 | +| test.c:94:3:94:12 | call to test_func1 | Argument $@ in call to test_func1 is incompatible with parameter short p1. | test.c:94:14:94:15 | p2 | p2 | diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected.gcc b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected.gcc new file mode 100644 index 0000000000..64026d92ea --- /dev/null +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected.gcc @@ -0,0 +1,3 @@ +| test.c:83:12:83:16 | call to atan2 | Argument $@ in call to atan2 is incompatible with parameter double __y. | test.c:83:18:83:18 | c | c | +| test.c:93:3:93:12 | call to test_func1 | Argument $@ in call to test_func1 is incompatible with parameter short p1. | test.c:93:14:93:15 | p1 | p1 | +| test.c:94:3:94:12 | call to test_func1 | Argument $@ in call to test_func1 is incompatible with parameter short p1. | test.c:94:14:94:15 | p2 | p2 | diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected new file mode 100644 index 0000000000..381e409d2a --- /dev/null +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected @@ -0,0 +1,67 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:66,38-46) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:69,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:74,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:107,23-31) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:116,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:116,45-53) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAccessVariableViaPointerOfIncompatibleType.ql:138,27-35) +edges +| test.c:49:8:49:9 | s3 | test.c:50:8:50:9 | s1 | provenance | | +| test.c:60:16:60:18 | E1A | test.c:61:16:61:17 | e1 | provenance | | +| test.c:60:16:60:18 | E1A | test.c:65:10:65:12 | & ... | provenance | | +| test.c:68:22:68:22 | v | test.c:68:41:68:41 | v | provenance | | +| test.c:72:13:72:15 | & ... | test.c:68:22:68:22 | v | provenance | | +| test.c:74:13:74:15 | & ... | test.c:68:22:68:22 | v | provenance | | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | provenance | | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | provenance | | +| test.c:98:32:98:38 | call to realloc | test.c:99:3:99:4 | s3 | provenance | | +| test.c:98:32:98:38 | call to realloc | test.c:100:10:100:11 | s3 | provenance | | +| test.c:98:40:98:41 | s2 | test.c:98:32:98:38 | call to realloc | provenance | Config | +nodes +| test.c:6:19:6:20 | & ... | semmle.label | & ... | +| test.c:11:10:11:11 | & ... | semmle.label | & ... | +| test.c:13:17:13:19 | & ... | semmle.label | & ... | +| test.c:15:17:15:19 | & ... | semmle.label | & ... | +| test.c:19:18:19:20 | & ... | semmle.label | & ... | +| test.c:20:20:20:22 | & ... | semmle.label | & ... | +| test.c:22:11:22:13 | & ... | semmle.label | & ... | +| test.c:27:17:27:19 | & ... | semmle.label | & ... | +| test.c:28:10:28:12 | & ... | semmle.label | & ... | +| test.c:29:13:29:15 | & ... | semmle.label | & ... | +| test.c:30:19:30:21 | & ... | semmle.label | & ... | +| test.c:31:16:31:18 | & ... | semmle.label | & ... | +| test.c:47:8:47:9 | s2 | semmle.label | s2 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:50:8:50:9 | s1 | semmle.label | s1 | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:61:16:61:17 | e1 | semmle.label | e1 | +| test.c:65:10:65:12 | & ... | semmle.label | & ... | +| test.c:68:22:68:22 | v | semmle.label | v | +| test.c:68:41:68:41 | v | semmle.label | v | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:99:3:99:4 | s3 | semmle.label | s3 | +| test.c:100:10:100:11 | s3 | semmle.label | s3 | +subpaths +#select +| test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | Cast from float to int results in an incompatible pointer base type. | +| test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | Cast from short[2] to int results in an incompatible pointer base type. | +| test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | Cast from short[2] to short[4] results in an incompatible pointer base type. | +| test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | Cast from char to signed char results in an incompatible pointer base type. | +| test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | Cast from int to unsigned int results in an incompatible pointer base type. | +| test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | Cast from (unnamed class/struct/union) to (unnamed class/struct/union) results in an incompatible pointer base type. | +| test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | Cast from S1 to (unnamed class/struct/union) results in an incompatible pointer base type. | +| test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | Cast from (unnamed class/struct/union) to S1 results in an incompatible pointer base type. | +| test.c:68:41:68:41 | v | test.c:72:13:72:15 | & ... | test.c:68:41:68:41 | v | Cast from float to int results in an incompatible pointer base type. | +| test.c:99:3:99:4 | s3 | test.c:98:40:98:41 | s2 | test.c:99:3:99:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected.qcc b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected.qcc new file mode 100644 index 0000000000..b327629aae --- /dev/null +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected.qcc @@ -0,0 +1,62 @@ +edges +| test.c:49:8:49:9 | s3 | test.c:50:8:50:9 | s1 | +| test.c:60:16:60:18 | E1A | test.c:61:16:61:17 | e1 | +| test.c:60:16:60:18 | E1A | test.c:65:10:65:12 | & ... | +| test.c:68:22:68:22 | v | test.c:68:41:68:41 | v | +| test.c:72:13:72:15 | & ... | test.c:68:22:68:22 | v | +| test.c:74:13:74:15 | & ... | test.c:68:22:68:22 | v | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | +| test.c:98:32:98:38 | call to realloc | test.c:99:3:99:4 | s3 | +| test.c:98:32:98:38 | call to realloc | test.c:100:10:100:11 | s3 | +| test.c:98:40:98:41 | s2 | test.c:98:32:98:38 | call to realloc | +nodes +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/stdlib.h:98:42:98:47 | __func | semmle.label | __func | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/stdlib.h:98:50:98:53 | 0 | semmle.label | 0 | +| test.c:6:19:6:20 | & ... | semmle.label | & ... | +| test.c:11:10:11:11 | & ... | semmle.label | & ... | +| test.c:13:17:13:19 | & ... | semmle.label | & ... | +| test.c:15:17:15:19 | & ... | semmle.label | & ... | +| test.c:19:18:19:20 | & ... | semmle.label | & ... | +| test.c:20:20:20:22 | & ... | semmle.label | & ... | +| test.c:22:11:22:13 | & ... | semmle.label | & ... | +| test.c:27:17:27:19 | & ... | semmle.label | & ... | +| test.c:28:10:28:12 | & ... | semmle.label | & ... | +| test.c:29:13:29:15 | & ... | semmle.label | & ... | +| test.c:30:19:30:21 | & ... | semmle.label | & ... | +| test.c:31:16:31:18 | & ... | semmle.label | & ... | +| test.c:47:8:47:9 | s2 | semmle.label | s2 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:50:8:50:9 | s1 | semmle.label | s1 | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:61:16:61:17 | e1 | semmle.label | e1 | +| test.c:65:10:65:12 | & ... | semmle.label | & ... | +| test.c:68:22:68:22 | v | semmle.label | v | +| test.c:68:41:68:41 | v | semmle.label | v | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:99:3:99:4 | s3 | semmle.label | s3 | +| test.c:100:10:100:11 | s3 | semmle.label | s3 | +subpaths +#select +| test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | Cast from float to int results in an incompatible pointer base type. | +| test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | Cast from short[2] to int results in an incompatible pointer base type. | +| test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | Cast from short[2] to short[4] results in an incompatible pointer base type. | +| test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | Cast from char to signed char results in an incompatible pointer base type. | +| test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | Cast from int to unsigned int results in an incompatible pointer base type. | +| test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | Cast from struct to struct results in an incompatible pointer base type. | +| test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | Cast from S1 to struct results in an incompatible pointer base type. | +| test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | Cast from struct to S1 results in an incompatible pointer base type. | +| test.c:68:41:68:41 | v | test.c:72:13:72:15 | & ... | test.c:68:41:68:41 | v | Cast from float to int results in an incompatible pointer base type. | +| test.c:99:3:99:4 | s3 | test.c:98:40:98:41 | s2 | test.c:99:3:99:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref new file mode 100644 index 0000000000..41eb7db3b1 --- /dev/null +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref @@ -0,0 +1 @@ +rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c new file mode 100644 index 0000000000..75d1eed462 --- /dev/null +++ b/c/cert/test/rules/EXP39-C/test.c @@ -0,0 +1,102 @@ +#include +#include + +void test_incompatible_arithmetic() { + float f = 0.0f; + int *p = (int *)&f; // NON_COMPLIANT - arithmetic types are not compatible + // with each other + (*p)++; + + short s[2]; + (int *)&s; // NON_COMPLIANT + + (short(*)[4]) & s; // NON_COMPLIANT - array of size 2 is not compatible with + // array of size 4 (n1570 6.7.6.2 paragraph 7) + (short(*)[2]) & s; // COMPLIANT + + // char may be signed or unsigned, and so is not compatible with either + char c1; + (signed char *)&c1; // NON_COMPLIANT + (unsigned char *)&c1; // COMPLIANT - the underlying byte representation is + // always compatible + (char *)&c1; // COMPLIANT - same type + + // int is defined as signed, so is compatible with all the signed versions + // (long, short etc. are similar) + int i1; + (signed int *)&i1; // COMPLIANT + (int *)&i1; // COMPLIANT + (signed *)&i1; // COMPLIANT + (unsigned int *)&i1; // NON_COMPLIANT + (const int *)&i1; // COMPLIANT - adding a const specifier is permitted +} + +struct { + int a; +} * s1; +struct { + int a; +} * s2; +struct S1 { + int a; +} * s3; +struct S1 *s4; + +void test_incompatible_structs() { + // s1 and s2 do not have tags, and are therefore not compatible + s1 = s2; // NON_COMPLIANT + // s3 tag is inconsistent with s1 tag + s1 = s3; // NON_COMPLIANT + s3 = s1; // NON_COMPLIANT + // s4 tag is consistent with s3 tag + s3 = s4; // COMPLIANT + s4 = s3; // COMPLIANT +} + +enum E1 { E1A, E1B }; +enum E2 { E2A, E2B }; + +void test_enums() { + enum E1 e1 = E1A; + enum E2 e2 = e1; // COMPLIANT + // Enums are also compatible with one of `char`, a signed integer type or an + // unsigned integer type. It is implementation defined which is used, so + // choose an appropriate type below for this test + (int *)&e1; // COMPLIANT +} + +int *void_cast(void *v) { return (int *)v; } + +void test_indirect_cast() { + float f1 = 0.0f; + void_cast(&f1); // NON_COMPLIANT + int i1 = 0; + void_cast(&i1); // COMPLIANT +} + +signed f(int y) { return y; } +int g(signed int x) { return x; } + +// 6.7.6.3 p15 +void test_compatible_functions() { + signed (*f1)(int) = &g; // COMPLIANT + int (*g1)(signed int) = &f; // COMPLIANT +} + +struct S2 { + int a; + int b; +}; + +struct S3 { + int a; + int b; +}; + +void test_realloc() { + struct S2 *s2 = (struct S2 *)malloc(sizeof(struct S2)); + struct S3 *s3 = (struct S3 *)realloc(s2, sizeof(struct S3)); + s3->a; // NON_COMPLIANT + memset(s3, 0, sizeof(struct S3)); + s3->a; // COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP40-C/DoNotModifyConstantObjects.expected b/c/cert/test/rules/EXP40-C/DoNotModifyConstantObjects.expected new file mode 100644 index 0000000000..2ac874e770 --- /dev/null +++ b/c/cert/test/rules/EXP40-C/DoNotModifyConstantObjects.expected @@ -0,0 +1,33 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyConstantObjects.ql:40,30-38) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyConstantObjects.ql:41,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyConstantObjects.ql:47,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyConstantObjects.ql:52,19-27) +edges +| test.c:5:8:5:9 | & ... | test.c:6:4:6:5 | aa | provenance | | +| test.c:26:15:26:15 | a | test.c:27:4:27:4 | a | provenance | | +| test.c:34:13:34:14 | & ... | test.c:39:7:39:8 | p1 | provenance | | +| test.c:39:7:39:8 | p1 | test.c:26:15:26:15 | a | provenance | | +| test.c:40:7:40:9 | * ... | test.c:26:15:26:15 | a | provenance | | +| test.c:59:7:59:8 | & ... | test.c:60:4:60:4 | p | provenance | | +| test.c:79:11:79:16 | call to strchr | test.c:81:6:81:12 | ... ++ | provenance | | +nodes +| test.c:5:8:5:9 | & ... | semmle.label | & ... | +| test.c:6:4:6:5 | aa | semmle.label | aa | +| test.c:26:15:26:15 | a | semmle.label | a | +| test.c:27:4:27:4 | a | semmle.label | a | +| test.c:34:13:34:14 | & ... | semmle.label | & ... | +| test.c:39:7:39:8 | p1 | semmle.label | p1 | +| test.c:40:7:40:9 | * ... | semmle.label | * ... | +| test.c:59:7:59:8 | & ... | semmle.label | & ... | +| test.c:60:4:60:4 | p | semmle.label | p | +| test.c:74:12:74:12 | s | semmle.label | s | +| test.c:79:11:79:16 | call to strchr | semmle.label | call to strchr | +| test.c:81:6:81:12 | ... ++ | semmle.label | ... ++ | +subpaths +#select +| test.c:6:4:6:5 | aa | test.c:5:8:5:9 | & ... | test.c:6:4:6:5 | aa | Const variable assigned with non const-value. | +| test.c:27:4:27:4 | a | test.c:34:13:34:14 | & ... | test.c:27:4:27:4 | a | Const variable assigned with non const-value. | +| test.c:27:4:27:4 | a | test.c:40:7:40:9 | * ... | test.c:27:4:27:4 | a | Const variable assigned with non const-value. | +| test.c:60:4:60:4 | p | test.c:59:7:59:8 | & ... | test.c:60:4:60:4 | p | Const variable assigned with non const-value. | +| test.c:74:12:74:12 | s | test.c:74:12:74:12 | s | test.c:74:12:74:12 | s | Const variable assigned with non const-value. | +| test.c:81:6:81:12 | ... ++ | test.c:79:11:79:16 | call to strchr | test.c:81:6:81:12 | ... ++ | Const variable assigned with non const-value. | diff --git a/c/cert/test/rules/EXP40-C/DoNotModifyConstantObjects.qlref b/c/cert/test/rules/EXP40-C/DoNotModifyConstantObjects.qlref new file mode 100644 index 0000000000..c07ac22f37 --- /dev/null +++ b/c/cert/test/rules/EXP40-C/DoNotModifyConstantObjects.qlref @@ -0,0 +1 @@ +rules/EXP40-C/DoNotModifyConstantObjects.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP40-C/test.c b/c/cert/test/rules/EXP40-C/test.c new file mode 100644 index 0000000000..dca79d3d36 --- /dev/null +++ b/c/cert/test/rules/EXP40-C/test.c @@ -0,0 +1,85 @@ +void f1() { + const int a = 3; + int *aa; + + aa = &a; + *aa = 100; // NON_COMPLIANT +} + +void f1a() { + const int a = 3; + int *aa; + + aa = &a; // COMPLIANT +} + +void f2() { + int a = 3; + int *aa; + a = 3; + + aa = &a; + *aa = a; + *aa = &a; +} + +void f4a(int *a) { + *a = 100; // NON_COMPLIANT +} + +void f4b(int *a) {} + +void f4() { + const int a = 100; + int *p1 = &a; // COMPLIANT + const int **p2; + + *p2 = &a; // COMPLIANT + + f4a(p1); // COMPLIANT + f4a(*p2); // COMPLIANT +} + +void f5() { + const int a = 100; + int *p1 = &a; // COMPLIANT + const int **p2; + + *p2 = &a; // COMPLIANT + + f4b(p1); + f4b(*p2); +} + +#include + +void f6a() { + char *p; + const char c = 'A'; + p = &c; + *p = 0; // NON_COMPLIANT +} + +void f6b() { + const char **cpp; + char *p; + const char c = 'A'; + cpp = &p; + *cpp = &c; + *p = 0; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +const char s[] = "foo"; // source +void f7() { + *(char *)s = '\0'; // NON_COMPLIANT +} + +const char *f8(const char *pathname) { + char *slash; + slash = strchr(pathname, '/'); + if (slash) { + *slash++ = '\0'; // NON_COMPLIANT + return slash; + } + return pathname; +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP42-C/DoNotComparePaddingData.testref b/c/cert/test/rules/EXP42-C/DoNotComparePaddingData.testref new file mode 100644 index 0000000000..fb0d5d283b --- /dev/null +++ b/c/cert/test/rules/EXP42-C/DoNotComparePaddingData.testref @@ -0,0 +1 @@ +c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.testref b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.testref new file mode 100644 index 0000000000..ef17bca58a --- /dev/null +++ b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.testref @@ -0,0 +1 @@ +c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected new file mode 100644 index 0000000000..40009edc03 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected @@ -0,0 +1,16 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:47,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:48,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:52,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:58,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:61,58-66) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:77,64-72) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (RestrictPointerReferencesOverlappingObject.ql:78,64-72) +| test.c:18:22:18:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:18:17:18:18 | i3 | i3 | test.c:18:22:18:23 | i2 | the object pointed to by i2 | +| test.c:19:8:19:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:5:15:5:16 | g1 | g1 | test.c:19:8:19:9 | g2 | the object pointed to by g2 | +| test.c:20:8:20:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:16:17:16:18 | i1 | i1 | test.c:20:8:20:9 | i2 | the object pointed to by i2 | +| test.c:27:10:27:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:23:19:23:20 | i5 | i5 | test.c:19:8:19:9 | g2 | the same source value | +| test.c:28:10:28:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:22:19:22:20 | i4 | i4 | test.c:19:8:19:9 | g2 | the same source value | +| test.c:39:22:39:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:39:17:39:18 | px | px | test.c:38:28:38:30 | & ... | v1 via address-of | +| test.c:45:10:45:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:42:19:42:20 | pz | pz | test.c:43:10:43:14 | & ... | v1 via address-of | +| test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:41:19:41:20 | py | py | test.c:43:10:43:14 | & ... | v1 via address-of | +| test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:41:19:41:20 | py | py | test.c:45:10:45:14 | & ... | v1 via address-of | diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref new file mode 100644 index 0000000000..81043b56c2 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref @@ -0,0 +1 @@ +rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP43-C/test.c b/c/cert/test/rules/EXP43-C/test.c new file mode 100644 index 0000000000..3bf7cfa490 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/test.c @@ -0,0 +1,100 @@ +#include +#include +#include + +int *restrict g1; +int *restrict g2; +int *restrict g1_1; +int *g2_1; + +struct s1 { + int x, y, z; +}; +struct s1 v1; + +void test_global_local() { + int *restrict i1 = g1; // COMPLIANT + int *restrict i2 = g2; // COMPLIANT + int *restrict i3 = i2; // NON_COMPLIANT + g1 = g2; // NON_COMPLIANT + i1 = i2; // NON_COMPLIANT + { + int *restrict i4; + int *restrict i5; + int *restrict i6; + i4 = g1; // COMPLIANT + i4 = (void *)0; // COMPLIANT + i5 = g1; // NON_COMPLIANT - block rather than statement scope matters + i4 = g1; // NON_COMPLIANT + i6 = g2; // COMPLIANT + } +} + +void test_global_local_1() { + g1_1 = g2_1; // COMPLIANT +} + +void test_structs() { + struct s1 *restrict p1 = &v1; + int *restrict px = &v1.x; // NON_COMPLIANT + { + int *restrict py; + int *restrict pz; + py = &v1.y; // COMPLIANT + py = (int *)0; + pz = &v1.z; // NON_COMPLIANT - block rather than statement scope matters + py = &v1.y; // NON_COMPLIANT + } +} + +void copy(int *restrict p1, int *restrict p2, size_t s) { + for (size_t i = 0; i < s; ++i) { + p2[i] = p1[i]; + } +} + +void test_restrict_params() { + int i1 = 1; + int i2 = 2; + copy(&i1, &i1, 1); // NON_COMPLIANT + copy(&i1, &i2, 1); // COMPLIANT + + int x[10]; + int *px = &x[0]; + copy(&x[0], &x[1], 1); // COMPLIANT - non overlapping + copy(&x[0], &x[1], 2); // NON_COMPLIANT - overlapping + copy(&x[0], (int *)x[0], 1); // COMPLIANT - non overlapping + copy(&x[0], px, 1); // NON_COMPLIANT - overlapping +} + +void test_strcpy() { + char s1[] = "my test string"; + char s2[] = "my other string"; + strcpy(&s1, &s1 + 3); // NON_COMPLIANT + strcpy(&s2, &s1); // COMPLIANT +} + +void test_memcpy() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memcpy(&s1, &s1 + 3, 5); // NON_COMPLIANT + memcpy(&s2, &s1 + 3, 5); // COMPLIANT +} + +void test_memmove() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memmove(&s1, &s1 + 3, 5); // COMPLIANT - memmove is allowed to overlap + memmove(&s2, &s1 + 3, 5); // COMPLIANT +} + +void test_scanf() { + char s1[200] = "%10s"; + scanf(&s1, &s1 + 4); // NON_COMPLIANT +} + +// TODO also consider the following: +// strncpy(), strncpy_s() +// strcat(), strcat_s() +// strncat(), strncat_s() +// strtok_s() \ No newline at end of file diff --git a/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected index c9252151d5..93d6de6b8a 100644 --- a/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected +++ b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected @@ -1,40 +1,12 @@ edges -| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | (const char *)... | -| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name | -| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name indirection | -| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | (const char *)... | -| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | -| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection | -| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | (const char *)... | -| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name | -| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name indirection | -| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | (LPCTSTR)... | -| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name | -| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name indirection | -| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | (LPCTSTR)... | -| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | -| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection | -| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | (LPCTSTR)... | -| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name | -| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name indirection | -subpaths +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | *file_name | provenance | | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | *file_name | provenance | | nodes -| test.c:20:15:20:23 | array to pointer conversion | semmle.label | array to pointer conversion | -| test.c:20:15:20:23 | file_name | semmle.label | file_name | | test.c:20:15:20:23 | scanf output argument | semmle.label | scanf output argument | -| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | -| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | -| test.c:21:8:21:16 | file_name | semmle.label | file_name | -| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | -| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | -| test.c:45:15:45:23 | array to pointer conversion | semmle.label | array to pointer conversion | -| test.c:45:15:45:23 | file_name | semmle.label | file_name | +| test.c:21:8:21:16 | *file_name | semmle.label | *file_name | | test.c:45:15:45:23 | scanf output argument | semmle.label | scanf output argument | -| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | -| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | -| test.c:46:29:46:37 | file_name | semmle.label | file_name | -| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | -| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +| test.c:46:29:46:37 | *file_name | semmle.label | *file_name | +subpaths #select -| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen((unnamed parameter 0)) | test.c:20:15:20:23 | file_name | user input (scanf) | -| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName) | test.c:45:15:45:23 | file_name | user input (scanf) | +| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | *file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen((unnamed parameter 0)). | test.c:20:15:20:23 | scanf output argument | user input (value read by scanf) | +| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | *file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName). | test.c:45:15:45:23 | scanf output argument | user input (value read by scanf) | diff --git a/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.clang b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.clang new file mode 100644 index 0000000000..d885a5b207 --- /dev/null +++ b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.clang @@ -0,0 +1,40 @@ +edges +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name indirection | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name indirection | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name indirection | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name indirection | +subpaths +nodes +| test.c:20:15:20:23 | array to pointer conversion | semmle.label | array to pointer conversion | +| test.c:20:15:20:23 | file_name | semmle.label | file_name | +| test.c:20:15:20:23 | scanf output argument | semmle.label | scanf output argument | +| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | +| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | +| test.c:21:8:21:16 | file_name | semmle.label | file_name | +| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | +| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | +| test.c:45:15:45:23 | array to pointer conversion | semmle.label | array to pointer conversion | +| test.c:45:15:45:23 | file_name | semmle.label | file_name | +| test.c:45:15:45:23 | scanf output argument | semmle.label | scanf output argument | +| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | +| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | +| test.c:46:29:46:37 | file_name | semmle.label | file_name | +| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +#select +| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen(__filename) | test.c:20:15:20:23 | file_name | user input (scanf) | +| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName) | test.c:45:15:45:23 | file_name | user input (scanf) | diff --git a/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.gcc b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.gcc new file mode 100644 index 0000000000..d885a5b207 --- /dev/null +++ b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.gcc @@ -0,0 +1,40 @@ +edges +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name indirection | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name indirection | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name indirection | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name indirection | +subpaths +nodes +| test.c:20:15:20:23 | array to pointer conversion | semmle.label | array to pointer conversion | +| test.c:20:15:20:23 | file_name | semmle.label | file_name | +| test.c:20:15:20:23 | scanf output argument | semmle.label | scanf output argument | +| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | +| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | +| test.c:21:8:21:16 | file_name | semmle.label | file_name | +| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | +| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | +| test.c:45:15:45:23 | array to pointer conversion | semmle.label | array to pointer conversion | +| test.c:45:15:45:23 | file_name | semmle.label | file_name | +| test.c:45:15:45:23 | scanf output argument | semmle.label | scanf output argument | +| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | +| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | +| test.c:46:29:46:37 | file_name | semmle.label | file_name | +| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +#select +| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen(__filename) | test.c:20:15:20:23 | file_name | user input (scanf) | +| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName) | test.c:45:15:45:23 | file_name | user input (scanf) | diff --git a/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.qcc b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.qcc new file mode 100644 index 0000000000..d885a5b207 --- /dev/null +++ b/c/cert/test/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.expected.qcc @@ -0,0 +1,40 @@ +edges +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name indirection | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | (const char *)... | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name | +| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name indirection | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name indirection | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | (LPCTSTR)... | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name | +| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name indirection | +subpaths +nodes +| test.c:20:15:20:23 | array to pointer conversion | semmle.label | array to pointer conversion | +| test.c:20:15:20:23 | file_name | semmle.label | file_name | +| test.c:20:15:20:23 | scanf output argument | semmle.label | scanf output argument | +| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | +| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... | +| test.c:21:8:21:16 | file_name | semmle.label | file_name | +| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | +| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection | +| test.c:45:15:45:23 | array to pointer conversion | semmle.label | array to pointer conversion | +| test.c:45:15:45:23 | file_name | semmle.label | file_name | +| test.c:45:15:45:23 | scanf output argument | semmle.label | scanf output argument | +| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | +| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... | +| test.c:46:29:46:37 | file_name | semmle.label | file_name | +| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection | +#select +| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen(__filename) | test.c:20:15:20:23 | file_name | user input (scanf) | +| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName) | test.c:45:15:45:23 | file_name | user input (scanf) | diff --git a/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected b/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected index f131146842..72ee574f06 100644 --- a/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected +++ b/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected @@ -1,5 +1,4 @@ -| test.c:10:20:10:26 | * ... | A FILE object is being copied. | -| test.c:17:21:17:30 | * ... | A FILE object is being copied. | -| test.c:23:21:23:31 | * ... | A FILE object is being copied. | -| test.c:29:15:29:21 | * ... | A FILE object is being copied. | -| test.c:42:19:42:28 | * ... | A FILE object is being copied. | \ No newline at end of file +| test.c:11:20:11:26 | * ... | A FILE object is being copied. | +| test.c:18:21:18:30 | * ... | A FILE object is being copied. | +| test.c:24:15:24:21 | * ... | A FILE object is being copied. | +| test.c:37:19:37:28 | * ... | A FILE object is being copied. | diff --git a/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected.clang b/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected.clang new file mode 100644 index 0000000000..4a4bc614b3 --- /dev/null +++ b/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected.clang @@ -0,0 +1,4 @@ +| test.c:4:20:4:26 | * ... | A FILE object is being copied. | +| test.c:11:21:11:30 | * ... | A FILE object is being copied. | +| test.c:17:15:17:21 | * ... | A FILE object is being copied. | +| test.c:30:19:30:28 | * ... | A FILE object is being copied. | diff --git a/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected.gcc b/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected.gcc new file mode 100644 index 0000000000..4a4bc614b3 --- /dev/null +++ b/c/cert/test/rules/FIO38-C/DoNotCopyAFileObject.expected.gcc @@ -0,0 +1,4 @@ +| test.c:4:20:4:26 | * ... | A FILE object is being copied. | +| test.c:11:21:11:30 | * ... | A FILE object is being copied. | +| test.c:17:15:17:21 | * ... | A FILE object is being copied. | +| test.c:30:19:30:28 | * ... | A FILE object is being copied. | diff --git a/c/cert/test/rules/FIO38-C/test.c b/c/cert/test/rules/FIO38-C/test.c index 0f0eb111ac..b20f6792d6 100644 --- a/c/cert/test/rules/FIO38-C/test.c +++ b/c/cert/test/rules/FIO38-C/test.c @@ -1,4 +1,5 @@ #include +// Workaround for the Musl implementing FILE as an incomplete type. #if !defined(__DEFINED_struct__IO_FILE) struct _IO_FILE { char __x; @@ -17,12 +18,6 @@ int f2(void) { FILE my_stdout2 = *my_stdout; // NON_COMPLIANT return fputs("Hello, World!\n", my_stdout); } -int f2b(void) { - FILE *const *my_stdout; - my_stdout = &stdout; // COMPLIANT - FILE my_stdout2 = **my_stdout; // NON_COMPLIANT - return fputs("Hello, World!\n", *my_stdout); -} int f3(void) { FILE my_stdout; diff --git a/c/cert/test/rules/FIO38-C/test.c.clang b/c/cert/test/rules/FIO38-C/test.c.clang new file mode 100644 index 0000000000..81eb9db7d9 --- /dev/null +++ b/c/cert/test/rules/FIO38-C/test.c.clang @@ -0,0 +1,37 @@ +#include + +int f1(void) { + FILE my_stdout = *stdout; // NON_COMPLIANT + return fputs("Hello, World!\n", &my_stdout); +} + +int f2(void) { + FILE *my_stdout; + my_stdout = stdout; // COMPLIANT + FILE my_stdout2 = *my_stdout; // NON_COMPLIANT + return fputs("Hello, World!\n", my_stdout); +} + +int f3(void) { + FILE my_stdout; + my_stdout = *stdout; // NON_COMPLIANT + return fputs("Hello, World!\n", &my_stdout); +} + +int f4(void) { + FILE *my_stdout; + my_stdout = fopen("file.txt", "w"); // COMPLIANT + return fputs("Hello, World!\n", my_stdout); +} + +int f5helper(FILE my_stdout) { return fputs("Hello, World!\n", &my_stdout); } +int f5(void) { + FILE *my_stdout = fopen("file.txt", "w"); // COMPLIANT + return f5helper(*my_stdout); // NON_COMPLIANT +} + +int f6helper(FILE *my_stdout) { return fputs("Hello, World!\n", my_stdout); } +int f6(void) { + FILE *my_stdout = fopen("file.txt", "w"); // COMPLIANT + return f6helper(my_stdout); // COMPLIANT +} diff --git a/c/cert/test/rules/FIO38-C/test.c.gcc b/c/cert/test/rules/FIO38-C/test.c.gcc new file mode 100644 index 0000000000..81eb9db7d9 --- /dev/null +++ b/c/cert/test/rules/FIO38-C/test.c.gcc @@ -0,0 +1,37 @@ +#include + +int f1(void) { + FILE my_stdout = *stdout; // NON_COMPLIANT + return fputs("Hello, World!\n", &my_stdout); +} + +int f2(void) { + FILE *my_stdout; + my_stdout = stdout; // COMPLIANT + FILE my_stdout2 = *my_stdout; // NON_COMPLIANT + return fputs("Hello, World!\n", my_stdout); +} + +int f3(void) { + FILE my_stdout; + my_stdout = *stdout; // NON_COMPLIANT + return fputs("Hello, World!\n", &my_stdout); +} + +int f4(void) { + FILE *my_stdout; + my_stdout = fopen("file.txt", "w"); // COMPLIANT + return fputs("Hello, World!\n", my_stdout); +} + +int f5helper(FILE my_stdout) { return fputs("Hello, World!\n", &my_stdout); } +int f5(void) { + FILE *my_stdout = fopen("file.txt", "w"); // COMPLIANT + return f5helper(*my_stdout); // NON_COMPLIANT +} + +int f6helper(FILE *my_stdout) { return fputs("Hello, World!\n", my_stdout); } +int f6(void) { + FILE *my_stdout = fopen("file.txt", "w"); // COMPLIANT + return f6helper(my_stdout); // COMPLIANT +} diff --git a/c/cert/test/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.expected b/c/cert/test/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.expected index 20c108cfa0..52cb85e5c4 100644 --- a/c/cert/test/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.expected +++ b/c/cert/test/rules/FIO40-C/ResetStringsOnFgetsOrFgetwsFailure.expected @@ -1,3 +1,6 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ResetStringsOnFgetsOrFgetwsFailure.ql:48,11-19) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ResetStringsOnFgetsOrFgetwsFailure.ql:48,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ResetStringsOnFgetsOrFgetwsFailure.ql:49,13-21) | test.c:20:10:20:12 | buf | The buffer is not reset before being referenced following a failed $@. | test.c:15:7:15:11 | call to fgets | call to fgets | | test.c:57:10:57:12 | buf | The buffer is not reset before being referenced following a failed $@. | test.c:52:7:52:11 | call to fgets | call to fgets | | test.c:66:18:66:20 | buf | The buffer is not reset before being referenced following a failed $@. | test.c:61:7:61:11 | call to fgets | call to fgets | diff --git a/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.qlref b/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.qlref deleted file mode 100644 index 53d28c862c..0000000000 --- a/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.testref b/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.testref new file mode 100644 index 0000000000..960e2354ae --- /dev/null +++ b/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.testref @@ -0,0 +1 @@ +c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO42-C/test.c b/c/cert/test/rules/FIO42-C/test.c deleted file mode 100644 index daa67792f7..0000000000 --- a/c/cert/test/rules/FIO42-C/test.c +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include - -int f1a(const char *filename) { - FILE *f = fopen(filename, "r"); // NON_COMPLIANT - if (NULL == f) { - return -1; - } - /* ... */ - return 0; -} - -int f1b(const char *filename) { - FILE *f = freopen(filename, "w+", stdout); // NON_COMPLIANT - if (NULL == f) { - return -1; - } - /* ... */ - return 0; -} - -void f1c(const char *filename) { - FILE *f = freopen(filename, "w+", stdout); // NON_COMPLIANT - if (NULL == f) { - return; - } - /* ... */ - // FILE* out of scope not closed -} - -int f2a(const char *filename) { - FILE *f = fopen(filename, "r"); // COMPLIANT - if (NULL == f) { - return -1; - } - /* ... */ - if (fclose(f) == EOF) { - return -1; - } - return 0; -} - -int f2b(const char *filename) { - FILE *f = fopen(filename, "r"); // NON_COMPLIANT - if (NULL == f) { - return -1; - } - if (filename) { - if (fclose(f) == EOF) { - return -1; - } - } - // file not closed on this path - return 0; -} - -// scope prolonged -int f2c(const char *filename) { - FILE *g; - { - FILE *f = fopen(filename, "r"); // COMPLIANT - if (NULL == f) { - return -1; - } - /* ... */ - g = f; - // f out of scope - } - if (fclose(g) == EOF) { - return -1; - } - return 0; -} - -// interprocedural -int closing_helper(FILE *g) { - if (fclose(g) == EOF) { - return -1; - } - return 0; -} -int f2inter(const char *filename) { - FILE *f = fopen(filename, "r"); // COMPLIANT (FALSE_POSITIVE) - if (NULL == f) { - return -1; - } - return closing_helper(f); -} - -int f2cfg(const char *filename) { - FILE *f = freopen(filename, "r", stdout); // NON_COMPLIANT - if (NULL == f) { - return -1; - } - if (filename) { - if (fclose(f) == EOF) { - return -1; - } - } - // file not closed on one path - return 0; -} - -int f3(const char *filename) { - FILE *f = fopen(filename, "w"); // NON_COMPLIANT - if (NULL == f) { - exit(EXIT_FAILURE); - } - /* ... */ - exit(EXIT_SUCCESS); -} - -int f4(const char *filename) { - FILE *f = fopen(filename, "w"); // COMPLIANT - if (NULL == f) { - /* Handle error */ - } - /* ... */ - if (fclose(f) == EOF) { - /* Handle error */ - } - exit(EXIT_SUCCESS); -} - -int f5(const char *filename) { - int fd = open(filename, O_RDONLY, S_IRUSR); // NON_COMPLIANT - if (-1 == fd) { - return -1; - } - /* ... */ - return 0; -} - -int f6(const char *filename) { - int fd = open(filename, O_RDONLY, S_IRUSR); // COMPLIANT - if (-1 == fd) { - return -1; - } - /* ... */ - if (-1 == close(fd)) { - return -1; - } - return 0; -} diff --git a/c/cert/test/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.expected b/c/cert/test/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.expected index 8074710738..ec05727161 100644 --- a/c/cert/test/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.expected +++ b/c/cert/test/rules/FIO44-C/OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.expected @@ -1,2 +1,7 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql:30,32-40) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql:31,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql:33,14-22) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql:36,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (OnlyUseValuesForFsetposThatAreReturnedFromFgetpos.ql:42,21-29) | test.c:7:24:7:30 | & ... | The position argument of a call to `fsetpos()` should be obtained from a call to `fgetpos()`. | | test.c:33:24:33:30 | & ... | The position argument of a call to `fsetpos()` should be obtained from a call to `fgetpos()`. | diff --git a/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected index 1b2923b780..a211aa4002 100644 --- a/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected +++ b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected @@ -1,2 +1,3 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ToctouRaceConditionsWhileAccessingFiles.ql:32,35-43) | test.c:4:13:4:17 | call to fopen | This call is trying to prevent an existing file from being overwritten by $@. An attacker might be able to exploit the race window between the two calls. | test.c:11:9:11:13 | call to fopen | another call | | test.c:88:13:88:17 | call to fopen | This call is trying to prevent an existing file from being overwritten by $@. An attacker might be able to exploit the race window between the two calls. | test.c:95:9:95:13 | call to fopen | another call | diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.clang b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.clang new file mode 100644 index 0000000000..f6a8f57da8 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.clang @@ -0,0 +1,90 @@ +| test.c:376:17:376:30 | v_intmax_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:378:17:378:28 | v_size_t_ptr | This argument should be of type 'int *' but is of type 'unsigned long *' | +| test.c:380:17:380:31 | v_ptrdiff_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:417:17:417:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:421:18:421:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:425:16:425:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:426:17:426:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:427:18:427:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:428:17:428:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:429:18:429:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:430:17:430:26 | v_char_ptr | This argument should be of type 'intmax_t' but is of type 'char *' | +| test.c:432:17:432:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:434:16:434:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:435:17:435:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:436:18:436:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:437:17:437:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:438:18:438:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:439:17:439:26 | v_char_ptr | This argument should be of type 'intmax_t' but is of type 'char *' | +| test.c:441:17:441:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:443:16:443:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:444:17:444:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:445:18:445:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:446:17:446:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:447:18:447:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:450:17:450:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:454:16:454:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:455:17:455:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:456:18:456:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:457:17:457:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:458:18:458:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:461:17:461:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:465:16:465:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:466:17:466:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:467:18:467:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:468:17:468:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:469:18:469:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:472:17:472:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:476:16:476:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:477:17:477:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:478:18:478:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:479:17:479:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:480:18:480:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:483:17:483:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:487:16:487:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:488:17:488:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:489:18:489:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:490:17:490:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:492:16:492:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:493:17:493:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:494:18:494:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:495:17:495:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:497:16:497:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:498:17:498:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:499:18:499:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:500:17:500:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:502:16:502:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:503:17:503:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:504:18:504:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:505:17:505:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:507:16:507:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:508:17:508:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:509:18:509:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:510:17:510:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:512:16:512:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:513:17:513:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:514:18:514:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:515:17:515:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:517:16:517:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:518:17:518:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:519:18:519:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:520:17:520:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:522:16:522:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:523:17:523:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:524:18:524:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:525:17:525:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:527:16:527:25 | v_char_ptr | This argument should be of type 'char' but is of type 'char *' | +| test.c:528:17:528:26 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:530:16:530:20 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:531:17:531:21 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | +| test.c:533:16:533:20 | v_int | This argument should be of type 'void *' but is of type 'int' | +| test.c:535:16:535:20 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:536:17:536:21 | v_int | This argument should be of type 'short *' but is of type 'int' | +| test.c:537:18:537:22 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:538:17:538:21 | v_int | This argument should be of type 'long *' but is of type 'int' | +| test.c:539:18:539:22 | v_int | This argument should be of type 'long long *' but is of type 'int' | +| test.c:540:17:540:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:541:17:541:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:542:17:542:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:544:16:544:25 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:546:16:546:20 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.gcc b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.gcc new file mode 100644 index 0000000000..f6a8f57da8 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.gcc @@ -0,0 +1,90 @@ +| test.c:376:17:376:30 | v_intmax_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:378:17:378:28 | v_size_t_ptr | This argument should be of type 'int *' but is of type 'unsigned long *' | +| test.c:380:17:380:31 | v_ptrdiff_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:417:17:417:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:421:18:421:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:425:16:425:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:426:17:426:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:427:18:427:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:428:17:428:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:429:18:429:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:430:17:430:26 | v_char_ptr | This argument should be of type 'intmax_t' but is of type 'char *' | +| test.c:432:17:432:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:434:16:434:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:435:17:435:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:436:18:436:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:437:17:437:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:438:18:438:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:439:17:439:26 | v_char_ptr | This argument should be of type 'intmax_t' but is of type 'char *' | +| test.c:441:17:441:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:443:16:443:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:444:17:444:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:445:18:445:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:446:17:446:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:447:18:447:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:450:17:450:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:454:16:454:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:455:17:455:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:456:18:456:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:457:17:457:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:458:18:458:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:461:17:461:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:465:16:465:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:466:17:466:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:467:18:467:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:468:17:468:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:469:18:469:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:472:17:472:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:476:16:476:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:477:17:477:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:478:18:478:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:479:17:479:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:480:18:480:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:483:17:483:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:487:16:487:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:488:17:488:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:489:18:489:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:490:17:490:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:492:16:492:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:493:17:493:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:494:18:494:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:495:17:495:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:497:16:497:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:498:17:498:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:499:18:499:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:500:17:500:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:502:16:502:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:503:17:503:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:504:18:504:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:505:17:505:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:507:16:507:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:508:17:508:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:509:18:509:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:510:17:510:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:512:16:512:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:513:17:513:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:514:18:514:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:515:17:515:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:517:16:517:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:518:17:518:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:519:18:519:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:520:17:520:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:522:16:522:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:523:17:523:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:524:18:524:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:525:17:525:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:527:16:527:25 | v_char_ptr | This argument should be of type 'char' but is of type 'char *' | +| test.c:528:17:528:26 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:530:16:530:20 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:531:17:531:21 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | +| test.c:533:16:533:20 | v_int | This argument should be of type 'void *' but is of type 'int' | +| test.c:535:16:535:20 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:536:17:536:21 | v_int | This argument should be of type 'short *' but is of type 'int' | +| test.c:537:18:537:22 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:538:17:538:21 | v_int | This argument should be of type 'long *' but is of type 'int' | +| test.c:539:18:539:22 | v_int | This argument should be of type 'long long *' but is of type 'int' | +| test.c:540:17:540:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:541:17:541:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:542:17:542:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:544:16:544:25 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:546:16:546:20 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.qcc b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.qcc new file mode 100644 index 0000000000..f6a8f57da8 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected.qcc @@ -0,0 +1,90 @@ +| test.c:376:17:376:30 | v_intmax_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:378:17:378:28 | v_size_t_ptr | This argument should be of type 'int *' but is of type 'unsigned long *' | +| test.c:380:17:380:31 | v_ptrdiff_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:417:17:417:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:421:18:421:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:425:16:425:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:426:17:426:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:427:18:427:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:428:17:428:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:429:18:429:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:430:17:430:26 | v_char_ptr | This argument should be of type 'intmax_t' but is of type 'char *' | +| test.c:432:17:432:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:434:16:434:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:435:17:435:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:436:18:436:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:437:17:437:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:438:18:438:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:439:17:439:26 | v_char_ptr | This argument should be of type 'intmax_t' but is of type 'char *' | +| test.c:441:17:441:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:443:16:443:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:444:17:444:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:445:18:445:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:446:17:446:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:447:18:447:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:450:17:450:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:454:16:454:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:455:17:455:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:456:18:456:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:457:17:457:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:458:18:458:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:461:17:461:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:465:16:465:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:466:17:466:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:467:18:467:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:468:17:468:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:469:18:469:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:472:17:472:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:476:16:476:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:477:17:477:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:478:18:478:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:479:17:479:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:480:18:480:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:483:17:483:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:487:16:487:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:488:17:488:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:489:18:489:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:490:17:490:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:492:16:492:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:493:17:493:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:494:18:494:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:495:17:495:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:497:16:497:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:498:17:498:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:499:18:499:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:500:17:500:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:502:16:502:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:503:17:503:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:504:18:504:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:505:17:505:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:507:16:507:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:508:17:508:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:509:18:509:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:510:17:510:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:512:16:512:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:513:17:513:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:514:18:514:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:515:17:515:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:517:16:517:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:518:17:518:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:519:18:519:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:520:17:520:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:522:16:522:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:523:17:523:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:524:18:524:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:525:17:525:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:527:16:527:25 | v_char_ptr | This argument should be of type 'char' but is of type 'char *' | +| test.c:528:17:528:26 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:530:16:530:20 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:531:17:531:21 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | +| test.c:533:16:533:20 | v_int | This argument should be of type 'void *' but is of type 'int' | +| test.c:535:16:535:20 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:536:17:536:21 | v_int | This argument should be of type 'short *' but is of type 'int' | +| test.c:537:18:537:22 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:538:17:538:21 | v_int | This argument should be of type 'long *' but is of type 'int' | +| test.c:539:18:539:22 | v_int | This argument should be of type 'long long *' but is of type 'int' | +| test.c:540:17:540:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:541:17:541:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:542:17:542:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:544:16:544:25 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:546:16:546:20 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | diff --git a/c/cert/test/rules/FIO47-C/test.c b/c/cert/test/rules/FIO47-C/test.c index 2ae9e02b2f..407191528b 100644 --- a/c/cert/test/rules/FIO47-C/test.c +++ b/c/cert/test/rules/FIO47-C/test.c @@ -17,8 +17,8 @@ unsigned char v_unsigned_char = 42; unsigned long v_unsigned_long = 42; unsigned long long v_unsigned_long_long = 42; uintmax_t v_uintmax_t = 42; -double v_double = 42; -long double v_long_double = 42; +double v_double = 42.0; +long double v_long_double = 42.0; int v_int = 42; wint_t v_wint_t = 42; char *v_char_ptr = "42"; @@ -427,8 +427,8 @@ void test_wrong_arg_type() { printf("%hhd", v_char_ptr); // NON_COMPLIANT printf("%ld", v_char_ptr); // NON_COMPLIANT printf("%lld", v_char_ptr); // NON_COMPLIANT - printf("%jd", v_char_ptr); // NON_COMPLIANT - printf("%zd", v_char_ptr); // NON_COMPLIANT + printf("%jd", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%zd", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%td", v_char_ptr); // NON_COMPLIANT printf("%i", v_char_ptr); // NON_COMPLIANT @@ -436,8 +436,8 @@ void test_wrong_arg_type() { printf("%hhi", v_char_ptr); // NON_COMPLIANT printf("%li", v_char_ptr); // NON_COMPLIANT printf("%lli", v_char_ptr); // NON_COMPLIANT - printf("%ji", v_char_ptr); // NON_COMPLIANT - printf("%zi", v_char_ptr); // NON_COMPLIANT + printf("%ji", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%zi", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%ti", v_char_ptr); // NON_COMPLIANT printf("%o", v_char_ptr); // NON_COMPLIANT diff --git a/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.expected b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.expected new file mode 100644 index 0000000000..43f8a04a66 --- /dev/null +++ b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.expected @@ -0,0 +1,3 @@ +| test.c:3:3:4:3 | for(...;...;...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter | +| test.c:5:3:7:3 | while (...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter | +| test.c:9:3:11:22 | do (...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter | diff --git a/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.qlref b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.qlref new file mode 100644 index 0000000000..1ada999730 --- /dev/null +++ b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.qlref @@ -0,0 +1 @@ +rules/FLP30-C/FloatingPointLoopCounters.ql \ No newline at end of file diff --git a/c/cert/test/rules/FLP30-C/test.c b/c/cert/test/rules/FLP30-C/test.c new file mode 100644 index 0000000000..e63dc5d1ed --- /dev/null +++ b/c/cert/test/rules/FLP30-C/test.c @@ -0,0 +1,26 @@ +void f1() { + float f = 0.0F; + for (f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT + } + while (f < 10.0F) { // NON_COMPLIANT + f = f * 2.0F; + } + + do { // NON_COMPLIANT + f *= 2.0F; + } while (f < 10.0F); +} + +void f2() { + + for (int i = 0; i < 10; i++) { // COMPLIANT + } + int j = 0; + while (j < 10) { // COMPLIANT + j = j * 2; + } + + do { + j++; + } while (j < 10); // COMPLIANT +} diff --git a/c/cert/test/rules/FLP32-C/UncheckedRangeDomainPoleErrors.testref b/c/cert/test/rules/FLP32-C/UncheckedRangeDomainPoleErrors.testref new file mode 100644 index 0000000000..50cf3fcb51 --- /dev/null +++ b/c/cert/test/rules/FLP32-C/UncheckedRangeDomainPoleErrors.testref @@ -0,0 +1 @@ +c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql \ No newline at end of file diff --git a/c/cert/test/rules/FLP34-C/UncheckedFloatingPointConversion.expected b/c/cert/test/rules/FLP34-C/UncheckedFloatingPointConversion.expected new file mode 100644 index 0000000000..78ef781335 --- /dev/null +++ b/c/cert/test/rules/FLP34-C/UncheckedFloatingPointConversion.expected @@ -0,0 +1 @@ +| test.c:8:11:8:11 | (int)... | Conversion of float to integer without appropriate guards avoiding undefined behavior. | diff --git a/c/cert/test/rules/FLP34-C/UncheckedFloatingPointConversion.qlref b/c/cert/test/rules/FLP34-C/UncheckedFloatingPointConversion.qlref new file mode 100644 index 0000000000..f539ee6e39 --- /dev/null +++ b/c/cert/test/rules/FLP34-C/UncheckedFloatingPointConversion.qlref @@ -0,0 +1 @@ +rules/FLP34-C/UncheckedFloatingPointConversion.ql \ No newline at end of file diff --git a/c/cert/test/rules/FLP34-C/test.c b/c/cert/test/rules/FLP34-C/test.c new file mode 100644 index 0000000000..0b60a40029 --- /dev/null +++ b/c/cert/test/rules/FLP34-C/test.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +void test_no_guard(float f) { + int i = f; // NON_COMPLIANT +} + +void test_fixed_narrow_range(float f) { + if (f > 0.0f && f < 100.0f) { + int i = f; // COMPLIANT + } +} + +/* 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) + +void test_precision_check(float f) { + if (isnan(f) || PRECISION(INT_MAX) < log2(fabs(f)) || + (f != 0.0F && fabs(f) < FLT_MIN)) { + /* Handle error */ + } else { + int i = f; // COMPLIANT + } +} + +void test_precision_check_double(double f) { + if (isnan(f) || PRECISION(INT_MAX) < log2f(fabsf(f)) || + (f != 0.0F && fabsf(f) < FLT_MIN)) { + /* Handle error */ + } else { + int i = f; // COMPLIANT + } +} + +void test_precision_check_long_double(long double f) { + if (isnan(f) || PRECISION(INT_MAX) < log2l(fabsl(f)) || + (f != 0.0F && fabsl(f) < FLT_MIN)) { + /* Handle error */ + } else { + int i = f; // COMPLIANT + } +} \ No newline at end of file diff --git a/c/cert/test/rules/FLP36-C/IntToFloatPreservePrecision.expected b/c/cert/test/rules/FLP36-C/IntToFloatPreservePrecision.expected new file mode 100644 index 0000000000..2239a44468 --- /dev/null +++ b/c/cert/test/rules/FLP36-C/IntToFloatPreservePrecision.expected @@ -0,0 +1,6 @@ +| test.c:5:3:5:11 | (float)... | The upper bound of this value (1234567890) cast from uint64_t to float is greater than the maximum value (16777216) that can be stored precisely. | +| test.c:13:3:13:11 | (float)... | The upper bound of this value (16777217) cast from uint64_t to float is greater than the maximum value (16777216) that can be stored precisely. | +| test.c:17:3:17:11 | (float)... | The upper bound of this value (9007199254740992) cast from uint64_t to float is greater than the maximum value (16777216) that can be stored precisely. | +| test.c:21:3:21:11 | (float)... | The upper bound of this value (9007199254740992) cast from uint64_t to float is greater than the maximum value (16777216) that can be stored precisely. | +| test.c:28:3:28:11 | (float)... | The upper bound of this value (9007199254740996) cast from uint64_t to float is greater than the maximum value (16777216) that can be stored precisely. | +| test.c:29:3:29:12 | (double)... | The upper bound of this value (9007199254740996) cast from uint64_t to double is greater than the maximum value (9007199254740992) that can be stored precisely. | diff --git a/c/cert/test/rules/FLP36-C/IntToFloatPreservePrecision.qlref b/c/cert/test/rules/FLP36-C/IntToFloatPreservePrecision.qlref new file mode 100644 index 0000000000..c08b3dfd72 --- /dev/null +++ b/c/cert/test/rules/FLP36-C/IntToFloatPreservePrecision.qlref @@ -0,0 +1 @@ +rules/FLP36-C/IntToFloatPreservePrecision.ql \ No newline at end of file diff --git a/c/cert/test/rules/FLP36-C/test.c b/c/cert/test/rules/FLP36-C/test.c new file mode 100644 index 0000000000..69e3af189a --- /dev/null +++ b/c/cert/test/rules/FLP36-C/test.c @@ -0,0 +1,30 @@ +#include + +void test_conversion_int_to_float() { + uint64_t i1 = 1234567890L; + (float)i1; // NON_COMPLIANT - precision (23 bits) isn't sufficient + (double)i1; // COMPLIANT - precision (52 bits) is sufficient + + uint32_t i2 = 16777216; // 2^24 + (float)i2; // COMPLIANT - precision (23 bits) is sufficient + (double)i2; // COMPLIANT - precision (52 bits) is sufficient + + uint64_t i3 = 16777217; // 2^24 + 1 + (float)i3; // NON_COMPLIANT - precision (23 bits) is not sufficient + (double)i3; // COMPLIANT - precision (52 bits) is sufficient + + uint64_t i4 = 9007199254740992L; // 2^54 + (float)i4; // NON_COMPLIANT - precision (23 bits) is not sufficient + (double)i4; // COMPLIANT - precision (52 bits) is sufficient + + uint64_t i5 = 9007199254740993L; // 2^54 + 1 + (float)i5; // NON_COMPLIANT - precision (23 bits) is not sufficient + (double)i5; // NON_COMPLIANT[FALSE_POSITIVE] - precision (52 bits) is not + // sufficient, but our analysis also works with doubles, so cannot + // precisely represent this value either, and chooses to round + // down, thus making this case impractical to detect. + + uint64_t i6 = 9007199254740995L; // 2^54 + 3 + (float)i6; // NON_COMPLIANT - precision (23 bits) is not sufficient + (double)i6; // NON_COMPLIANT - precision (52 bits) is not sufficient +} \ No newline at end of file diff --git a/c/cert/test/rules/FLP37-C/MemcmpUsedToCompareFloats.expected b/c/cert/test/rules/FLP37-C/MemcmpUsedToCompareFloats.expected new file mode 100644 index 0000000000..a57f64f94b --- /dev/null +++ b/c/cert/test/rules/FLP37-C/MemcmpUsedToCompareFloats.expected @@ -0,0 +1,10 @@ +| test.c:27:3:27:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type float. | test.c:27:10:27:12 | & ... | first buffer | +| test.c:27:3:27:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type float. | test.c:27:15:27:17 | & ... | second buffer | +| test.c:35:3:35:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S2. | test.c:35:10:35:13 | & ... | first buffer | +| test.c:35:3:35:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S2. | test.c:35:16:35:19 | & ... | second buffer | +| test.c:39:3:39:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S3. | test.c:39:10:39:13 | & ... | first buffer | +| test.c:39:3:39:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S3. | test.c:39:16:39:19 | & ... | second buffer | +| test.c:43:3:43:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S4. | test.c:43:10:43:13 | & ... | first buffer | +| test.c:43:3:43:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S4. | test.c:43:16:43:19 | & ... | second buffer | +| test.c:47:3:47:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S5. | test.c:47:10:47:13 | & ... | first buffer | +| test.c:47:3:47:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S5. | test.c:47:16:47:19 | & ... | second buffer | diff --git a/c/cert/test/rules/FLP37-C/MemcmpUsedToCompareFloats.qlref b/c/cert/test/rules/FLP37-C/MemcmpUsedToCompareFloats.qlref new file mode 100644 index 0000000000..37be07461c --- /dev/null +++ b/c/cert/test/rules/FLP37-C/MemcmpUsedToCompareFloats.qlref @@ -0,0 +1 @@ +rules/FLP37-C/MemcmpUsedToCompareFloats.ql \ No newline at end of file diff --git a/c/cert/test/rules/FLP37-C/test.c b/c/cert/test/rules/FLP37-C/test.c new file mode 100644 index 0000000000..fdb32fe07c --- /dev/null +++ b/c/cert/test/rules/FLP37-C/test.c @@ -0,0 +1,48 @@ +#include + +struct S1 { + int i; +}; + +struct S2 { + float f; +}; + +struct S3 { + struct S2 s2; +}; + +struct S4 { + struct S3 s3; +}; + +struct S5 { + union { + float f1; + int i1; + }; +}; + +void test_float_memcmp(float f1, float f2) { + memcmp(&f1, &f2, sizeof(float)); // NON_COMPLIANT +} + +void test_struct_int_memcmp(struct S1 s1a, struct S1 s1b) { + memcmp(&s1a, &s1b, sizeof(struct S1)); // COMPLIANT +} + +void test_struct_float_memcmp(struct S2 s2a, struct S2 s2b) { + memcmp(&s2a, &s2b, sizeof(struct S2)); // NON_COMPLIANT +} + +void test_struct_nested_float_memcmp(struct S3 s3a, struct S3 s3b) { + memcmp(&s3a, &s3b, sizeof(struct S3)); // NON_COMPLIANT +} + +void test_struct_nested_nested_float_memcmp(struct S4 s4a, struct S4 s4b) { + memcmp(&s4a, &s4b, sizeof(struct S4)); // NON_COMPLIANT +} + +void test_union_nested_float_memcmp(struct S5 s5a, struct S5 s5b) { + memcmp(&s5a, &s5b, sizeof(struct S5)); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/INT30-C/UnsignedIntegerOperationsWrapAround.testref b/c/cert/test/rules/INT30-C/UnsignedIntegerOperationsWrapAround.testref new file mode 100644 index 0000000000..c9bc9d9637 --- /dev/null +++ b/c/cert/test/rules/INT30-C/UnsignedIntegerOperationsWrapAround.testref @@ -0,0 +1 @@ +c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT31-C/IntegerConversionCausesDataLoss.expected b/c/cert/test/rules/INT31-C/IntegerConversionCausesDataLoss.expected new file mode 100644 index 0000000000..ee18410a48 --- /dev/null +++ b/c/cert/test/rules/INT31-C/IntegerConversionCausesDataLoss.expected @@ -0,0 +1,11 @@ +| test.c:7:3:7:15 | (signed int)... | Conversion from unsigned int to signed int may cause data loss (casting from range 0...4294967295 to range -2147483648...2147483647). | +| test.c:17:3:17:17 | (unsigned int)... | Conversion from signed int to unsigned int may cause data loss (casting from range -2147483648...2147483647 to range 0...4294967295). | +| test.c:34:3:34:17 | (signed short)... | Conversion from signed int to signed short may cause data loss (casting from range -2147483648...2147483647 to range -32768...32767). | +| test.c:51:3:51:19 | (unsigned short)... | Conversion from unsigned int to unsigned short may cause data loss (casting from range 0...4294967295 to range 0...65535). | +| test.c:89:3:89:19 | (unsigned char)... | Conversion from signed int to unsigned char may cause data loss (casting from range 100000...100000 to range 0...255). | +| test.c:92:3:92:19 | (unsigned char)... | Conversion from signed int to unsigned char may cause data loss (casting from range -129...-129 to range 0...255). | +| test.c:93:3:93:19 | (unsigned char)... | Conversion from signed int to unsigned char may cause data loss (casting from range 256...256 to range 0...255). | +| test.c:97:9:97:12 | 4096 | Conversion from int to unsigned char may cause data loss (casting from range 4096...4096 to range 0...255). | +| test.c:99:10:99:13 | 4096 | Conversion from int to unsigned char may cause data loss (casting from range 4096...4096 to range 0...255). | +| test.c:101:13:101:16 | 4096 | Conversion from int to unsigned char may cause data loss (casting from range 4096...4096 to range 0...255). | +| test.c:103:13:103:16 | 4096 | Conversion from int to unsigned char may cause data loss (casting from range 4096...4096 to range 0...255). | diff --git a/c/cert/test/rules/INT31-C/IntegerConversionCausesDataLoss.qlref b/c/cert/test/rules/INT31-C/IntegerConversionCausesDataLoss.qlref new file mode 100644 index 0000000000..277a450807 --- /dev/null +++ b/c/cert/test/rules/INT31-C/IntegerConversionCausesDataLoss.qlref @@ -0,0 +1 @@ +rules/INT31-C/IntegerConversionCausesDataLoss.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT31-C/test.c b/c/cert/test/rules/INT31-C/test.c new file mode 100644 index 0000000000..08b09cf6b8 --- /dev/null +++ b/c/cert/test/rules/INT31-C/test.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +void test_unsigned_to_signed(unsigned int x) { + (signed int)x; // NON_COMPLIANT - not larger enough to represent all +} + +void test_unsigned_to_signed_check(unsigned int x) { + if (x <= INT_MAX) { + (signed int)x; // COMPLIANT + } +} + +void test_signed_to_unsigned(signed int x) { + (unsigned int)x; // NON_COMPLIANT - not large enough to represent all +} + +void test_signed_to_unsigned_check(signed int x) { + if (x >= 0) { + (unsigned int)x; // COMPLIANT + } +} + +void test_signed_to_unsigned_check2(signed int x) { + if (x < 0) { + } else { + (unsigned int)x; // COMPLIANT + } +} + +void test_signed_loss_of_precision(signed int x) { + (signed short)x; // NON_COMPLIANT - not large enough to represent all +} + +void test_signed_loss_of_precision_check(signed int x) { + if (x >= SHRT_MIN && x <= SHRT_MAX) { + (signed short)x; // COMPLIANT + } +} + +void test_signed_loss_of_precision_check2(signed int x) { + if (x < SHRT_MIN || x > SHRT_MAX) { + } else { + (signed short)x; // COMPLIANT + } +} + +void test_unsigned_loss_of_precision(unsigned int x) { + (unsigned short)x; // NON_COMPLIANT - not large enough to represent all +} + +void test_unsigned_loss_of_precision_check(unsigned int x) { + if (x <= USHRT_MAX) { + (unsigned short)x; // COMPLIANT + } +} + +void test_unsigned_loss_of_precision_check2(unsigned int x) { + if (x > USHRT_MAX) { + } else { + (unsigned short)x; // COMPLIANT + } +} + +// We create a fake stub here to test the case +// that time_t is an unsigned type. +typedef unsigned int time_t; +time_t time(time_t *seconds); + +void test_time_t_check_against_zero(time_t x) { + time_t now = time(0); + if (now != -1) { // NON_COMPLIANT[FALSE_NEGATIVE] - there is no conversion + // here in our model + } + if (now != (time_t)-1) { // COMPLIANT + } +} + +void test_chars() { + signed int i1 = 'A'; + signed int i2 = 100000; + signed int i3 = -128; + signed int i4 = 255; + signed int i5 = -129; + signed int i6 = 256; + (unsigned char)i1; // COMPLIANT + (unsigned char)i2; // NON_COMPLIANT + (unsigned char)i3; // COMPLIANT + (unsigned char)i4; // COMPLIANT + (unsigned char)i5; // NON_COMPLIANT + (unsigned char)i6; // NON_COMPLIANT +} + +void test_funcs(int *a, size_t n) { + fputc(4096, stdout); // NON_COMPLIANT + fputc('A', stdout); // COMPLIANT + ungetc(4096, stdin); // NON_COMPLIANT + ungetc('A', stdin); // COMPLIANT + memchr(a, 4096, n); // NON_COMPLIANT + memchr(a, 'A', n); // COMPLIANT + memset(a, 4096, n); // NON_COMPLIANT + memset(a, 0, n); // COMPLIANT + // not supported in our stdlib, or in any of the compilers + // memset_s(a, rn, 4096, n); // NON_COMPLIANT + // memset_s(a, rn, 0, n); // COMPLIANT +} + +void test_bool(signed int s) { + (bool)s; // COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/INT32-C/SignedIntegerOverflow.expected b/c/cert/test/rules/INT32-C/SignedIntegerOverflow.expected new file mode 100644 index 0000000000..0e107bcafa --- /dev/null +++ b/c/cert/test/rules/INT32-C/SignedIntegerOverflow.expected @@ -0,0 +1,24 @@ +| test.c:6:3:6:9 | ... + ... | Operation + of type int may overflow or underflow. | +| test.c:7:3:7:10 | ... += ... | Operation += of type signed int may overflow or underflow. | +| test.c:22:7:22:13 | ... + ... | Operation + of type int may overflow or underflow. | +| test.c:25:5:25:11 | ... + ... | Operation + of type int may overflow or underflow. | +| test.c:26:5:26:12 | ... += ... | Operation += of type signed int may overflow or underflow. | +| test.c:31:19:31:25 | ... + ... | Operation + of type int may overflow or underflow. | +| test.c:36:3:36:10 | ... += ... | Operation += of type signed int may overflow or underflow. | +| test.c:43:3:43:9 | ... - ... | Operation - of type int may overflow or underflow. | +| test.c:44:3:44:10 | ... -= ... | Operation -= of type signed int may overflow or underflow. | +| test.c:58:19:58:25 | ... - ... | Operation - of type int may overflow or underflow. | +| test.c:62:3:62:10 | ... -= ... | Operation -= of type signed int may overflow or underflow. | +| test.c:69:3:69:8 | ... * ... | Operation * of type int may overflow or underflow. | +| test.c:70:3:70:10 | ... *= ... | Operation *= of type signed int may overflow or underflow. | +| test.c:115:3:115:9 | ... / ... | Operation / of type int may overflow or underflow. | +| test.c:116:3:116:10 | ... /= ... | Operation /= of type signed int may overflow or underflow. | +| test.c:123:5:123:11 | ... / ... | Operation / of type int may overflow or underflow. | +| test.c:124:5:124:12 | ... /= ... | Operation /= of type signed int may overflow or underflow. | +| test.c:138:3:138:9 | ... % ... | Operation % of type int may overflow or underflow. | +| test.c:139:3:139:10 | ... %= ... | Operation %= of type signed int may overflow or underflow. | +| test.c:146:5:146:11 | ... % ... | Operation % of type int may overflow or underflow. | +| test.c:147:5:147:12 | ... %= ... | Operation %= of type signed int may overflow or underflow. | +| test.c:161:3:161:5 | - ... | Operation - of type signed int may overflow or underflow. | +| test.c:173:3:173:6 | ... ++ | Operation ++ of type signed int may overflow or underflow. | +| test.c:189:3:189:6 | ... -- | Operation -- of type signed int may overflow or underflow. | diff --git a/c/cert/test/rules/INT32-C/SignedIntegerOverflow.qlref b/c/cert/test/rules/INT32-C/SignedIntegerOverflow.qlref new file mode 100644 index 0000000000..dcb26795eb --- /dev/null +++ b/c/cert/test/rules/INT32-C/SignedIntegerOverflow.qlref @@ -0,0 +1 @@ +rules/INT32-C/SignedIntegerOverflow.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT32-C/test.c b/c/cert/test/rules/INT32-C/test.c new file mode 100644 index 0000000000..cde579123b --- /dev/null +++ b/c/cert/test/rules/INT32-C/test.c @@ -0,0 +1,202 @@ +#include +#include +#include + +void test_add_simple(signed int i1, signed int i2) { + i1 + i2; // NON_COMPLIANT - not bounds checked + i1 += i2; // NON_COMPLIANT - not bounds checked +} + +void test_add_precheck(signed int i1, signed int i2) { + // Style recommended by CERT + if (((i2 > 0) && (i1 > (INT_MAX - i2))) || + ((i2 < 0) && (i1 < (INT_MIN - i2)))) { + // handle error + } else { + i1 + i2; // COMPLIANT - bounds appropriately checked + i1 += i2; // COMPLIANT - bounds appropriately checked + } +} + +void test_add_precheck_2(signed int i1, signed int i2) { + if (i1 + i2 < i1) { // NON_COMPLIANT - bad overflow check - undefined behavior + // handle error + } else { + i1 + i2; // NON_COMPLIANT + i1 += i2; // NON_COMPLIANT + } +} + +void test_add_postcheck(signed int i1, signed int i2) { + signed int i3 = i1 + i2; // NON_COMPLIANT - signed overflow is undefined + // behavior, so checking afterwards is not sufficient + if (i3 < i1) { + // handle error + } + i1 += i2; // NON_COMPLIANT + if (i1 < i2) { + // handle error + } +} + +void test_sub_simple(signed int i1, signed int i2) { + i1 - i2; // NON_COMPLIANT - not bounds checked + i1 -= i2; // NON_COMPLIANT - not bounds checked +} + +void test_sub_precheck(signed int i1, signed int i2) { + // Style recomended by CERT + if ((i2 > 0 && i1 < INT_MIN + i2) || (i2 < 0 && i1 > INT_MAX + i2)) { + // handle error + } else { + i1 - i2; // COMPLIANT - bounds checked + i1 -= i2; // COMPLIANT - bounds checked + } +} + +void test_sub_postcheck(signed int i1, signed int i2) { + signed int i3 = i1 - i2; // NON_COMPLIANT - underflow is undefined behavior. + if (i3 > i1) { + // handle error + } + i1 -= i2; // NON_COMPLIANT - underflow is undefined behavior. + if (i1 > i2) { + // handle error + } +} + +void test_mul_simple(signed int i1, signed int i2) { + i1 *i2; // NON_COMPLIANT + i1 *= i2; // NON_COMPLIANT +} + +void test_mul_precheck(signed int i1, signed int i2) { + signed long long tmp = + (signed long long)i1 * (signed long long)i2; // COMPLIANT + signed int result; + + if (tmp > INT_MAX || tmp < INT_MIN) { + // handle error + } else { + result = (signed int)tmp; + } +} + +void test_mul_precheck_2(signed int i1, signed int i2) { + if (i1 > 0) { + if (i2 > 0) { + if (i1 > (INT_MAX / i2)) { + return; // handle error + } + } else { + if (i2 < (INT_MIN / i1)) { + // handle error + return; // handle error + } + } + } else { + if (i2 > 0) { + if (i1 < (INT_MIN / i2)) { + // handle error + return; // handle error + } + } else { + if ((i1 != 0) && (i2 < (INT_MAX / i1))) { + // handle error + return; // handle error + } + } + } + i1 *i2; // COMPLIANT + i1 *= i2; // COMPLIANT +} + +void test_simple_div(signed int i1, signed int i2) { + i1 / i2; // NON_COMPLIANT + i1 /= i2; // NON_COMPLIANT +} + +void test_simple_div_no_zero(signed int i1, signed int i2) { + if (i2 == 0) { + // handle error + } else { + i1 / i2; // NON_COMPLIANT + i1 /= i2; // NON_COMPLIANT + } +} + +void test_div_precheck(signed int i1, signed int i2) { + if ((i2 == 0) || ((i1 == INT_MIN) && (i2 == -1))) { + /* Handle error */ + } else { + i1 / i2; // COMPLIANT + i1 /= i2; // COMPLIANT + } +} + +void test_simple_rem(signed int i1, signed int i2) { + i1 % i2; // NON_COMPLIANT + i1 %= i2; // NON_COMPLIANT +} + +void test_simple_rem_no_zero(signed int i1, signed int i2) { + if (i2 == 0) { + // handle error + } else { + i1 % i2; // NON_COMPLIANT + i1 %= i2; // NON_COMPLIANT + } +} + +void test_rem_precheck(signed int i1, signed int i2) { + if ((i2 == 0) || ((i1 == INT_MIN) && (i2 == -1))) { + /* Handle error */ + } else { + i1 % i2; // COMPLIANT + i1 %= i2; // COMPLIANT + } +} + +void test_simple_negate(signed int i1) { + -i1; // NON_COMPLIANT +} + +void test_negate_precheck(signed int i1) { + if (i1 == INT_MIN) { + // handle error + } else { + -i1; // COMPLIANT + } +} + +void test_inc(signed int i1) { + i1++; // NON_COMPLIANT +} + +void test_inc_guard(signed int i1) { + if (i1 < INT_MAX) { + i1++; // COMPLIANT + } +} + +void test_inc_loop_guard() { + for (signed int i1 = 0; i1 < 10; i1++) { // COMPLIANT + // ... + } +} + +void test_dec(signed int i1) { + i1--; // NON_COMPLIANT +} + +void test_dec_guard(signed int i1) { + if (i1 > INT_MIN) { + i1--; // COMPLIANT + } +} + +void test_dec_loop_guard() { + for (signed int i1 = 10; i1 > 0; i1--) { // COMPLIANT + // ... + } +} \ No newline at end of file diff --git a/c/cert/test/rules/INT33-C/DivOrRemByZero.expected b/c/cert/test/rules/INT33-C/DivOrRemByZero.expected new file mode 100644 index 0000000000..66911a2ad6 --- /dev/null +++ b/c/cert/test/rules/INT33-C/DivOrRemByZero.expected @@ -0,0 +1,4 @@ +| test.c:4:3:4:9 | ... / ... | Division or remainder expression with divisor that may be zero (divisor range -2147483648...2147483647). | +| test.c:5:3:5:9 | ... % ... | Division or remainder expression with divisor that may be zero (divisor range -2147483648...2147483647). | +| test.c:12:5:12:11 | ... / ... | Division or remainder expression with divisor that may be zero (divisor range -2147483648...2147483647). | +| test.c:13:5:13:11 | ... % ... | Division or remainder expression with divisor that may be zero (divisor range -2147483648...2147483647). | diff --git a/c/cert/test/rules/INT33-C/DivOrRemByZero.qlref b/c/cert/test/rules/INT33-C/DivOrRemByZero.qlref new file mode 100644 index 0000000000..c3144339c8 --- /dev/null +++ b/c/cert/test/rules/INT33-C/DivOrRemByZero.qlref @@ -0,0 +1 @@ +rules/INT33-C/DivOrRemByZero.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT33-C/test.c b/c/cert/test/rules/INT33-C/test.c new file mode 100644 index 0000000000..2dd76580f0 --- /dev/null +++ b/c/cert/test/rules/INT33-C/test.c @@ -0,0 +1,33 @@ +#include + +void test_simple(signed int i1, signed int i2) { + i1 / i2; // NON_COMPLIANT + i1 % i2; // NON_COMPLIANT +} + +void test_incomplete_check(signed int i1, signed int i2) { + if (i1 == INT_MIN && i2 == -1) { + // handle error + } else { + i1 / i2; // NON_COMPLIANT + i1 % i2; // NON_COMPLIANT + } +} + +void test_complete_check(signed int i1, signed int i2) { + if (i2 == 0 || (i1 == INT_MIN && i2 == -1)) { + // handle error + } else { + i1 / i2; // COMPLIANT + i1 % i2; // COMPLIANT + } +} + +void test_unsigned(unsigned int i1, unsigned int i2) { + if (i2 == 0) { + // handle error + } else { + i1 / i2; // COMPLIANT + i1 % i2; // COMPLIANT + } +} \ No newline at end of file diff --git a/c/cert/test/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.expected b/c/cert/test/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.expected new file mode 100644 index 0000000000..dc92d0f1be --- /dev/null +++ b/c/cert/test/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.expected @@ -0,0 +1,159 @@ +| test.c:43:3:43:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs0' whose upper bound (8) is greater than or equal to the precision. | +| test.c:47:3:47:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:49:3:49:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs4' whose upper bound (15) is greater than or equal to the precision. | +| test.c:51:3:51:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs5' whose upper bound (15) is greater than or equal to the precision. | +| test.c:53:3:53:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:55:3:55:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:57:3:57:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:59:3:59:14 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:61:3:61:15 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:63:3:63:15 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:65:3:65:15 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:67:3:67:15 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:69:3:69:15 | ... << ... | The operand 'lhs0' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:71:3:71:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs0' whose upper bound (8) is greater than or equal to the precision. | +| test.c:73:3:73:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs1' whose upper bound (7) is greater than or equal to the precision. | +| test.c:75:3:75:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs2' whose upper bound (7) is greater than or equal to the precision. | +| test.c:77:3:77:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:79:3:79:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs4' whose upper bound (15) is greater than or equal to the precision. | +| test.c:81:3:81:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs5' whose upper bound (15) is greater than or equal to the precision. | +| test.c:83:3:83:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:85:3:85:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:87:3:87:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:89:3:89:14 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:91:3:91:15 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:93:3:93:15 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:95:3:95:15 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:97:3:97:15 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:99:3:99:15 | ... << ... | The operand 'lhs1' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:134:3:134:14 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:138:3:138:14 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:140:3:140:14 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:142:3:142:14 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:144:3:144:14 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:146:3:146:15 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:148:3:148:15 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:150:3:150:15 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:152:3:152:15 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:154:3:154:15 | ... << ... | The operand 'lhs3' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:159:3:159:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:161:3:161:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs4' whose upper bound (15) is greater than or equal to the precision. | +| test.c:163:3:163:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs5' whose upper bound (15) is greater than or equal to the precision. | +| test.c:165:3:165:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:167:3:167:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:169:3:169:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:171:3:171:14 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:173:3:173:15 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:175:3:175:15 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:177:3:177:15 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:179:3:179:15 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:181:3:181:15 | ... << ... | The operand 'lhs4' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:216:3:216:14 | ... << ... | The operand 'lhs6' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:220:3:220:14 | ... << ... | The operand 'lhs6' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:224:3:224:15 | ... << ... | The operand 'lhs6' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:226:3:226:15 | ... << ... | The operand 'lhs6' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:228:3:228:15 | ... << ... | The operand 'lhs6' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:236:3:236:14 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:238:3:238:14 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:240:3:240:14 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:242:3:242:14 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:244:3:244:15 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:246:3:246:15 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:248:3:248:15 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:250:3:250:15 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:252:3:252:15 | ... << ... | The operand 'lhs7' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:292:3:292:15 | ... << ... | The operand 'lhs9' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:316:3:316:16 | ... << ... | The operand 'lhs10' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:318:3:318:16 | ... << ... | The operand 'lhs10' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:320:3:320:16 | ... << ... | The operand 'lhs10' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:358:3:358:16 | ... << ... | The operand 'lhs12' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:374:3:374:16 | ... << ... | The operand 'lhs13' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:376:3:376:16 | ... << ... | The operand 'lhs13' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:378:3:378:16 | ... << ... | The operand 'lhs13' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1579:3:1580:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs0' whose upper bound (8) is greater than or equal to the precision. | +| test.c:1583:3:1584:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:1585:3:1586:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs4' whose upper bound (15) is greater than or equal to the precision. | +| test.c:1587:3:1588:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs5' whose upper bound (15) is greater than or equal to the precision. | +| test.c:1589:3:1590:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1591:3:1592:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1593:3:1594:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1595:3:1596:10 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1597:3:1597:15 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1599:3:1599:15 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1601:3:1601:15 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1603:3:1603:15 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1605:3:1605:15 | ... >> ... | The operand 'lhs0' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1607:3:1608:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs0' whose upper bound (8) is greater than or equal to the precision. | +| test.c:1609:3:1610:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs1' whose upper bound (7) is greater than or equal to the precision. | +| test.c:1611:3:1612:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs2' whose upper bound (7) is greater than or equal to the precision. | +| test.c:1613:3:1614:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:1615:3:1616:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs4' whose upper bound (15) is greater than or equal to the precision. | +| test.c:1617:3:1618:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs5' whose upper bound (15) is greater than or equal to the precision. | +| test.c:1619:3:1620:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1621:3:1622:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1623:3:1624:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1625:3:1626:10 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1627:3:1627:15 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1629:3:1629:15 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1631:3:1631:15 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1633:3:1633:15 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1635:3:1635:15 | ... >> ... | The operand 'lhs1' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1670:3:1671:10 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:1674:3:1675:10 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1676:3:1677:10 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1678:3:1679:10 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1680:3:1681:10 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1682:3:1682:15 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1684:3:1684:15 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1686:3:1686:15 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1688:3:1688:15 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1690:3:1690:15 | ... >> ... | The operand 'lhs3' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1695:3:1696:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs3' whose upper bound (16) is greater than or equal to the precision. | +| test.c:1697:3:1698:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs4' whose upper bound (15) is greater than or equal to the precision. | +| test.c:1699:3:1700:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs5' whose upper bound (15) is greater than or equal to the precision. | +| test.c:1701:3:1702:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1703:3:1704:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1705:3:1706:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1707:3:1708:10 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1709:3:1709:15 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1711:3:1711:15 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1713:3:1713:15 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1715:3:1715:15 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1717:3:1717:15 | ... >> ... | The operand 'lhs4' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1752:3:1753:10 | ... >> ... | The operand 'lhs6' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1756:3:1757:10 | ... >> ... | The operand 'lhs6' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1760:3:1760:15 | ... >> ... | The operand 'lhs6' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1762:3:1762:15 | ... >> ... | The operand 'lhs6' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1764:3:1764:15 | ... >> ... | The operand 'lhs6' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1772:3:1773:10 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs6' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1774:3:1775:10 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs7' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1776:3:1777:10 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs8' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1778:3:1779:10 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs9' whose upper bound (32) is greater than or equal to the precision. | +| test.c:1780:3:1780:15 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs10' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1782:3:1782:15 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs11' whose upper bound (31) is greater than or equal to the precision. | +| test.c:1784:3:1784:15 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1786:3:1786:15 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1788:3:1788:15 | ... >> ... | The operand 'lhs7' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1828:3:1828:15 | ... >> ... | The operand 'lhs9' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1852:3:1852:16 | ... >> ... | The operand 'lhs10' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1854:3:1854:16 | ... >> ... | The operand 'lhs10' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1856:3:1856:16 | ... >> ... | The operand 'lhs10' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1894:3:1894:16 | ... >> ... | The operand 'lhs12' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1910:3:1910:16 | ... >> ... | The operand 'lhs13' is shifted by an expression 'rhs12' whose upper bound (64) is greater than or equal to the precision. | +| test.c:1912:3:1912:16 | ... >> ... | The operand 'lhs13' is shifted by an expression 'rhs13' whose upper bound (63) is greater than or equal to the precision. | +| test.c:1914:3:1914:16 | ... >> ... | The operand 'lhs13' is shifted by an expression 'rhs14' whose upper bound (63) is greater than or equal to the precision. | +| test.c:3115:3:3115:12 | ... << ... | The operand 'lhs0' is shifted by an expression '- ...' which may be negative. | +| test.c:3116:3:3116:12 | ... << ... | The operand 'lhs1' is shifted by an expression '- ...' which may be negative. | +| test.c:3117:3:3117:12 | ... << ... | The operand 'lhs2' is shifted by an expression '- ...' which may be negative. | +| test.c:3118:3:3118:12 | ... << ... | The operand 'lhs3' is shifted by an expression '- ...' which may be negative. | +| test.c:3119:3:3119:12 | ... << ... | The operand 'lhs4' is shifted by an expression '- ...' which may be negative. | +| test.c:3120:3:3120:12 | ... << ... | The operand 'lhs5' is shifted by an expression '- ...' which may be negative. | +| test.c:3121:3:3121:12 | ... << ... | The operand 'lhs6' is shifted by an expression '- ...' which may be negative. | +| test.c:3122:3:3122:12 | ... << ... | The operand 'lhs7' is shifted by an expression '- ...' which may be negative. | +| test.c:3123:3:3123:12 | ... << ... | The operand 'lhs8' is shifted by an expression '- ...' which may be negative. | +| test.c:3124:3:3124:12 | ... << ... | The operand 'lhs9' is shifted by an expression '- ...' which may be negative. | +| test.c:3125:3:3125:13 | ... << ... | The operand 'lhs10' is shifted by an expression '- ...' which may be negative. | +| test.c:3126:3:3126:13 | ... << ... | The operand 'lhs11' is shifted by an expression '- ...' which may be negative. | +| test.c:3127:3:3127:13 | ... << ... | The operand 'lhs12' is shifted by an expression '- ...' which may be negative. | +| test.c:3128:3:3128:13 | ... << ... | The operand 'lhs13' is shifted by an expression '- ...' which may be negative. | +| test.c:3129:3:3129:13 | ... << ... | The operand 'lhs14' is shifted by an expression '- ...' which may be negative. | diff --git a/c/cert/test/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.qlref b/c/cert/test/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.qlref new file mode 100644 index 0000000000..9ed91335c9 --- /dev/null +++ b/c/cert/test/rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.qlref @@ -0,0 +1 @@ +rules/INT34-C/ExprShiftedbyNegativeOrGreaterPrecisionOperand.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT34-C/test.c b/c/cert/test/rules/INT34-C/test.c new file mode 100644 index 0000000000..c47df4b55d --- /dev/null +++ b/c/cert/test/rules/INT34-C/test.c @@ -0,0 +1,3132 @@ +#include +#include +#include + +extern size_t popcount(uintmax_t x){}; +#define PRECISION(x) popcount(x) + +int main() { + unsigned char lhs0 = UCHAR_MAX; + signed char lhs1 = CHAR_MAX; + char lhs2 = CHAR_MAX; + unsigned short lhs3 = USHRT_MAX; + signed short lhs4 = SHRT_MAX; + short lhs5 = SHRT_MAX; + unsigned int lhs6 = UINT_MAX; + signed int lhs7 = INT_MAX; + int lhs8 = INT_MAX; + unsigned long lhs9 = ULONG_MAX; + signed long lhs10 = LONG_MAX; + long lhs11 = LONG_MAX; + unsigned long long lhs12 = ULLONG_MAX; + signed long long lhs13 = LLONG_MAX; + long long lhs14 = LLONG_MAX; + + unsigned long long rhs0 = 8; + unsigned long long rhs1 = 7; + unsigned long long rhs2 = 7; + unsigned long long rhs3 = 16; + unsigned long long rhs4 = 15; + unsigned long long rhs5 = 15; + unsigned long long rhs6 = 32; + unsigned long long rhs7 = 31; + unsigned long long rhs8 = 31; + unsigned long long rhs9 = 32; + unsigned long long rhs10 = 31; + unsigned long long rhs11 = 31; + unsigned long long rhs12 = 64; + unsigned long long rhs13 = 63; + unsigned long long rhs14 = 63; + + /* ========== Left shifts ========== */ + + lhs0 << rhs0; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs0's + lhs0 << rhs1; // COMPLIANT: lhs0's precision is strictly greater than rhs1 + lhs0 << rhs2; // COMPLIANT: lhs0's precision is strictly greater than rhs2 + lhs0 << rhs3; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs3's + lhs0 << rhs4; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs4's + lhs0 << rhs5; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs5's + lhs0 << rhs6; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs6's + lhs0 << rhs7; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs7's + lhs0 << rhs8; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs8's + lhs0 << rhs9; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs9's + lhs0 << rhs10; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs10's + lhs0 << rhs11; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs11's + lhs0 << rhs12; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs12's + lhs0 << rhs13; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs13's + lhs0 << rhs14; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs14's + lhs1 << rhs0; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs0's + lhs1 << rhs1; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs1's + lhs1 << rhs2; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs2's + lhs1 << rhs3; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs3's + lhs1 << rhs4; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs4's + lhs1 << rhs5; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs5's + lhs1 << rhs6; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs6's + lhs1 << rhs7; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs7's + lhs1 << rhs8; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs8's + lhs1 << rhs9; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs9's + lhs1 << rhs10; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs10's + lhs1 << rhs11; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs11's + lhs1 << rhs12; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs12's + lhs1 << rhs13; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs13's + lhs1 << rhs14; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs14's + lhs2 << rhs0; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs0's + lhs2 << rhs1; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs1's + lhs2 << rhs2; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs2's + lhs2 << rhs3; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs3's + lhs2 << rhs4; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs4's + lhs2 << rhs5; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs5's + lhs2 << rhs6; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs6's + lhs2 << rhs7; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs7's + lhs2 << rhs8; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs8's + lhs2 << rhs9; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs9's + lhs2 << rhs10; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs10's + lhs2 << rhs11; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs11's + lhs2 << rhs12; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs12's + lhs2 << rhs13; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs13's + lhs2 << rhs14; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs14's + lhs3 << rhs0; // COMPLIANT: lhs3's precision is strictly greater than rhs0 + lhs3 << rhs1; // COMPLIANT: lhs3's precision is strictly greater than rhs1 + lhs3 << rhs2; // COMPLIANT: lhs3's precision is strictly greater than rhs2 + lhs3 << rhs3; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs3's + lhs3 << rhs4; // COMPLIANT: lhs3's precision is strictly greater than rhs4 + lhs3 << rhs5; // COMPLIANT: lhs3's precision is strictly greater than rhs5 + lhs3 << rhs6; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs6's + lhs3 << rhs7; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs7's + lhs3 << rhs8; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs8's + lhs3 << rhs9; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs9's + lhs3 << rhs10; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs10's + lhs3 << rhs11; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs11's + lhs3 << rhs12; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs12's + lhs3 << rhs13; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs13's + lhs3 << rhs14; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs14's + lhs4 << rhs0; // COMPLIANT: lhs4's precision is strictly greater than rhs0 + lhs4 << rhs1; // COMPLIANT: lhs4's precision is strictly greater than rhs1 + lhs4 << rhs2; // COMPLIANT: lhs4's precision is strictly greater than rhs2 + lhs4 << rhs3; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs3's + lhs4 << rhs4; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs4's + lhs4 << rhs5; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs5's + lhs4 << rhs6; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs6's + lhs4 << rhs7; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs7's + lhs4 << rhs8; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs8's + lhs4 << rhs9; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs9's + lhs4 << rhs10; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs10's + lhs4 << rhs11; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs11's + lhs4 << rhs12; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs12's + lhs4 << rhs13; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs13's + lhs4 << rhs14; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs14's + lhs5 << rhs0; // COMPLIANT: lhs5's precision is strictly greater than rhs0 + lhs5 << rhs1; // COMPLIANT: lhs5's precision is strictly greater than rhs1 + lhs5 << rhs2; // COMPLIANT: lhs5's precision is strictly greater than rhs2 + lhs5 << rhs3; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs3's + lhs5 << rhs4; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs4's + lhs5 << rhs5; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs5's + lhs5 << rhs6; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs6's + lhs5 << rhs7; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs7's + lhs5 << rhs8; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs8's + lhs5 << rhs9; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs9's + lhs5 << rhs10; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs10's + lhs5 << rhs11; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs11's + lhs5 << rhs12; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs12's + lhs5 << rhs13; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs13's + lhs5 << rhs14; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs14's + lhs6 << rhs0; // COMPLIANT: lhs6's precision is strictly greater than rhs0 + lhs6 << rhs1; // COMPLIANT: lhs6's precision is strictly greater than rhs1 + lhs6 << rhs2; // COMPLIANT: lhs6's precision is strictly greater than rhs2 + lhs6 << rhs3; // COMPLIANT: lhs6's precision is strictly greater than rhs3 + lhs6 << rhs4; // COMPLIANT: lhs6's precision is strictly greater than rhs4 + lhs6 << rhs5; // COMPLIANT: lhs6's precision is strictly greater than rhs5 + lhs6 << rhs6; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs6's + lhs6 << rhs7; // COMPLIANT: lhs6's precision is strictly greater than rhs7 + lhs6 << rhs8; // COMPLIANT: lhs6's precision is strictly greater than rhs8 + lhs6 << rhs9; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs9's + lhs6 << rhs10; // COMPLIANT: lhs6's precision is strictly greater than rhs10 + lhs6 << rhs11; // COMPLIANT: lhs6's precision is strictly greater than rhs11 + lhs6 << rhs12; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs12's + lhs6 << rhs13; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs13's + lhs6 << rhs14; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs14's + lhs7 << rhs0; // COMPLIANT: lhs7's precision is strictly greater than rhs0 + lhs7 << rhs1; // COMPLIANT: lhs7's precision is strictly greater than rhs1 + lhs7 << rhs2; // COMPLIANT: lhs7's precision is strictly greater than rhs2 + lhs7 << rhs3; // COMPLIANT: lhs7's precision is strictly greater than rhs3 + lhs7 << rhs4; // COMPLIANT: lhs7's precision is strictly greater than rhs4 + lhs7 << rhs5; // COMPLIANT: lhs7's precision is strictly greater than rhs5 + lhs7 << rhs6; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs6's + lhs7 << rhs7; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs7's + lhs7 << rhs8; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs8's + lhs7 << rhs9; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs9's + lhs7 << rhs10; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs10's + lhs7 << rhs11; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs11's + lhs7 << rhs12; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs12's + lhs7 << rhs13; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs13's + lhs7 << rhs14; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs14's + lhs8 << rhs0; // COMPLIANT: lhs8's precision is strictly greater than rhs0 + lhs8 << rhs1; // COMPLIANT: lhs8's precision is strictly greater than rhs1 + lhs8 << rhs2; // COMPLIANT: lhs8's precision is strictly greater than rhs2 + lhs8 << rhs3; // COMPLIANT: lhs8's precision is strictly greater than rhs3 + lhs8 << rhs4; // COMPLIANT: lhs8's precision is strictly greater than rhs4 + lhs8 << rhs5; // COMPLIANT: lhs8's precision is strictly greater than rhs5 + lhs8 << rhs6; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs6's + lhs8 << rhs7; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs7's + lhs8 << rhs8; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs8's + lhs8 << rhs9; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs9's + lhs8 << rhs10; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs10's + lhs8 << rhs11; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs11's + lhs8 << rhs12; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs12's + lhs8 << rhs13; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs13's + lhs8 << rhs14; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs14's + lhs9 << rhs0; // COMPLIANT: lhs9's precision is strictly greater than rhs0 + lhs9 << rhs1; // COMPLIANT: lhs9's precision is strictly greater than rhs1 + lhs9 << rhs2; // COMPLIANT: lhs9's precision is strictly greater than rhs2 + lhs9 << rhs3; // COMPLIANT: lhs9's precision is strictly greater than rhs3 + lhs9 << rhs4; // COMPLIANT: lhs9's precision is strictly greater than rhs4 + lhs9 << rhs5; // COMPLIANT: lhs9's precision is strictly greater than rhs5 + lhs9 << rhs6; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs6's + lhs9 << rhs7; // COMPLIANT: lhs9's precision is strictly greater than rhs7 + lhs9 << rhs8; // COMPLIANT: lhs9's precision is strictly greater than rhs8 + lhs9 << rhs9; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs9's + lhs9 << rhs10; // COMPLIANT: lhs9's precision is strictly greater than rhs10 + lhs9 << rhs11; // COMPLIANT: lhs9's precision is strictly greater than rhs11 + lhs9 << rhs12; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs12's + lhs9 << rhs13; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs13's + lhs9 << rhs14; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs14's + lhs10 << rhs0; // COMPLIANT: lhs10's precision is strictly greater than rhs0 + lhs10 << rhs1; // COMPLIANT: lhs10's precision is strictly greater than rhs1 + lhs10 << rhs2; // COMPLIANT: lhs10's precision is strictly greater than rhs2 + lhs10 << rhs3; // COMPLIANT: lhs10's precision is strictly greater than rhs3 + lhs10 << rhs4; // COMPLIANT: lhs10's precision is strictly greater than rhs4 + lhs10 << rhs5; // COMPLIANT: lhs10's precision is strictly greater than rhs5 + lhs10 << rhs6; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs6's + lhs10 << rhs7; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs7's + lhs10 << rhs8; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs8's + lhs10 << rhs9; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs9's + lhs10 << rhs10; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs10's + lhs10 << rhs11; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs11's + lhs10 << rhs12; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs12's + lhs10 << rhs13; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs13's + lhs10 << rhs14; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs14's + lhs11 << rhs0; // COMPLIANT: lhs11's precision is strictly greater than rhs0 + lhs11 << rhs1; // COMPLIANT: lhs11's precision is strictly greater than rhs1 + lhs11 << rhs2; // COMPLIANT: lhs11's precision is strictly greater than rhs2 + lhs11 << rhs3; // COMPLIANT: lhs11's precision is strictly greater than rhs3 + lhs11 << rhs4; // COMPLIANT: lhs11's precision is strictly greater than rhs4 + lhs11 << rhs5; // COMPLIANT: lhs11's precision is strictly greater than rhs5 + lhs11 << rhs6; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs6's + lhs11 << rhs7; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs7's + lhs11 << rhs8; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs8's + lhs11 << rhs9; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs9's + lhs11 << rhs10; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs10's + lhs11 << rhs11; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs11's + lhs11 << rhs12; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs12's + lhs11 << rhs13; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs13's + lhs11 << rhs14; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs14's + lhs12 << rhs0; // COMPLIANT: lhs12's precision is strictly greater than rhs0 + lhs12 << rhs1; // COMPLIANT: lhs12's precision is strictly greater than rhs1 + lhs12 << rhs2; // COMPLIANT: lhs12's precision is strictly greater than rhs2 + lhs12 << rhs3; // COMPLIANT: lhs12's precision is strictly greater than rhs3 + lhs12 << rhs4; // COMPLIANT: lhs12's precision is strictly greater than rhs4 + lhs12 << rhs5; // COMPLIANT: lhs12's precision is strictly greater than rhs5 + lhs12 << rhs6; // COMPLIANT: lhs12's precision is strictly greater than rhs6 + lhs12 << rhs7; // COMPLIANT: lhs12's precision is strictly greater than rhs7 + lhs12 << rhs8; // COMPLIANT: lhs12's precision is strictly greater than rhs8 + lhs12 << rhs9; // COMPLIANT: lhs12's precision is strictly greater than rhs9 + lhs12 << rhs10; // COMPLIANT: lhs12's precision is strictly greater than rhs10 + lhs12 << rhs11; // COMPLIANT: lhs12's precision is strictly greater than rhs11 + lhs12 << rhs12; // NON_COMPLIANT: lhs12's precision is not strictly greater + // than rhs12's + lhs12 << rhs13; // COMPLIANT: lhs12's precision is strictly greater than rhs13 + lhs12 << rhs14; // COMPLIANT: lhs12's precision is strictly greater than rhs14 + lhs13 << rhs0; // COMPLIANT: lhs13's precision is strictly greater than rhs0 + lhs13 << rhs1; // COMPLIANT: lhs13's precision is strictly greater than rhs1 + lhs13 << rhs2; // COMPLIANT: lhs13's precision is strictly greater than rhs2 + lhs13 << rhs3; // COMPLIANT: lhs13's precision is strictly greater than rhs3 + lhs13 << rhs4; // COMPLIANT: lhs13's precision is strictly greater than rhs4 + lhs13 << rhs5; // COMPLIANT: lhs13's precision is strictly greater than rhs5 + lhs13 << rhs6; // COMPLIANT: lhs13's precision is strictly greater than rhs6 + lhs13 << rhs7; // COMPLIANT: lhs13's precision is strictly greater than rhs7 + lhs13 << rhs8; // COMPLIANT: lhs13's precision is strictly greater than rhs8 + lhs13 << rhs9; // COMPLIANT: lhs13's precision is strictly greater than rhs9 + lhs13 << rhs10; // COMPLIANT: lhs13's precision is strictly greater than rhs10 + lhs13 << rhs11; // COMPLIANT: lhs13's precision is strictly greater than rhs11 + lhs13 << rhs12; // NON_COMPLIANT: lhs13's precision is not strictly greater + // than rhs12's + lhs13 << rhs13; // NON_COMPLIANT: lhs13's precision is not strictly greater + // than rhs13's + lhs13 << rhs14; // NON_COMPLIANT: lhs13's precision is not strictly greater + // than rhs14's + lhs14 << rhs0; // COMPLIANT: lhs14's precision is strictly greater than rhs0 + lhs14 << rhs1; // COMPLIANT: lhs14's precision is strictly greater than rhs1 + lhs14 << rhs2; // COMPLIANT: lhs14's precision is strictly greater than rhs2 + lhs14 << rhs3; // COMPLIANT: lhs14's precision is strictly greater than rhs3 + lhs14 << rhs4; // COMPLIANT: lhs14's precision is strictly greater than rhs4 + lhs14 << rhs5; // COMPLIANT: lhs14's precision is strictly greater than rhs5 + lhs14 << rhs6; // COMPLIANT: lhs14's precision is strictly greater than rhs6 + lhs14 << rhs7; // COMPLIANT: lhs14's precision is strictly greater than rhs7 + lhs14 << rhs8; // COMPLIANT: lhs14's precision is strictly greater than rhs8 + lhs14 << rhs9; // COMPLIANT: lhs14's precision is strictly greater than rhs9 + lhs14 << rhs10; // COMPLIANT: lhs14's precision is strictly greater than rhs10 + lhs14 << rhs11; // COMPLIANT: lhs14's precision is strictly greater than rhs11 + lhs14 << rhs12; // NON_COMPLIANT: lhs14's precision is not strictly greater + // than rhs12's + lhs14 << rhs13; // NON_COMPLIANT: lhs14's precision is not strictly greater + // than rhs13's + lhs14 << rhs14; // NON_COMPLIANT: lhs14's precision is not strictly greater + // than rhs14's + + /* ===== Left shift with guards, the shift expression is at `then` branch + * ===== */ + + if (rhs0 < PRECISION(UCHAR_MAX)) + lhs0 << rhs0; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + if (rhs3 < PRECISION(UCHAR_MAX)) + lhs0 << rhs3; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(UCHAR_MAX)) + lhs0 << rhs4; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(UCHAR_MAX)) + lhs0 << rhs5; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(UCHAR_MAX)) + lhs0 << rhs6; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(UCHAR_MAX)) + lhs0 << rhs7; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(UCHAR_MAX)) + lhs0 << rhs8; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(UCHAR_MAX)) + lhs0 << rhs9; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(UCHAR_MAX)) + lhs0 << rhs10; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(UCHAR_MAX)) + lhs0 << rhs11; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(UCHAR_MAX)) + lhs0 << rhs12; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(UCHAR_MAX)) + lhs0 << rhs13; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(UCHAR_MAX)) + lhs0 << rhs14; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs0 < PRECISION(CHAR_MAX)) + lhs1 << rhs0; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + if (rhs1 < PRECISION(CHAR_MAX)) + lhs1 << rhs1; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + if (rhs2 < PRECISION(CHAR_MAX)) + lhs1 << rhs2; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + if (rhs3 < PRECISION(CHAR_MAX)) + lhs1 << rhs3; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(CHAR_MAX)) + lhs1 << rhs4; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(CHAR_MAX)) + lhs1 << rhs5; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(CHAR_MAX)) + lhs1 << rhs6; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(CHAR_MAX)) + lhs1 << rhs7; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(CHAR_MAX)) + lhs1 << rhs8; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(CHAR_MAX)) + lhs1 << rhs9; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(CHAR_MAX)) + lhs1 << rhs10; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(CHAR_MAX)) + lhs1 << rhs11; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(CHAR_MAX)) + lhs1 << rhs12; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(CHAR_MAX)) + lhs1 << rhs13; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(CHAR_MAX)) + lhs1 << rhs14; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs0 < PRECISION(CHAR_MAX)) + lhs2 << rhs0; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + if (rhs1 < PRECISION(CHAR_MAX)) + lhs2 << rhs1; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + if (rhs2 < PRECISION(CHAR_MAX)) + lhs2 << rhs2; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + if (rhs3 < PRECISION(CHAR_MAX)) + lhs2 << rhs3; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(CHAR_MAX)) + lhs2 << rhs4; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(CHAR_MAX)) + lhs2 << rhs5; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(CHAR_MAX)) + lhs2 << rhs6; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(CHAR_MAX)) + lhs2 << rhs7; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(CHAR_MAX)) + lhs2 << rhs8; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(CHAR_MAX)) + lhs2 << rhs9; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(CHAR_MAX)) + lhs2 << rhs10; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(CHAR_MAX)) + lhs2 << rhs11; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(CHAR_MAX)) + lhs2 << rhs12; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(CHAR_MAX)) + lhs2 << rhs13; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(CHAR_MAX)) + lhs2 << rhs14; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs3 < PRECISION(USHRT_MAX)) + lhs3 << rhs3; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs6 < PRECISION(USHRT_MAX)) + lhs3 << rhs6; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(USHRT_MAX)) + lhs3 << rhs7; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(USHRT_MAX)) + lhs3 << rhs8; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(USHRT_MAX)) + lhs3 << rhs9; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(USHRT_MAX)) + lhs3 << rhs10; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(USHRT_MAX)) + lhs3 << rhs11; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(USHRT_MAX)) + lhs3 << rhs12; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(USHRT_MAX)) + lhs3 << rhs13; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(USHRT_MAX)) + lhs3 << rhs14; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs3 < PRECISION(SHRT_MAX)) + lhs4 << rhs3; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(SHRT_MAX)) + lhs4 << rhs4; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(SHRT_MAX)) + lhs4 << rhs5; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(SHRT_MAX)) + lhs4 << rhs6; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(SHRT_MAX)) + lhs4 << rhs7; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(SHRT_MAX)) + lhs4 << rhs8; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(SHRT_MAX)) + lhs4 << rhs9; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(SHRT_MAX)) + lhs4 << rhs10; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(SHRT_MAX)) + lhs4 << rhs11; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(SHRT_MAX)) + lhs4 << rhs12; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(SHRT_MAX)) + lhs4 << rhs13; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(SHRT_MAX)) + lhs4 << rhs14; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs3 < PRECISION(SHRT_MAX)) + lhs5 << rhs3; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(SHRT_MAX)) + lhs5 << rhs4; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(SHRT_MAX)) + lhs5 << rhs5; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(SHRT_MAX)) + lhs5 << rhs6; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(SHRT_MAX)) + lhs5 << rhs7; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(SHRT_MAX)) + lhs5 << rhs8; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(SHRT_MAX)) + lhs5 << rhs9; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(SHRT_MAX)) + lhs5 << rhs10; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(SHRT_MAX)) + lhs5 << rhs11; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(SHRT_MAX)) + lhs5 << rhs12; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(SHRT_MAX)) + lhs5 << rhs13; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(SHRT_MAX)) + lhs5 << rhs14; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(UINT_MAX)) + lhs6 << rhs6; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs9 < PRECISION(UINT_MAX)) + lhs6 << rhs9; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs12 < PRECISION(UINT_MAX)) + lhs6 << rhs12; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(UINT_MAX)) + lhs6 << rhs13; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(UINT_MAX)) + lhs6 << rhs14; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(INT_MAX)) + lhs7 << rhs6; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(INT_MAX)) + lhs7 << rhs7; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(INT_MAX)) + lhs7 << rhs8; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(INT_MAX)) + lhs7 << rhs9; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(INT_MAX)) + lhs7 << rhs10; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(INT_MAX)) + lhs7 << rhs11; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(INT_MAX)) + lhs7 << rhs12; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(INT_MAX)) + lhs7 << rhs13; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(INT_MAX)) + lhs7 << rhs14; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(INT_MAX)) + lhs8 << rhs6; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(INT_MAX)) + lhs8 << rhs7; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(INT_MAX)) + lhs8 << rhs8; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(INT_MAX)) + lhs8 << rhs9; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(INT_MAX)) + lhs8 << rhs10; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(INT_MAX)) + lhs8 << rhs11; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(INT_MAX)) + lhs8 << rhs12; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(INT_MAX)) + lhs8 << rhs13; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(INT_MAX)) + lhs8 << rhs14; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(ULONG_MAX)) + lhs9 << rhs6; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs9 < PRECISION(ULONG_MAX)) + lhs9 << rhs9; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs12 < PRECISION(ULONG_MAX)) + lhs9 << rhs12; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(ULONG_MAX)) + lhs9 << rhs13; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(ULONG_MAX)) + lhs9 << rhs14; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(LONG_MAX)) + lhs10 << rhs6; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(LONG_MAX)) + lhs10 << rhs7; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(LONG_MAX)) + lhs10 << rhs8; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(LONG_MAX)) + lhs10 << rhs9; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(LONG_MAX)) + lhs10 << rhs10; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(LONG_MAX)) + lhs10 << rhs11; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LONG_MAX)) + lhs10 << rhs12; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LONG_MAX)) + lhs10 << rhs13; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LONG_MAX)) + lhs10 << rhs14; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(LONG_MAX)) + lhs11 << rhs6; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(LONG_MAX)) + lhs11 << rhs7; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(LONG_MAX)) + lhs11 << rhs8; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(LONG_MAX)) + lhs11 << rhs9; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(LONG_MAX)) + lhs11 << rhs10; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(LONG_MAX)) + lhs11 << rhs11; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LONG_MAX)) + lhs11 << rhs12; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LONG_MAX)) + lhs11 << rhs13; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LONG_MAX)) + lhs11 << rhs14; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs12 < PRECISION(ULLONG_MAX)) + lhs12 << rhs12; // COMPLIANT: lhs12's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LLONG_MAX)) + lhs13 << rhs12; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LLONG_MAX)) + lhs13 << rhs13; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LLONG_MAX)) + lhs13 << rhs14; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LLONG_MAX)) + lhs14 << rhs12; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LLONG_MAX)) + lhs14 << rhs13; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LLONG_MAX)) + lhs14 << rhs14; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + + /* ===== Left shift with guards, the shift expression is at `else` branch + * ===== */ + + if (rhs0 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs0; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs3; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs4; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs5; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs6; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs7; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs8; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs9; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs10; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs11; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs12; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs13; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 << rhs14; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs0 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs0; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + } + if (rhs1 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs1; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + } + if (rhs2 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs2; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs3; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs4; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs5; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs6; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs7; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs8; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs9; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs10; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs11; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs12; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs13; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 << rhs14; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs0 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs0; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + } + if (rhs1 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs1; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + } + if (rhs2 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs2; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs3; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs4; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs5; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs6; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs7; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs8; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs9; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs10; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs11; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs12; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs13; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 << rhs14; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs3; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs6; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs7; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs8; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs9; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs10; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs11; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs12; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs13; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 << rhs14; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs3; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs4; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs5; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs6; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs7; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs8; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs9; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs10; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs11; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs12; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs13; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 << rhs14; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs3; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs4; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs5; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs6; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs7; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs8; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs9; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs10; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs11; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs12; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs13; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 << rhs14; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 << rhs6; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 << rhs9; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 << rhs12; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 << rhs13; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 << rhs14; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs6; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs7; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs8; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs9; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs10; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs11; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs12; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs13; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 << rhs14; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs6; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs7; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs8; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs9; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs10; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs11; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs12; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs13; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 << rhs14; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 << rhs6; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 << rhs9; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 << rhs12; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 << rhs13; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 << rhs14; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs6; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs7; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs8; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs9; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs10; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs11; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs12; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs13; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 << rhs14; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs6; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs7; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs8; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs9; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs10; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs11; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs12; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs13; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 << rhs14; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(ULLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs12 << rhs12; // COMPLIANT: lhs12's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs13 << rhs12; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs13 << rhs13; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs13 << rhs14; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs14 << rhs12; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs14 << rhs13; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs14 << rhs14; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + + /* ========== Right shifts ========== */ + + lhs0 >> + rhs0; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs0 + lhs0 >> rhs1; // COMPLIANT: lhs0's precision is strictly greater than rhs1 + lhs0 >> rhs2; // COMPLIANT: lhs0's precision is strictly greater than rhs2 + lhs0 >> + rhs3; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs3 + lhs0 >> + rhs4; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs4 + lhs0 >> + rhs5; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs5 + lhs0 >> + rhs6; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs6 + lhs0 >> + rhs7; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs7 + lhs0 >> + rhs8; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs8 + lhs0 >> + rhs9; // NON_COMPLIANT: lhs0's precision is not strictly greater than rhs9 + lhs0 >> rhs10; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs10 + lhs0 >> rhs11; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs11 + lhs0 >> rhs12; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs12 + lhs0 >> rhs13; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs13 + lhs0 >> rhs14; // NON_COMPLIANT: lhs0's precision is not strictly greater than + // rhs14 + lhs1 >> + rhs0; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs0 + lhs1 >> + rhs1; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs1 + lhs1 >> + rhs2; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs2 + lhs1 >> + rhs3; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs3 + lhs1 >> + rhs4; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs4 + lhs1 >> + rhs5; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs5 + lhs1 >> + rhs6; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs6 + lhs1 >> + rhs7; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs7 + lhs1 >> + rhs8; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs8 + lhs1 >> + rhs9; // NON_COMPLIANT: lhs1's precision is not strictly greater than rhs9 + lhs1 >> rhs10; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs10 + lhs1 >> rhs11; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs11 + lhs1 >> rhs12; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs12 + lhs1 >> rhs13; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs13 + lhs1 >> rhs14; // NON_COMPLIANT: lhs1's precision is not strictly greater than + // rhs14 + lhs2 >> + rhs0; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs0 + lhs2 >> + rhs1; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs1 + lhs2 >> + rhs2; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs2 + lhs2 >> + rhs3; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs3 + lhs2 >> + rhs4; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs4 + lhs2 >> + rhs5; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs5 + lhs2 >> + rhs6; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs6 + lhs2 >> + rhs7; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs7 + lhs2 >> + rhs8; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs8 + lhs2 >> + rhs9; // NON_COMPLIANT: lhs2's precision is not strictly greater than rhs9 + lhs2 >> rhs10; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs10 + lhs2 >> rhs11; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs11 + lhs2 >> rhs12; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs12 + lhs2 >> rhs13; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs13 + lhs2 >> rhs14; // NON_COMPLIANT: lhs2's precision is not strictly greater than + // rhs14 + lhs3 >> rhs0; // COMPLIANT: lhs3's precision is strictly greater than rhs0 + lhs3 >> rhs1; // COMPLIANT: lhs3's precision is strictly greater than rhs1 + lhs3 >> rhs2; // COMPLIANT: lhs3's precision is strictly greater than rhs2 + lhs3 >> + rhs3; // NON_COMPLIANT: lhs3's precision is not strictly greater than rhs3 + lhs3 >> rhs4; // COMPLIANT: lhs3's precision is strictly greater than rhs4 + lhs3 >> rhs5; // COMPLIANT: lhs3's precision is strictly greater than rhs5 + lhs3 >> + rhs6; // NON_COMPLIANT: lhs3's precision is not strictly greater than rhs6 + lhs3 >> + rhs7; // NON_COMPLIANT: lhs3's precision is not strictly greater than rhs7 + lhs3 >> + rhs8; // NON_COMPLIANT: lhs3's precision is not strictly greater than rhs8 + lhs3 >> + rhs9; // NON_COMPLIANT: lhs3's precision is not strictly greater than rhs9 + lhs3 >> rhs10; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs10 + lhs3 >> rhs11; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs11 + lhs3 >> rhs12; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs12 + lhs3 >> rhs13; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs13 + lhs3 >> rhs14; // NON_COMPLIANT: lhs3's precision is not strictly greater than + // rhs14 + lhs4 >> rhs0; // COMPLIANT: lhs4's precision is strictly greater than rhs0 + lhs4 >> rhs1; // COMPLIANT: lhs4's precision is strictly greater than rhs1 + lhs4 >> rhs2; // COMPLIANT: lhs4's precision is strictly greater than rhs2 + lhs4 >> + rhs3; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs3 + lhs4 >> + rhs4; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs4 + lhs4 >> + rhs5; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs5 + lhs4 >> + rhs6; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs6 + lhs4 >> + rhs7; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs7 + lhs4 >> + rhs8; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs8 + lhs4 >> + rhs9; // NON_COMPLIANT: lhs4's precision is not strictly greater than rhs9 + lhs4 >> rhs10; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs10 + lhs4 >> rhs11; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs11 + lhs4 >> rhs12; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs12 + lhs4 >> rhs13; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs13 + lhs4 >> rhs14; // NON_COMPLIANT: lhs4's precision is not strictly greater than + // rhs14 + lhs5 >> rhs0; // COMPLIANT: lhs5's precision is strictly greater than rhs0 + lhs5 >> rhs1; // COMPLIANT: lhs5's precision is strictly greater than rhs1 + lhs5 >> rhs2; // COMPLIANT: lhs5's precision is strictly greater than rhs2 + lhs5 >> + rhs3; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs3 + lhs5 >> + rhs4; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs4 + lhs5 >> + rhs5; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs5 + lhs5 >> + rhs6; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs6 + lhs5 >> + rhs7; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs7 + lhs5 >> + rhs8; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs8 + lhs5 >> + rhs9; // NON_COMPLIANT: lhs5's precision is not strictly greater than rhs9 + lhs5 >> rhs10; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs10 + lhs5 >> rhs11; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs11 + lhs5 >> rhs12; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs12 + lhs5 >> rhs13; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs13 + lhs5 >> rhs14; // NON_COMPLIANT: lhs5's precision is not strictly greater than + // rhs14 + lhs6 >> rhs0; // COMPLIANT: lhs6's precision is strictly greater than rhs0 + lhs6 >> rhs1; // COMPLIANT: lhs6's precision is strictly greater than rhs1 + lhs6 >> rhs2; // COMPLIANT: lhs6's precision is strictly greater than rhs2 + lhs6 >> rhs3; // COMPLIANT: lhs6's precision is strictly greater than rhs3 + lhs6 >> rhs4; // COMPLIANT: lhs6's precision is strictly greater than rhs4 + lhs6 >> rhs5; // COMPLIANT: lhs6's precision is strictly greater than rhs5 + lhs6 >> + rhs6; // NON_COMPLIANT: lhs6's precision is not strictly greater than rhs6 + lhs6 >> rhs7; // COMPLIANT: lhs6's precision is strictly greater than rhs7 + lhs6 >> rhs8; // COMPLIANT: lhs6's precision is strictly greater than rhs8 + lhs6 >> + rhs9; // NON_COMPLIANT: lhs6's precision is not strictly greater than rhs9 + lhs6 >> rhs10; // COMPLIANT: lhs6's precision is strictly greater than rhs10 + lhs6 >> rhs11; // COMPLIANT: lhs6's precision is strictly greater than rhs11 + lhs6 >> rhs12; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs12 + lhs6 >> rhs13; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs13 + lhs6 >> rhs14; // NON_COMPLIANT: lhs6's precision is not strictly greater than + // rhs14 + lhs7 >> rhs0; // COMPLIANT: lhs7's precision is strictly greater than rhs0 + lhs7 >> rhs1; // COMPLIANT: lhs7's precision is strictly greater than rhs1 + lhs7 >> rhs2; // COMPLIANT: lhs7's precision is strictly greater than rhs2 + lhs7 >> rhs3; // COMPLIANT: lhs7's precision is strictly greater than rhs3 + lhs7 >> rhs4; // COMPLIANT: lhs7's precision is strictly greater than rhs4 + lhs7 >> rhs5; // COMPLIANT: lhs7's precision is strictly greater than rhs5 + lhs7 >> + rhs6; // NON_COMPLIANT: lhs7's precision is not strictly greater than rhs6 + lhs7 >> + rhs7; // NON_COMPLIANT: lhs7's precision is not strictly greater than rhs7 + lhs7 >> + rhs8; // NON_COMPLIANT: lhs7's precision is not strictly greater than rhs8 + lhs7 >> + rhs9; // NON_COMPLIANT: lhs7's precision is not strictly greater than rhs9 + lhs7 >> rhs10; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs10 + lhs7 >> rhs11; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs11 + lhs7 >> rhs12; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs12 + lhs7 >> rhs13; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs13 + lhs7 >> rhs14; // NON_COMPLIANT: lhs7's precision is not strictly greater than + // rhs14 + lhs8 >> rhs0; // COMPLIANT: lhs8's precision is strictly greater than rhs0 + lhs8 >> rhs1; // COMPLIANT: lhs8's precision is strictly greater than rhs1 + lhs8 >> rhs2; // COMPLIANT: lhs8's precision is strictly greater than rhs2 + lhs8 >> rhs3; // COMPLIANT: lhs8's precision is strictly greater than rhs3 + lhs8 >> rhs4; // COMPLIANT: lhs8's precision is strictly greater than rhs4 + lhs8 >> rhs5; // COMPLIANT: lhs8's precision is strictly greater than rhs5 + lhs8 >> + rhs6; // NON_COMPLIANT: lhs8's precision is not strictly greater than rhs6 + lhs8 >> + rhs7; // NON_COMPLIANT: lhs8's precision is not strictly greater than rhs7 + lhs8 >> + rhs8; // NON_COMPLIANT: lhs8's precision is not strictly greater than rhs8 + lhs8 >> + rhs9; // NON_COMPLIANT: lhs8's precision is not strictly greater than rhs9 + lhs8 >> rhs10; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs10 + lhs8 >> rhs11; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs11 + lhs8 >> rhs12; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs12 + lhs8 >> rhs13; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs13 + lhs8 >> rhs14; // NON_COMPLIANT: lhs8's precision is not strictly greater than + // rhs14 + lhs9 >> rhs0; // COMPLIANT: lhs9's precision is strictly greater than rhs0 + lhs9 >> rhs1; // COMPLIANT: lhs9's precision is strictly greater than rhs1 + lhs9 >> rhs2; // COMPLIANT: lhs9's precision is strictly greater than rhs2 + lhs9 >> rhs3; // COMPLIANT: lhs9's precision is strictly greater than rhs3 + lhs9 >> rhs4; // COMPLIANT: lhs9's precision is strictly greater than rhs4 + lhs9 >> rhs5; // COMPLIANT: lhs9's precision is strictly greater than rhs5 + lhs9 >> + rhs6; // NON_COMPLIANT: lhs9's precision is not strictly greater than rhs6 + lhs9 >> rhs7; // COMPLIANT: lhs9's precision is strictly greater than rhs7 + lhs9 >> rhs8; // COMPLIANT: lhs9's precision is strictly greater than rhs8 + lhs9 >> + rhs9; // NON_COMPLIANT: lhs9's precision is not strictly greater than rhs9 + lhs9 >> rhs10; // COMPLIANT: lhs9's precision is strictly greater than rhs10 + lhs9 >> rhs11; // COMPLIANT: lhs9's precision is strictly greater than rhs11 + lhs9 >> rhs12; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs12 + lhs9 >> rhs13; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs13 + lhs9 >> rhs14; // NON_COMPLIANT: lhs9's precision is not strictly greater than + // rhs14 + lhs10 >> rhs0; // COMPLIANT: lhs10's precision is strictly greater than rhs0 + lhs10 >> rhs1; // COMPLIANT: lhs10's precision is strictly greater than rhs1 + lhs10 >> rhs2; // COMPLIANT: lhs10's precision is strictly greater than rhs2 + lhs10 >> rhs3; // COMPLIANT: lhs10's precision is strictly greater than rhs3 + lhs10 >> rhs4; // COMPLIANT: lhs10's precision is strictly greater than rhs4 + lhs10 >> rhs5; // COMPLIANT: lhs10's precision is strictly greater than rhs5 + lhs10 >> rhs6; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs6 + lhs10 >> rhs7; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs7 + lhs10 >> rhs8; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs8 + lhs10 >> rhs9; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs9 + lhs10 >> rhs10; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs10 + lhs10 >> rhs11; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs11 + lhs10 >> rhs12; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs12 + lhs10 >> rhs13; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs13 + lhs10 >> rhs14; // NON_COMPLIANT: lhs10's precision is not strictly greater + // than rhs14 + lhs11 >> rhs0; // COMPLIANT: lhs11's precision is strictly greater than rhs0 + lhs11 >> rhs1; // COMPLIANT: lhs11's precision is strictly greater than rhs1 + lhs11 >> rhs2; // COMPLIANT: lhs11's precision is strictly greater than rhs2 + lhs11 >> rhs3; // COMPLIANT: lhs11's precision is strictly greater than rhs3 + lhs11 >> rhs4; // COMPLIANT: lhs11's precision is strictly greater than rhs4 + lhs11 >> rhs5; // COMPLIANT: lhs11's precision is strictly greater than rhs5 + lhs11 >> rhs6; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs6 + lhs11 >> rhs7; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs7 + lhs11 >> rhs8; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs8 + lhs11 >> rhs9; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs9 + lhs11 >> rhs10; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs10 + lhs11 >> rhs11; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs11 + lhs11 >> rhs12; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs12 + lhs11 >> rhs13; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs13 + lhs11 >> rhs14; // NON_COMPLIANT: lhs11's precision is not strictly greater + // than rhs14 + lhs12 >> rhs0; // COMPLIANT: lhs12's precision is strictly greater than rhs0 + lhs12 >> rhs1; // COMPLIANT: lhs12's precision is strictly greater than rhs1 + lhs12 >> rhs2; // COMPLIANT: lhs12's precision is strictly greater than rhs2 + lhs12 >> rhs3; // COMPLIANT: lhs12's precision is strictly greater than rhs3 + lhs12 >> rhs4; // COMPLIANT: lhs12's precision is strictly greater than rhs4 + lhs12 >> rhs5; // COMPLIANT: lhs12's precision is strictly greater than rhs5 + lhs12 >> rhs6; // COMPLIANT: lhs12's precision is strictly greater than rhs6 + lhs12 >> rhs7; // COMPLIANT: lhs12's precision is strictly greater than rhs7 + lhs12 >> rhs8; // COMPLIANT: lhs12's precision is strictly greater than rhs8 + lhs12 >> rhs9; // COMPLIANT: lhs12's precision is strictly greater than rhs9 + lhs12 >> rhs10; // COMPLIANT: lhs12's precision is strictly greater than rhs10 + lhs12 >> rhs11; // COMPLIANT: lhs12's precision is strictly greater than rhs11 + lhs12 >> rhs12; // NON_COMPLIANT: lhs12's precision is not strictly greater + // than rhs12 + lhs12 >> rhs13; // COMPLIANT: lhs12's precision is strictly greater than rhs13 + lhs12 >> rhs14; // COMPLIANT: lhs12's precision is strictly greater than rhs14 + lhs13 >> rhs0; // COMPLIANT: lhs13's precision is strictly greater than rhs0 + lhs13 >> rhs1; // COMPLIANT: lhs13's precision is strictly greater than rhs1 + lhs13 >> rhs2; // COMPLIANT: lhs13's precision is strictly greater than rhs2 + lhs13 >> rhs3; // COMPLIANT: lhs13's precision is strictly greater than rhs3 + lhs13 >> rhs4; // COMPLIANT: lhs13's precision is strictly greater than rhs4 + lhs13 >> rhs5; // COMPLIANT: lhs13's precision is strictly greater than rhs5 + lhs13 >> rhs6; // COMPLIANT: lhs13's precision is strictly greater than rhs6 + lhs13 >> rhs7; // COMPLIANT: lhs13's precision is strictly greater than rhs7 + lhs13 >> rhs8; // COMPLIANT: lhs13's precision is strictly greater than rhs8 + lhs13 >> rhs9; // COMPLIANT: lhs13's precision is strictly greater than rhs9 + lhs13 >> rhs10; // COMPLIANT: lhs13's precision is strictly greater than rhs10 + lhs13 >> rhs11; // COMPLIANT: lhs13's precision is strictly greater than rhs11 + lhs13 >> rhs12; // NON_COMPLIANT: lhs13's precision is not strictly greater + // than rhs12 + lhs13 >> rhs13; // NON_COMPLIANT: lhs13's precision is not strictly greater + // than rhs13 + lhs13 >> rhs14; // NON_COMPLIANT: lhs13's precision is not strictly greater + // than rhs14 + lhs14 >> rhs0; // COMPLIANT: lhs14's precision is strictly greater than rhs0 + lhs14 >> rhs1; // COMPLIANT: lhs14's precision is strictly greater than rhs1 + lhs14 >> rhs2; // COMPLIANT: lhs14's precision is strictly greater than rhs2 + lhs14 >> rhs3; // COMPLIANT: lhs14's precision is strictly greater than rhs3 + lhs14 >> rhs4; // COMPLIANT: lhs14's precision is strictly greater than rhs4 + lhs14 >> rhs5; // COMPLIANT: lhs14's precision is strictly greater than rhs5 + lhs14 >> rhs6; // COMPLIANT: lhs14's precision is strictly greater than rhs6 + lhs14 >> rhs7; // COMPLIANT: lhs14's precision is strictly greater than rhs7 + lhs14 >> rhs8; // COMPLIANT: lhs14's precision is strictly greater than rhs8 + lhs14 >> rhs9; // COMPLIANT: lhs14's precision is strictly greater than rhs9 + lhs14 >> rhs10; // COMPLIANT: lhs14's precision is strictly greater than rhs10 + lhs14 >> rhs11; // COMPLIANT: lhs14's precision is strictly greater than rhs11 + lhs14 >> rhs12; // NON_COMPLIANT: lhs14's precision is not strictly greater + // than rhs12 + lhs14 >> rhs13; // NON_COMPLIANT: lhs14's precision is not strictly greater + // than rhs13 + lhs14 >> rhs14; // NON_COMPLIANT: lhs14's precision is not strictly greater + // than rhs14 + + /* ===== Right shift with guards, the shift expression is at `then` branch + * ===== */ + + if (rhs0 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs0; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + if (rhs3 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs3; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs4; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs5; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs6; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs7; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs8; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs9; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs10; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs11; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs12; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs13; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(UCHAR_MAX)) + lhs0 >> rhs14; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs0 < PRECISION(CHAR_MAX)) + lhs1 >> rhs0; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + if (rhs1 < PRECISION(CHAR_MAX)) + lhs1 >> rhs1; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + if (rhs2 < PRECISION(CHAR_MAX)) + lhs1 >> rhs2; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + if (rhs3 < PRECISION(CHAR_MAX)) + lhs1 >> rhs3; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(CHAR_MAX)) + lhs1 >> rhs4; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(CHAR_MAX)) + lhs1 >> rhs5; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(CHAR_MAX)) + lhs1 >> rhs6; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(CHAR_MAX)) + lhs1 >> rhs7; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(CHAR_MAX)) + lhs1 >> rhs8; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(CHAR_MAX)) + lhs1 >> rhs9; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(CHAR_MAX)) + lhs1 >> rhs10; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(CHAR_MAX)) + lhs1 >> rhs11; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(CHAR_MAX)) + lhs1 >> rhs12; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(CHAR_MAX)) + lhs1 >> rhs13; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(CHAR_MAX)) + lhs1 >> rhs14; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs0 < PRECISION(CHAR_MAX)) + lhs2 >> rhs0; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + if (rhs1 < PRECISION(CHAR_MAX)) + lhs2 >> rhs1; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + if (rhs2 < PRECISION(CHAR_MAX)) + lhs2 >> rhs2; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + if (rhs3 < PRECISION(CHAR_MAX)) + lhs2 >> rhs3; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(CHAR_MAX)) + lhs2 >> rhs4; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(CHAR_MAX)) + lhs2 >> rhs5; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(CHAR_MAX)) + lhs2 >> rhs6; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(CHAR_MAX)) + lhs2 >> rhs7; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(CHAR_MAX)) + lhs2 >> rhs8; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(CHAR_MAX)) + lhs2 >> rhs9; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(CHAR_MAX)) + lhs2 >> rhs10; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(CHAR_MAX)) + lhs2 >> rhs11; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(CHAR_MAX)) + lhs2 >> rhs12; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(CHAR_MAX)) + lhs2 >> rhs13; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(CHAR_MAX)) + lhs2 >> rhs14; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs3 < PRECISION(USHRT_MAX)) + lhs3 >> rhs3; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs6 < PRECISION(USHRT_MAX)) + lhs3 >> rhs6; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(USHRT_MAX)) + lhs3 >> rhs7; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(USHRT_MAX)) + lhs3 >> rhs8; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(USHRT_MAX)) + lhs3 >> rhs9; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(USHRT_MAX)) + lhs3 >> rhs10; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(USHRT_MAX)) + lhs3 >> rhs11; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(USHRT_MAX)) + lhs3 >> rhs12; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(USHRT_MAX)) + lhs3 >> rhs13; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(USHRT_MAX)) + lhs3 >> rhs14; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs3 < PRECISION(SHRT_MAX)) + lhs4 >> rhs3; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(SHRT_MAX)) + lhs4 >> rhs4; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(SHRT_MAX)) + lhs4 >> rhs5; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(SHRT_MAX)) + lhs4 >> rhs6; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(SHRT_MAX)) + lhs4 >> rhs7; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(SHRT_MAX)) + lhs4 >> rhs8; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(SHRT_MAX)) + lhs4 >> rhs9; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(SHRT_MAX)) + lhs4 >> rhs10; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(SHRT_MAX)) + lhs4 >> rhs11; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(SHRT_MAX)) + lhs4 >> rhs12; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(SHRT_MAX)) + lhs4 >> rhs13; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(SHRT_MAX)) + lhs4 >> rhs14; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs3 < PRECISION(SHRT_MAX)) + lhs5 >> rhs3; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + if (rhs4 < PRECISION(SHRT_MAX)) + lhs5 >> rhs4; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + if (rhs5 < PRECISION(SHRT_MAX)) + lhs5 >> rhs5; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + if (rhs6 < PRECISION(SHRT_MAX)) + lhs5 >> rhs6; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(SHRT_MAX)) + lhs5 >> rhs7; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(SHRT_MAX)) + lhs5 >> rhs8; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(SHRT_MAX)) + lhs5 >> rhs9; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(SHRT_MAX)) + lhs5 >> rhs10; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(SHRT_MAX)) + lhs5 >> rhs11; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(SHRT_MAX)) + lhs5 >> rhs12; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(SHRT_MAX)) + lhs5 >> rhs13; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(SHRT_MAX)) + lhs5 >> rhs14; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(UINT_MAX)) + lhs6 >> rhs6; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs9 < PRECISION(UINT_MAX)) + lhs6 >> rhs9; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs12 < PRECISION(UINT_MAX)) + lhs6 >> rhs12; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(UINT_MAX)) + lhs6 >> rhs13; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(UINT_MAX)) + lhs6 >> rhs14; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(INT_MAX)) + lhs7 >> rhs6; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(INT_MAX)) + lhs7 >> rhs7; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(INT_MAX)) + lhs7 >> rhs8; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(INT_MAX)) + lhs7 >> rhs9; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(INT_MAX)) + lhs7 >> rhs10; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(INT_MAX)) + lhs7 >> rhs11; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(INT_MAX)) + lhs7 >> rhs12; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(INT_MAX)) + lhs7 >> rhs13; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(INT_MAX)) + lhs7 >> rhs14; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(INT_MAX)) + lhs8 >> rhs6; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(INT_MAX)) + lhs8 >> rhs7; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(INT_MAX)) + lhs8 >> rhs8; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(INT_MAX)) + lhs8 >> rhs9; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(INT_MAX)) + lhs8 >> rhs10; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(INT_MAX)) + lhs8 >> rhs11; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(INT_MAX)) + lhs8 >> rhs12; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(INT_MAX)) + lhs8 >> rhs13; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(INT_MAX)) + lhs8 >> rhs14; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(ULONG_MAX)) + lhs9 >> rhs6; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs9 < PRECISION(ULONG_MAX)) + lhs9 >> rhs9; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs12 < PRECISION(ULONG_MAX)) + lhs9 >> rhs12; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(ULONG_MAX)) + lhs9 >> rhs13; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(ULONG_MAX)) + lhs9 >> rhs14; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(LONG_MAX)) + lhs10 >> rhs6; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(LONG_MAX)) + lhs10 >> rhs7; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(LONG_MAX)) + lhs10 >> rhs8; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(LONG_MAX)) + lhs10 >> rhs9; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(LONG_MAX)) + lhs10 >> rhs10; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(LONG_MAX)) + lhs10 >> rhs11; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LONG_MAX)) + lhs10 >> rhs12; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LONG_MAX)) + lhs10 >> rhs13; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LONG_MAX)) + lhs10 >> rhs14; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs6 < PRECISION(LONG_MAX)) + lhs11 >> rhs6; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + if (rhs7 < PRECISION(LONG_MAX)) + lhs11 >> rhs7; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + if (rhs8 < PRECISION(LONG_MAX)) + lhs11 >> rhs8; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + if (rhs9 < PRECISION(LONG_MAX)) + lhs11 >> rhs9; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + if (rhs10 < PRECISION(LONG_MAX)) + lhs11 >> rhs10; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + if (rhs11 < PRECISION(LONG_MAX)) + lhs11 >> rhs11; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LONG_MAX)) + lhs11 >> rhs12; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LONG_MAX)) + lhs11 >> rhs13; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LONG_MAX)) + lhs11 >> rhs14; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs12 < PRECISION(ULLONG_MAX)) + lhs12 >> rhs12; // COMPLIANT: lhs12's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LLONG_MAX)) + lhs13 >> rhs12; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LLONG_MAX)) + lhs13 >> rhs13; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LLONG_MAX)) + lhs13 >> rhs14; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + if (rhs12 < PRECISION(LLONG_MAX)) + lhs14 >> rhs12; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + if (rhs13 < PRECISION(LLONG_MAX)) + lhs14 >> rhs13; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + if (rhs14 < PRECISION(LLONG_MAX)) + lhs14 >> rhs14; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + + /* ===== Right shift with guards, the shift expression is at `else` branch + * ===== */ + + if (rhs0 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs0; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs3; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs4; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs5; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs6; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs7; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs8; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs9; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs10; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs11; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs12; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs13; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(UCHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs0 >> rhs14; // COMPLIANT: lhs0's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs0 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs0; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + } + if (rhs1 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs1; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + } + if (rhs2 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs2; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs3; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs4; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs5; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs6; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs7; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs8; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs9; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs10; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs11; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs12; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs13; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs1 >> rhs14; // COMPLIANT: lhs1's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs0 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs0; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs0, but it's inside a PRECISION guard + } + if (rhs1 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs1; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs1, but it's inside a PRECISION guard + } + if (rhs2 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs2; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs2, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs3; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs4; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs5; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs6; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs7; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs8; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs9; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs10; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs11; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs12; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs13; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(CHAR_MAX)) { + ; /* Handle Error */ + } else { + lhs2 >> rhs14; // COMPLIANT: lhs2's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs3; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs6; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs7; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs8; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs9; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs10; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs11; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs12; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs13; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(USHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs3 >> rhs14; // COMPLIANT: lhs3's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs3; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs4; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs5; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs6; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs7; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs8; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs9; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs10; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs11; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs12; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs13; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs4 >> rhs14; // COMPLIANT: lhs4's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs3 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs3; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs3, but it's inside a PRECISION guard + } + if (rhs4 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs4; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs4, but it's inside a PRECISION guard + } + if (rhs5 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs5; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs5, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs6; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs7; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs8; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs9; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs10; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs11; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs12; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs13; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(SHRT_MAX)) { + ; /* Handle Error */ + } else { + lhs5 >> rhs14; // COMPLIANT: lhs5's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 >> rhs6; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 >> rhs9; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 >> rhs12; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 >> rhs13; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(UINT_MAX)) { + ; /* Handle Error */ + } else { + lhs6 >> rhs14; // COMPLIANT: lhs6's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs6; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs7; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs8; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs9; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs10; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs11; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs12; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs13; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs7 >> rhs14; // COMPLIANT: lhs7's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs6; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs7; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs8; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs9; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs10; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs11; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs12; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs13; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(INT_MAX)) { + ; /* Handle Error */ + } else { + lhs8 >> rhs14; // COMPLIANT: lhs8's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 >> rhs6; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 >> rhs9; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 >> rhs12; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 >> rhs13; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(ULONG_MAX)) { + ; /* Handle Error */ + } else { + lhs9 >> rhs14; // COMPLIANT: lhs9's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs6; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs7; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs8; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs9; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs10; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs11; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs12; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs13; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs10 >> rhs14; // COMPLIANT: lhs10's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs6 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs6; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs6, but it's inside a PRECISION guard + } + if (rhs7 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs7; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs7, but it's inside a PRECISION guard + } + if (rhs8 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs8; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs8, but it's inside a PRECISION guard + } + if (rhs9 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs9; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs9, but it's inside a PRECISION guard + } + if (rhs10 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs10; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs10, but it's inside a PRECISION guard + } + if (rhs11 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs11; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs11, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs12; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs13; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LONG_MAX)) { + ; /* Handle Error */ + } else { + lhs11 >> rhs14; // COMPLIANT: lhs11's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(ULLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs12 >> rhs12; // COMPLIANT: lhs12's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs13 >> rhs12; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs13 >> rhs13; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs13 >> rhs14; // COMPLIANT: lhs13's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + if (rhs12 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs14 >> rhs12; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs12, but it's inside a PRECISION guard + } + if (rhs13 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs14 >> rhs13; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs13, but it's inside a PRECISION guard + } + if (rhs14 >= PRECISION(LLONG_MAX)) { + ; /* Handle Error */ + } else { + lhs14 >> rhs14; // COMPLIANT: lhs14's precision is not strictly greater than + // rhs14, but it's inside a PRECISION guard + } + + /* Negative shifts */ + + lhs0 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs1 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs2 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs3 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs4 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs5 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs6 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs7 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs8 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs9 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs10 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs11 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs12 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs13 << -1; // NON_COMPLIANT: shifting by a negative operand + lhs14 << -1; // NON_COMPLIANT: shifting by a negative operand + + return 0; +} diff --git a/c/cert/test/rules/INT35-C/UseCorrectIntegerPrecisions.expected b/c/cert/test/rules/INT35-C/UseCorrectIntegerPrecisions.expected new file mode 100644 index 0000000000..e43dc19077 --- /dev/null +++ b/c/cert/test/rules/INT35-C/UseCorrectIntegerPrecisions.expected @@ -0,0 +1,2 @@ +| test.c:11:12:11:31 | sizeof(unsigned int) | sizeof operator used to determine the precision of an integer type. | +| test.c:27:25:27:42 | sizeof(signed int) | sizeof operator used to determine the precision of an integer type. | diff --git a/c/cert/test/rules/INT35-C/UseCorrectIntegerPrecisions.qlref b/c/cert/test/rules/INT35-C/UseCorrectIntegerPrecisions.qlref new file mode 100644 index 0000000000..c408baf78d --- /dev/null +++ b/c/cert/test/rules/INT35-C/UseCorrectIntegerPrecisions.qlref @@ -0,0 +1 @@ +rules/INT35-C/UseCorrectIntegerPrecisions.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT35-C/test.c b/c/cert/test/rules/INT35-C/test.c new file mode 100644 index 0000000000..72bca5f8d8 --- /dev/null +++ b/c/cert/test/rules/INT35-C/test.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +size_t popcount(uintmax_t num); + +#define PRECISION(umax_value) popcount(umax_value) + +void test_incorrect_precision_check(int e) { + if (e >= sizeof(unsigned int) * CHAR_BIT) { // NON_COMPLIANT + // handle error + } else { + 1 << e; + } +} + +void test_correct_precision_check(int e) { + if (e >= PRECISION(UINT_MAX)) { // COMPLIANT + /* Handle error */ + } else { + 1 << e; + } +} + +void test_incorrect_precision_check_cast(float f) { + if (log2f(fabsf(f)) > sizeof(signed int) * CHAR_BIT) { // NON_COMPLIANT + // handle error + } else { + (signed int)f; + } +} + +void test_correct_precision_check_cast(float f) { + if (log2f(fabsf(f)) > PRECISION(INT_MAX)) { // COMPLIANT + /* Handle error */ + } else { + (signed int)f; + } +} \ No newline at end of file diff --git a/c/cert/test/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.expected b/c/cert/test/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.expected new file mode 100644 index 0000000000..67a3935c0f --- /dev/null +++ b/c/cert/test/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.expected @@ -0,0 +1,9 @@ +| test.c:17:23:17:32 | (int *)... | Integer expression 28036591 is implicitly cast to a pointer type. | +| test.c:20:7:20:16 | (int *)... | Integer expression 28036591 is implicitly cast to a pointer type. | +| test.c:22:7:22:14 | (int *)... | Integer expression integer1 is implicitly cast to a pointer type. | +| test.c:24:7:25:7 | (int *)... | Integer expression ... + ... is implicitly cast to a pointer type. | +| test.c:27:7:27:23 | (int *)... | Integer expression 28036591 is cast to a pointer type. | +| test.c:29:7:29:21 | (int *)... | Integer expression integer1 is cast to a pointer type. | +| test.c:34:26:34:34 | (int)... | Pointer expression & ... is implicitly cast to an integer type. | +| test.c:36:22:36:30 | (int)... | Pointer expression & ... is implicitly cast to an integer type. | +| test.c:39:7:39:20 | (int)... | Pointer expression & ... is cast to an integer type. | diff --git a/c/cert/test/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.qlref b/c/cert/test/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.qlref new file mode 100644 index 0000000000..70ae157f74 --- /dev/null +++ b/c/cert/test/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.qlref @@ -0,0 +1 @@ +rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql \ No newline at end of file diff --git a/c/cert/test/rules/INT36-C/test.c b/c/cert/test/rules/INT36-C/test.c new file mode 100644 index 0000000000..e289f34fd4 --- /dev/null +++ b/c/cert/test/rules/INT36-C/test.c @@ -0,0 +1,60 @@ +#include + +int main() { + /* Compliant declarations and assignments */ + int integer1 = 1; // COMPLIANT: declaring integer as integer + int integer2 = integer1; // COMPLIANT: declaring integer as integer + integer1 = + integer2; // COMPLIANT: assigning integer rvalue to integer variable + int *int_pointer1 = + &integer1; // COMPLIANT: declaring pointer variable as an address + int *int_pointer2 = int_pointer1; // COMPLIANT: declaring pointer variable as + // an address rvalue + int_pointer1 = + int_pointer2; // COMPLIANT: assigning pointer rvalue to a pointer variable + + /* Integer to pointer */ + int *int_pointer3 = 0x01abcdef; // NON_COMPLIANT: declaring pointer variable + // with raw hex integer + int_pointer3 = + 0x01abcdef; // NON_COMPLIANT: assigning raw hex to pointer variable + int *int_pointer4 = + integer1; // NON_COMPLIANT: declaring pointer variable with integer value + int_pointer4 = + integer1 + + 1; // NON_COMPLIANT: assigning integer rvalue to pointer variable + int *integer_address5 = + (int *)0x01abcdef; // NON_COMPLIANT: casting raw hex to pointer type + int *integer_address6 = + (int *)integer1; // NON_COMPLIANT: casting integer value to pointer type + + /* Pointer to integer */ + int *integer_address7 = + &integer1; // COMPLIANT: declaring pointer variable as an address + int integer_address8 = &integer1; // NON_COMPLIANT: declaring integer + // variable with pointer type value + integer_address8 = &integer1; // NON_COMPLIANT: assigning pointer type rvalue + // to integer variable + int integer_address = + (int)&integer1; // NON_COMPLIANT: casting pointer value to integer type + + /* Exceptions that are COMPLIANT */ + int *null_pointer1 = + 0; // COMPLIANT: integer 0 converted to pointer becomes null pointer + int *null_pointer2 = (int *)0; // COMPLIANT: integer 0 is converted to pointer + // becomes null pointer + null_pointer2 = + 0; // COMPLIANT: integer 0 converted to pointer becomes null pointer + + void *void_pointer = &integer1; + intptr_t void_pointer_integer1 = + void_pointer; // COMPLIANT: void pointer can be converted to intptr_t + uintptr_t void_pointer_integer2 = + void_pointer; // COMPLIANT: void pointer can be converted to uintptr_t + void *void_pointer1 = (void *) + void_pointer_integer1; // COMPLIANT: intptr_t can be converted to void* + void *void_pointer2 = (void *) + void_pointer_integer2; // COMPLIANT: uintptr_t can be converted to void* + + return 0; +} \ No newline at end of file diff --git a/c/cert/test/rules/MEM30-C/DoNotAccessFreedMemory.expected b/c/cert/test/rules/MEM30-C/DoNotAccessFreedMemory.expected new file mode 100644 index 0000000000..49a91e7935 --- /dev/null +++ b/c/cert/test/rules/MEM30-C/DoNotAccessFreedMemory.expected @@ -0,0 +1,5 @@ +| test.c:11:47:11:47 | p | Pointer 'p' accessed but may have been previously freed $@. | test.c:12:5:12:8 | call to free | here | +| test.c:25:10:25:12 | buf | Pointer 'buf' accessed but may have been previously freed $@. | test.c:24:3:24:6 | call to free | here | +| test.c:32:15:32:17 | buf | Pointer 'buf' accessed but may have been previously freed $@. | test.c:31:3:31:6 | call to free | here | +| test.c:33:9:33:11 | buf | Pointer 'buf' accessed but may have been previously freed $@. | test.c:31:3:31:6 | call to free | here | +| test.c:34:16:34:18 | buf | Pointer 'buf' accessed but may have been previously freed $@. | test.c:31:3:31:6 | call to free | here | diff --git a/c/cert/test/rules/MEM30-C/DoNotAccessFreedMemory.qlref b/c/cert/test/rules/MEM30-C/DoNotAccessFreedMemory.qlref new file mode 100644 index 0000000000..514afd57d2 --- /dev/null +++ b/c/cert/test/rules/MEM30-C/DoNotAccessFreedMemory.qlref @@ -0,0 +1 @@ +rules/MEM30-C/DoNotAccessFreedMemory.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM30-C/test.c b/c/cert/test/rules/MEM30-C/test.c new file mode 100644 index 0000000000..a3c7532072 --- /dev/null +++ b/c/cert/test/rules/MEM30-C/test.c @@ -0,0 +1,38 @@ +#include +#include + +struct node { + struct node *next; +}; + +void test_freed_loop_var(struct node *list1, struct node *list2) { + struct node *tmp; + + for (struct node *p = list1; p != NULL; p = p->next) { // NON_COMPLIANT + free(p); + } + + for (struct node *p = list2; p != NULL; p = tmp) { // COMPLIANT + tmp = p->next; + free(p); + } +} + +void test_freed_arg(char *input) { + char *buf = (char *)malloc(strlen(input) + 1); + strcpy(buf, input); // COMPLIANT + free(buf); + strcpy(buf, input); // NON_COMPLIANT +} + +void test_freed_access_no_deref(char *input) { + char *buf = (char *)malloc(strlen(input) + 1); + strcpy(buf, input); // COMPLIANT + free(buf); + char *tmp = buf; // NON_COMPLIANT + tmp = buf + 1; // NON_COMPLIANT + char *tmp2 = buf; // NON_COMPLIANT + buf = NULL; // COMPLIANT + (char *)buf; // COMPLIANT + tmp2 = buf + 1; // COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.testref b/c/cert/test/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.testref new file mode 100644 index 0000000000..c3215c5533 --- /dev/null +++ b/c/cert/test/rules/MEM31-C/FreeMemoryWhenNoLongerNeededCert.testref @@ -0,0 +1 @@ +c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.expected b/c/cert/test/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.expected new file mode 100644 index 0000000000..0df24e1dcc --- /dev/null +++ b/c/cert/test/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.expected @@ -0,0 +1,7 @@ +| test.c:15:13:15:14 | v1 | $@ contains a flexible array member but is not dynamically allocated. | test.c:15:13:15:14 | v1 | v1 | +| test.c:17:8:17:13 | call to malloc | $@ allocated with insufficient memory for its flexible array member. | test.c:16:14:16:15 | v2 | v2 | +| test.c:20:7:20:12 | call to malloc | $@ allocated with insufficient memory for its flexible array member. | test.c:19:14:19:15 | v3 | v3 | +| test.c:22:19:22:24 | call to malloc | $@ allocated with insufficient memory for its flexible array member. | test.c:22:14:22:15 | v4 | v4 | +| test.c:31:30:31:31 | p1 | $@ contains a flexible array member but is not dynamically allocated. | test.c:31:30:31:31 | p1 | p1 | +| test.c:40:13:40:14 | v1 | $@ contains a flexible array member but is not dynamically allocated. | test.c:40:13:40:14 | v1 | v1 | +| test.c:48:13:48:14 | v1 | $@ contains a flexible array member but is not dynamically allocated. | test.c:48:13:48:14 | v1 | v1 | diff --git a/c/cert/test/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.qlref b/c/cert/test/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.qlref new file mode 100644 index 0000000000..963e5e0175 --- /dev/null +++ b/c/cert/test/rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.qlref @@ -0,0 +1 @@ +rules/MEM33-C/AllocStructsWithAFlexibleArrayMemberDynamically.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.expected b/c/cert/test/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.expected new file mode 100644 index 0000000000..4b9c0bbd65 --- /dev/null +++ b/c/cert/test/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.expected @@ -0,0 +1,2 @@ +| test.c:48:18:48:20 | * ... | Struct containing a flexible array member copied by assignment. | +| test.c:49:9:49:11 | * ... | Struct containing a flexible array member copied by assignment. | diff --git a/c/cert/test/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.qlref b/c/cert/test/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.qlref new file mode 100644 index 0000000000..37493b023f --- /dev/null +++ b/c/cert/test/rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.qlref @@ -0,0 +1 @@ +rules/MEM33-C/CopyStructsWithAFlexibleArrayMemberDynamically.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM33-C/test.c b/c/cert/test/rules/MEM33-C/test.c new file mode 100644 index 0000000000..364e7b761e --- /dev/null +++ b/c/cert/test/rules/MEM33-C/test.c @@ -0,0 +1,54 @@ +#include +#include + +struct s1 { + int num; + char b[]; +}; + +struct s2 { + int a; + char b[1]; +}; + +void test_alloc(void) { + struct s1 v1; // NON_COMPLIANT + struct s1 *v2; // COMPLIANT + v2 = malloc(sizeof(struct s1)); // NON_COMPLIANT - size does not include space + // for the flexible array + struct s1 *v3 = + malloc(sizeof(struct s1)); // NON_COMPLIANT - size does not include space + // for the flexible array + struct s1 *v4 = malloc( + sizeof(struct s1) - + 1); // NON_COMPLIANT - size does not include space for the flexible array + struct s1 *v5 = malloc(sizeof(struct s1) + 1); // COMPLIANT + struct s2 v6; // COMPLIANT - no flex array + struct s2 *v7 = malloc(sizeof(struct s1)); // COMPLIANT - no flex array +} + +// calls to this function are never compliant +void test_fa_param(struct s1 p1) {} // NON_COMPLIANT + +// calls to this function are always compliant +void test_pfa_param(struct s1 *p1) {} // COMPLIANT + +// calls to this function are always compliant +void test_s_param(struct s2 p1) {} // COMPLIANT + +void test_fa_params_call(void) { + struct s1 v1; // NON_COMPLIANT + struct s1 *v2 = malloc(sizeof(struct s1) + 1); + test_fa_param(v1); // NON_COMPLIANT + test_pfa_param(&v1); // COMPLIANT + test_pfa_param(v2); // COMPLIANT +} + +void test_copy(struct s1 *p1, struct s1 *p2) { + struct s1 v1 = *p2; // NON_COMPLIANT + *p1 = *p2; // NON_COMPLIANT + memcpy(p1, p2, + sizeof(struct s1)); // NON_COMPLIANT - not copying size of array + memcpy(p1, p2, sizeof(struct s1) + 1); // COMPLIANT + memcpy(p1, p2, sizeof(struct s1) + p2->num); // COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.testref b/c/cert/test/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.testref new file mode 100644 index 0000000000..edf7c5cc3b --- /dev/null +++ b/c/cert/test/rules/MEM34-C/OnlyFreeMemoryAllocatedDynamicallyCert.testref @@ -0,0 +1 @@ +c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM35-C/InsufficientMemoryAllocatedForObject.expected b/c/cert/test/rules/MEM35-C/InsufficientMemoryAllocatedForObject.expected new file mode 100644 index 0000000000..86bdeedf5f --- /dev/null +++ b/c/cert/test/rules/MEM35-C/InsufficientMemoryAllocatedForObject.expected @@ -0,0 +1,11 @@ +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (InsufficientMemoryAllocatedForObject.ql:90,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (InsufficientMemoryAllocatedForObject.ql:148,5-18) +| test.c:12:19:12:24 | call to malloc | Allocation size (32 bytes) is not a multiple of the size of 'S1' (36 bytes). | test.c:12:26:12:32 | 32 | | +| test.c:15:19:15:24 | call to malloc | Allocation size calculated from the size of a different type ($@). | test.c:15:26:15:35 | sizeof() | sizeof(S1 *) | +| test.c:20:19:20:24 | call to malloc | Allocation size (128 bytes) is not a multiple of the size of 'S1' (36 bytes). | test.c:20:26:20:36 | ... * ... | | +| test.c:21:19:21:24 | call to malloc | Allocation size (128 bytes) is not a multiple of the size of 'S1' (36 bytes). | test.c:21:26:21:36 | ... * ... | | +| test.c:25:14:25:19 | call to malloc | Allocation size calculated from the size of a different type ($@). | test.c:25:27:25:37 | sizeof(int) | sizeof(int) | +| test.c:25:14:25:19 | call to malloc | Allocation size derived from potentially overflowing or wrapping $@. | test.c:25:21:25:37 | ... * ... | integer operation | +| test.c:31:14:31:19 | call to malloc | Allocation size derived from potentially overflowing or wrapping $@. | test.c:31:21:31:38 | ... * ... | integer operation | +| test.c:32:14:32:19 | call to malloc | Allocation size derived from potentially overflowing or wrapping $@. | test.c:29:17:29:34 | ... * ... | integer operation | +| test.c:40:14:40:19 | call to malloc | Allocation size derived from potentially overflowing or wrapping $@. | test.c:29:17:29:34 | ... * ... | integer operation | diff --git a/c/cert/test/rules/MEM35-C/InsufficientMemoryAllocatedForObject.qlref b/c/cert/test/rules/MEM35-C/InsufficientMemoryAllocatedForObject.qlref new file mode 100644 index 0000000000..7da5a9c268 --- /dev/null +++ b/c/cert/test/rules/MEM35-C/InsufficientMemoryAllocatedForObject.qlref @@ -0,0 +1 @@ +rules/MEM35-C/InsufficientMemoryAllocatedForObject.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM35-C/test.c b/c/cert/test/rules/MEM35-C/test.c new file mode 100644 index 0000000000..938d3f1076 --- /dev/null +++ b/c/cert/test/rules/MEM35-C/test.c @@ -0,0 +1,41 @@ +#include +#include + +#define S1_SIZE 32 // incorrect size for struct S1 + +struct S1 { + char f1[S1_SIZE]; + int f2; +}; + +void sizecheck_test(void) { + struct S1 *v1 = malloc(S1_SIZE); // NON_COMPLIANT + struct S1 *v2 = malloc(sizeof(struct S1)); // COMPLIANT + struct S1 *v3 = malloc(sizeof(*v2)); // COMPLIANT + struct S1 *v4 = malloc(sizeof(v4)); // NON_COMPLIANT + char *v5 = malloc(10); // COMPLIANT +} + +void sizecheck2_test(size_t len) { + struct S1 *v1 = malloc(S1_SIZE * 4); // NON_COMPLIANT + struct S1 *v2 = malloc(S1_SIZE * 4); // NON_COMPLIANT + struct S1 *v3 = malloc( + S1_SIZE * 9); // COMPLIANT - erroneous logic, but the size product is an + // LCM of S1_SIZE and sizeof(S1) and thus a valid multiple + long *v4 = malloc(len * sizeof(int)); // NON_COMPLIANT - wrong sizeof type +} + +void unsafe_int_test(size_t len) { + size_t size = len * sizeof(long); + long *v1 = malloc(len); // COMPLIANT - even could indicate a logic error + long *v2 = malloc(len * sizeof(long)); // NON_COMPLIANT - unbounded int + long *v3 = malloc(size); // NON_COMPLIANT - unbounded int + + if (len > SIZE_MAX / sizeof(*v3)) { + // overflow/wrapping check + return; + } + + long *v4 = malloc(len * sizeof(long)); // COMPLIANT - overflow checked + long *v5 = malloc(size); // NON_COMPLIANT - `size` not checked +} diff --git a/c/cert/test/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.expected b/c/cert/test/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.expected new file mode 100644 index 0000000000..587ae786d1 --- /dev/null +++ b/c/cert/test/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.expected @@ -0,0 +1,23 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyAlignmentOfMemoryWithRealloc.ql:31,36-44) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyAlignmentOfMemoryWithRealloc.ql:45,47-55) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyAlignmentOfMemoryWithRealloc.ql:46,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyAlignmentOfMemoryWithRealloc.ql:50,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotModifyAlignmentOfMemoryWithRealloc.ql:55,36-44) +edges +| test.c:5:10:5:22 | call to aligned_alloc | test.c:15:8:15:28 | call to aligned_alloc_wrapper | provenance | | +| test.c:8:29:8:31 | ptr | test.c:8:64:8:66 | ptr | provenance | | +| test.c:15:8:15:28 | call to aligned_alloc_wrapper | test.c:16:24:16:25 | v1 | provenance | | +| test.c:16:24:16:25 | v1 | test.c:8:29:8:31 | ptr | provenance | | +| test.c:22:8:22:20 | call to aligned_alloc | test.c:23:16:23:17 | v3 | provenance | | +nodes +| test.c:5:10:5:22 | call to aligned_alloc | semmle.label | call to aligned_alloc | +| test.c:8:29:8:31 | ptr | semmle.label | ptr | +| test.c:8:64:8:66 | ptr | semmle.label | ptr | +| test.c:15:8:15:28 | call to aligned_alloc_wrapper | semmle.label | call to aligned_alloc_wrapper | +| test.c:16:24:16:25 | v1 | semmle.label | v1 | +| test.c:22:8:22:20 | call to aligned_alloc | semmle.label | call to aligned_alloc | +| test.c:23:16:23:17 | v3 | semmle.label | v3 | +subpaths +#select +| test.c:8:64:8:66 | ptr | test.c:5:10:5:22 | call to aligned_alloc | test.c:8:64:8:66 | ptr | Memory allocated with $@ but reallocated with realloc. | test.c:5:10:5:22 | call to aligned_alloc | aligned_alloc | +| test.c:23:16:23:17 | v3 | test.c:22:8:22:20 | call to aligned_alloc | test.c:23:16:23:17 | v3 | Memory allocated with $@ but reallocated with realloc. | test.c:22:8:22:20 | call to aligned_alloc | aligned_alloc | diff --git a/c/cert/test/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.qlref b/c/cert/test/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.qlref new file mode 100644 index 0000000000..60d530bf5f --- /dev/null +++ b/c/cert/test/rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.qlref @@ -0,0 +1 @@ +rules/MEM36-C/DoNotModifyAlignmentOfMemoryWithRealloc.ql \ No newline at end of file diff --git a/c/cert/test/rules/MEM36-C/test.c b/c/cert/test/rules/MEM36-C/test.c new file mode 100644 index 0000000000..1f2d159e09 --- /dev/null +++ b/c/cert/test/rules/MEM36-C/test.c @@ -0,0 +1,24 @@ +#include +#include + +void *aligned_alloc_wrapper(size_t alignment, size_t size) { + return aligned_alloc(alignment, size); +} + +void *realloc_wrapper(void *ptr, size_t size) { return realloc(ptr, size); } + +void test_aligned_alloc_to_realloc(void) { + void *v1; + void *v2; + void *v3; + + v1 = aligned_alloc_wrapper(32, 32); + v1 = realloc_wrapper(v1, 64); // NON_COMPLIANT - result reported in wrapper + v1 = realloc(v1, 64); // COMPLIANT + + v2 = aligned_alloc(16, 16); + v2 = realloc(v2, 32); // COMPLIANT - alignment unchanged + + v3 = aligned_alloc(32, 16); + v3 = realloc(v3, 32); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.testref b/c/cert/test/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.testref index 31cba60b74..726f27535d 100644 --- a/c/cert/test/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.testref +++ b/c/cert/test/rules/MSC30-C/RandUsedForGeneratingPseudorandomNumbers.testref @@ -1 +1 @@ -cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql \ No newline at end of file +c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql \ No newline at end of file diff --git a/c/cert/test/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.expected b/c/cert/test/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.expected new file mode 100644 index 0000000000..7ebeb7a8c1 --- /dev/null +++ b/c/cert/test/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.expected @@ -0,0 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotPassInvalidDataToTheAsctimeFunction.ql:38,38-46) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotPassInvalidDataToTheAsctimeFunction.ql:39,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotPassInvalidDataToTheAsctimeFunction.ql:46,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotPassInvalidDataToTheAsctimeFunction.ql:49,27-35) +| test.c:6:24:6:30 | time_tm | The function `asctime` and `asctime_r` should be discouraged. Unsanitized input can overflow the output buffer. | diff --git a/c/cert/test/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.qlref b/c/cert/test/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.qlref new file mode 100644 index 0000000000..86b5c9f0c3 --- /dev/null +++ b/c/cert/test/rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.qlref @@ -0,0 +1 @@ +rules/MSC33-C/DoNotPassInvalidDataToTheAsctimeFunction.ql \ No newline at end of file diff --git a/c/cert/test/rules/MSC33-C/test.c b/c/cert/test/rules/MSC33-C/test.c new file mode 100644 index 0000000000..09324c48f3 --- /dev/null +++ b/c/cert/test/rules/MSC33-C/test.c @@ -0,0 +1,37 @@ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include + +void f1a(struct tm *time_tm) { + char *time = asctime(time_tm); // NON_COMPLIANT + /* ... */ +} + +void f1b() { + time_t ltime; + /* Get the time in seconds */ + time(<ime); + /* Convert it to the structure tm */ + struct tm *time_tm = localtime(<ime); + char *time = asctime(time_tm); // COMPLIANT +} + +enum { maxsize = 26 }; + +void f2(struct tm *time) { + char s[maxsize]; + /* Current time representation for locale */ + const char *format = "%c"; + + size_t size = strftime(s, maxsize, format, time); +} + +#ifdef __STDC_LIB_EXT1__ +void f3(struct tm *time_tm) { + char buffer[maxsize]; + + if (asctime_s(buffer, maxsize, &time_tm)) { + /* Handle error */ + } +} +#endif \ No newline at end of file diff --git a/c/cert/test/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.expected b/c/cert/test/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.expected new file mode 100644 index 0000000000..4e14eb2873 --- /dev/null +++ b/c/cert/test/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.expected @@ -0,0 +1,13 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:43,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:44,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:49,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:52,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:73,10-18) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:74,29-37) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql:75,29-37) +| test.c:23:32:23:33 | ap | The value of ap is indeterminate after the $@. | test.c:17:7:17:19 | call to contains_zero | call to contains_zero | +| test.c:26:10:26:11 | ap | The value of ap is indeterminate after the $@. | test.c:17:7:17:19 | call to contains_zero | call to contains_zero | +| test.c:39:12:39:13 | ap | The value of ap is indeterminate after the $@. | test.c:35:7:35:19 | call to contains_zero | call to contains_zero | +| test.c:48:10:48:11 | ap | The value of ap is indeterminate after the $@. | test.c:35:7:35:19 | call to contains_zero | call to contains_zero | +| test.c:65:34:65:35 | ap | The value of ap is indeterminate after the $@. | test.c:58:7:58:19 | call to contains_zero | call to contains_zero | +| test.c:71:10:71:11 | ap | The value of ap is indeterminate after the $@. | test.c:58:7:58:19 | call to contains_zero | call to contains_zero | diff --git a/c/cert/test/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.qlref b/c/cert/test/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.qlref new file mode 100644 index 0000000000..0f33f99195 --- /dev/null +++ b/c/cert/test/rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.qlref @@ -0,0 +1 @@ +rules/MSC39-C/DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue.ql \ No newline at end of file diff --git a/c/cert/test/rules/MSC39-C/test.c b/c/cert/test/rules/MSC39-C/test.c new file mode 100644 index 0000000000..5e88092534 --- /dev/null +++ b/c/cert/test/rules/MSC39-C/test.c @@ -0,0 +1,105 @@ +#include +#include + +int contains_zero(size_t count, va_list ap) { + for (size_t i = 1; i < count; ++i) { + if (va_arg(ap, double) == 0.0) { // COMPLIANT + return 1; + } + } + return 0; +} + +int f1a(size_t count, ...) { + va_list ap; + va_start(ap, count); + + if (contains_zero(count, ap)) { + va_start(ap, count); + return 1; + } + + for (size_t i = 0; i < count; ++i) { + printf("%f ", 1.0 / va_arg(ap, double)); // NON_COMPLIANT + } + + va_end(ap); // NON_COMPLIANT + return 0; +} + +int f1b(size_t count, ...) { + int status; + va_list ap; + va_start(ap, count); + + if (contains_zero(count, ap)) { + printf("0 in arguments!\n"); + status = 1; + } else { + va_end(ap); // NON_COMPLIANT + va_start(ap, count); + for (size_t i = 0; i < count; i++) { + printf("%f ", 1.0 / va_arg(ap, double)); // COMPLIANT + } + printf("\n"); + status = 0; + } + + va_end(ap); // NON_COMPLIANT + return status; +} + +int f1c(size_t count, ...) { + int status; + va_list ap; + va_list ap1; + va_start(ap, count); + + if (contains_zero(count, ap)) { + printf("0 in arguments!\n"); + status = 1; + } else { + va_end(ap1); // COMPLIANT + va_start(ap1, count); + for (size_t i = 0; i < count; i++) { + printf("%f ", 1.0 / va_arg(ap, double)); // NON_COMPLIANT + } + printf("\n"); + status = 0; + } + + va_end(ap); // NON_COMPLIANT + return status; +} + +int contains_zero_ok(size_t count, va_list *ap) { + va_list ap1; + va_copy(ap1, *ap); + for (size_t i = 1; i < count; ++i) { + if (va_arg(ap1, double) == 0.0) { // COMPLIANT + return 1; + } + } + va_end(ap1); // COMPLIANT + return 0; +} + +int print_reciprocals_ok(size_t count, ...) { + int status; + va_list ap; + va_start(ap, count); + + if (contains_zero_ok(count, &ap)) { + printf("0 in arguments!\n"); + status = 1; + } else { + for (size_t i = 0; i < count; i++) { + printf("%f ", 1.0 / va_arg(ap, double)); // COMPLIANT + } + printf("\n"); + status = 0; + } + + va_end(ap); // COMPLIANT + return status; +} diff --git a/c/cert/test/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.expected b/c/cert/test/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.expected new file mode 100644 index 0000000000..f258d4adef --- /dev/null +++ b/c/cert/test/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.expected @@ -0,0 +1,6 @@ +| test.c:6:14:6:14 | i | Static local variable $@ declared in the extern inlined function $@. | test.c:6:14:6:14 | i | i | test.c:5:20:5:24 | test1 | test1 | +| test.c:7:3:7:4 | g1 | Identifier $@ with internal linkage referenced in the extern inlined function $@. | test.c:1:12:1:13 | g1 | g1 | test.c:5:20:5:24 | test1 | test1 | +| test.c:9:3:9:4 | g3 | Identifier $@ with internal linkage referenced in the extern inlined function $@. | test.c:3:11:3:12 | g3 | g3 | test.c:5:20:5:24 | test1 | test1 | +| test.c:27:14:27:14 | i | Static local variable $@ declared in the extern inlined function $@. | test.c:27:14:27:14 | i | i | test.c:26:13:26:17 | test4 | test4 | +| test.c:28:3:28:4 | g1 | Identifier $@ with internal linkage referenced in the extern inlined function $@. | test.c:1:12:1:13 | g1 | g1 | test.c:26:13:26:17 | test4 | test4 | +| test.c:30:3:30:4 | g3 | Identifier $@ with internal linkage referenced in the extern inlined function $@. | test.c:3:11:3:12 | g3 | g3 | test.c:26:13:26:17 | test4 | test4 | diff --git a/c/cert/test/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.qlref b/c/cert/test/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.qlref new file mode 100644 index 0000000000..f14d4270cc --- /dev/null +++ b/c/cert/test/rules/MSC40-C/DoNotViolateInLineLinkageConstraints.qlref @@ -0,0 +1 @@ +rules/MSC40-C/DoNotViolateInLineLinkageConstraints.ql \ No newline at end of file diff --git a/c/cert/test/rules/MSC40-C/test.c b/c/cert/test/rules/MSC40-C/test.c new file mode 100644 index 0000000000..d892935d41 --- /dev/null +++ b/c/cert/test/rules/MSC40-C/test.c @@ -0,0 +1,31 @@ +static int g1 = 0; +extern int g2 = 1; +const int g3 = 1; // defaults to internal linkage + +extern inline void test1() { + static int i = 0; // NON_COMPLIANT + g1++; // NON_COMPLIANT + g2++; // COMPLIANT + g3; // NON_COMPLIANT +} + +extern void test2() { + static int i = 0; // COMPLIANT + g1++; // COMPLIANT + g2++; // COMPLIANT + g3; // COMPLIANT +} + +void test3() { + static int i = 0; // COMPLIANT + g1++; // COMPLIANT + g2++; // COMPLIANT + g3; // COMPLIANT +} + +inline void test4() { + static int i = 0; // NON_COMPLIANT + g1++; // NON_COMPLIANT + g2++; // COMPLIANT + g3; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.expected b/c/cert/test/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.expected new file mode 100644 index 0000000000..769d0c81c9 --- /dev/null +++ b/c/cert/test/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.expected @@ -0,0 +1,10 @@ +| test.c:9:10:9:12 | ... ++ | Argument i++ to unsafe macro 'unsafe' is expanded to 'i++' multiple times and includes the use of the ++ operator as a side-effect. | +| test.c:9:10:9:12 | ... ++ | Argument i++ to unsafe macro 'unsafe' is expanded to 'i++' multiple times and includes the use of the ++ operator as a side-effect. | +| test.c:11:10:11:12 | ... -- | Argument i-- to unsafe macro 'unsafe' is expanded to 'i--' multiple times and includes the use of the -- operator as a side-effect. | +| test.c:11:10:11:12 | ... -- | Argument i-- to unsafe macro 'unsafe' is expanded to 'i--' multiple times and includes the use of the -- operator as a side-effect. | +| test.c:26:10:26:15 | call to addOne | Argument addOne(10) to unsafe macro 'unsafe' is expanded to 'addOne(10)' multiple times and includes a call to the function 'addOne' as a side-effect. | +| test.c:26:10:26:15 | call to addOne | Argument addOne(10) to unsafe macro 'unsafe' is expanded to 'addOne(10)' multiple times and includes a call to the function 'addOne' as a side-effect. | +| test.c:27:10:27:17 | call to external | Argument external() to unsafe macro 'unsafe' is expanded to 'external()' multiple times and includes a call to the function 'external' as a side-effect. | +| test.c:27:10:27:17 | call to external | Argument external() to unsafe macro 'unsafe' is expanded to 'external()' multiple times and includes a call to the function 'external' as a side-effect. | +| test.c:28:10:28:15 | call to writeX | Argument writeX(10) to unsafe macro 'unsafe' is expanded to 'writeX(10)' multiple times and includes a call to the function 'writeX' as a side-effect. | +| test.c:28:10:28:15 | call to writeX | Argument writeX(10) to unsafe macro 'unsafe' is expanded to 'writeX(10)' multiple times and includes a call to the function 'writeX' as a side-effect. | diff --git a/c/cert/test/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.qlref b/c/cert/test/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.qlref new file mode 100644 index 0000000000..25a8d53fae --- /dev/null +++ b/c/cert/test/rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.qlref @@ -0,0 +1 @@ +rules/PRE31-C/SideEffectsInArgumentsToUnsafeMacros.ql \ No newline at end of file diff --git a/c/cert/test/rules/PRE31-C/test.c b/c/cert/test/rules/PRE31-C/test.c new file mode 100644 index 0000000000..87ca535f2b --- /dev/null +++ b/c/cert/test/rules/PRE31-C/test.c @@ -0,0 +1,29 @@ +#include + +#define safe(x) ((x) + 1) +#define unsafe(x) (x) * (x) + +void test_crement() { + int i = 0; + safe(i++); // COMPLIANT + unsafe(i++); // NON_COMPLIANT + safe(i--); // COMPLIANT + unsafe(i--); // NON_COMPLIANT +} + +int addOne(int x) { return x + 1; } +int writeX(int x) { + printf("%d", x); + return x; +} + +int external(); + +void test_call() { + safe(addOne(10)); // COMPLIANT + safe(external()); // COMPLIANT + safe(writeX(10)); // COMPLIANT + unsafe(addOne(10)); // COMPLIANT + unsafe(external()); // NON_COMPLIANT + unsafe(writeX(10)); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.expected b/c/cert/test/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.expected index f25c7ea0e0..efbf021972 100644 --- a/c/cert/test/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.expected +++ b/c/cert/test/rules/PRE32-C/MacroOrFunctionArgsContainHashToken.expected @@ -4,5 +4,3 @@ | test.c:20:1:20:16 | #ifdef SOMEMACRO | Invocation of function memcpy includes a token "#ifdef SOMEMACRO" that could be confused for an argument preprocessor directive. | | test.c:22:1:22:5 | #else | Invocation of function memcpy includes a token "#else" that could be confused for an argument preprocessor directive. | | test.c:24:1:24:6 | #endif | Invocation of function memcpy includes a token "#endif" that could be confused for an argument preprocessor directive. | -| test.c:27:1:27:8 | #if TEST | Invocation of function memcpy includes a token "#if TEST" that could be confused for an argument preprocessor directive. | -| test.c:28:1:28:6 | #endif | Invocation of function memcpy includes a token "#endif" that could be confused for an argument preprocessor directive. | diff --git a/c/cert/test/rules/PRE32-C/test.c b/c/cert/test/rules/PRE32-C/test.c index af3606f24c..bf07beecb5 100644 --- a/c/cert/test/rules/PRE32-C/test.c +++ b/c/cert/test/rules/PRE32-C/test.c @@ -24,6 +24,6 @@ void func(const char *src) { #endif // NON_COMPLIANT ); -#if TEST // COMPLIANT[FALSE_POSITIVE] -#endif // COMPLIANT[FALSE_POSITIVE] -} \ No newline at end of file +#if TEST // COMPLIANT +#endif // COMPLIANT +} diff --git a/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.expected b/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.expected new file mode 100644 index 0000000000..ce13ee69a7 --- /dev/null +++ b/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.expected @@ -0,0 +1,7 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql:110,11-19) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql:110,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql:111,9-17) +| test.c:10:3:10:18 | call to log_local_unsafe | Asynchronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:16:7:16:12 | call to signal | signal handler | +| test.c:11:3:11:6 | call to free | Asynchronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:16:7:16:12 | call to signal | signal handler | +| test.c:46:3:46:9 | call to longjmp | Asynchronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:50:7:50:12 | call to signal | signal handler | +| test.c:76:7:76:11 | call to raise | Asynchronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:91:7:91:12 | call to signal | signal handler | diff --git a/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.expected.qcc b/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.expected.qcc new file mode 100644 index 0000000000..9bbf6be660 --- /dev/null +++ b/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.expected.qcc @@ -0,0 +1,4 @@ +| test.c:10:3:10:18 | call to log_local_unsafe | Asyncronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:16:7:16:12 | call to signal | signal handler | +| test.c:11:3:11:6 | call to free | Asyncronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:16:7:16:12 | call to signal | signal handler | +| test.c:46:3:46:17 | call to siglongjmp | Asyncronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:50:7:50:12 | call to signal | signal handler | +| test.c:76:7:76:11 | call to raise | Asyncronous-unsafe function calls within a $@ can lead to undefined behavior. | test.c:91:7:91:12 | call to signal | signal handler | diff --git a/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.qlref b/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.qlref new file mode 100644 index 0000000000..42cda5f2e7 --- /dev/null +++ b/c/cert/test/rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.qlref @@ -0,0 +1 @@ +rules/SIG30-C/CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql \ No newline at end of file diff --git a/c/cert/test/rules/SIG30-C/test.c b/c/cert/test/rules/SIG30-C/test.c new file mode 100644 index 0000000000..451c0652ad --- /dev/null +++ b/c/cert/test/rules/SIG30-C/test.c @@ -0,0 +1,120 @@ +#include +#include +#include + +char *info = NULL; + +void log_local_unsafe(void) { fputs(info, stderr); } + +void handler1(int signum) { + log_local_unsafe(); // NON_COMPLIANT + free(info); // NON_COMPLIANT + info = NULL; +} + +int f1(void) { + if (signal(SIGINT, handler1) == SIG_ERR) { + //... + } + + log_local_unsafe(); + + return 0; +} + +volatile sig_atomic_t eflag = 0; + +void handler2(int signum) { eflag = 1; } + +int f2(void) { + if (signal(SIGINT, handler2) == SIG_ERR) { + // ... + } + + while (!eflag) { + log_local_unsafe(); + } + + return 0; +} + +#include + +static jmp_buf env; + +void handler3(int signum) { + longjmp(env, 1); // NON_COMPLIANT +} + +int f3(void) { + if (signal(SIGINT, handler3) == SIG_ERR) { + // ... + } + log_local_unsafe(); + + return 0; +} + +int f4(void) { + if (signal(SIGINT, handler2) == SIG_ERR) { + // ... + } + + while (!eflag) { + + log_local_unsafe(); + } + + return 0; +} + +void term_handler(int signum) { // SIGTERM handler +} + +void int_handler(int signum) { + // SIGINT handler + if (raise(SIGTERM) != 0) { // NON_COMPLIANT + // ... + } + if (raise(SIGINT) != 0) { // COMPLIANT + // ... + } + if (raise(signum) != 0) { // COMPLIANT + // ... + } +} + +int f5(void) { + if (signal(SIGTERM, term_handler) == SIG_ERR) { + // ... + } + if (signal(SIGINT, int_handler) == SIG_ERR) { + // ... + } + + if (raise(SIGINT) != 0) { + // ... + } + + return EXIT_SUCCESS; +} + +void int_handler6(int signum) { + + term_handler(SIGTERM); // COMPLIANT +} + +int f6(void) { + if (signal(SIGTERM, term_handler) == SIG_ERR) { + // ... + } + if (signal(SIGINT, int_handler6) == SIG_ERR) { + // ... + } + + if (raise(SIGINT) != 0) { + // ... + } + + return EXIT_SUCCESS; +} diff --git a/c/cert/test/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.expected b/c/cert/test/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.expected new file mode 100644 index 0000000000..2a0f3e6bde --- /dev/null +++ b/c/cert/test/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.expected @@ -0,0 +1 @@ +| test.c:7:10:7:16 | err_msg | Shared object access within a $@ can lead to undefined behavior. | test.c:11:3:11:8 | call to signal | signal handler | diff --git a/c/cert/test/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.qlref b/c/cert/test/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.qlref new file mode 100644 index 0000000000..d6202bdc6d --- /dev/null +++ b/c/cert/test/rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.qlref @@ -0,0 +1 @@ +rules/SIG31-C/DoNotAccessSharedObjectsInSignalHandlers.ql \ No newline at end of file diff --git a/c/cert/test/rules/SIG31-C/test.c b/c/cert/test/rules/SIG31-C/test.c new file mode 100644 index 0000000000..1966ee940f --- /dev/null +++ b/c/cert/test/rules/SIG31-C/test.c @@ -0,0 +1,40 @@ +#include +#include +#include + +char *err_msg; +void handler(int signum) { + strcpy(err_msg, "SIGINT encountered."); // NON_COMPLIANT +} + +void f1(void) { + signal(SIGINT, handler); + // ... +} + +volatile sig_atomic_t e_flag1 = 0; +void handler2(int signum) { + e_flag1 = 1; // COMPLIANT +} + +void f2(void) { + signal(SIGINT, handler2); + // ... +} + +#include + +atomic_int e_flag3 = ATOMIC_VAR_INIT(0); +void handler3(int signum) { + e_flag3 = 1; // COMPLIANT +} + +void f3(void) { + if (!atomic_is_lock_free(&e_flag3)) { + // ... + } + + if (signal(SIGINT, handler3) == SIG_ERR) { + // ... + } +} diff --git a/c/cert/test/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.expected b/c/cert/test/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.expected new file mode 100644 index 0000000000..8bb7fc76c9 --- /dev/null +++ b/c/cert/test/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.expected @@ -0,0 +1,2 @@ +| test.c:4:7:4:12 | call to signal | Reasserting handler bindings introduces a race condition on nonpersistent platforms and is redundant otherwise. | +| test.c:17:7:17:12 | call to signal | Reasserting handler bindings introduces a race condition on nonpersistent platforms and is redundant otherwise. | diff --git a/c/cert/test/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.qlref b/c/cert/test/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.qlref new file mode 100644 index 0000000000..11d893257f --- /dev/null +++ b/c/cert/test/rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.qlref @@ -0,0 +1 @@ +rules/SIG34-C/DoNotCallSignalFromInterruptibleSignalHandlers.ql \ No newline at end of file diff --git a/c/cert/test/rules/SIG34-C/test.c b/c/cert/test/rules/SIG34-C/test.c new file mode 100644 index 0000000000..df9f465409 --- /dev/null +++ b/c/cert/test/rules/SIG34-C/test.c @@ -0,0 +1,27 @@ +#include + +void handler1(int signum) { + if (signal(signum, handler1) == SIG_ERR) // NON_COMPLIANT + { + //... + } +} + +void f1(void) { + if (signal(SIGUSR1, handler1) == SIG_ERR) { + // ... + } +} + +void handler2(int signum) { + if (signal(SIGUSR1, handler2) == SIG_ERR) // NON_COMPLIANT + { + //... + } +} + +void f2(void) { + if (signal(SIGUSR1, handler2) == SIG_ERR) { + // ... + } +} diff --git a/c/cert/test/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.expected b/c/cert/test/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.expected new file mode 100644 index 0000000000..fb78049d25 --- /dev/null +++ b/c/cert/test/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.expected @@ -0,0 +1,2 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotReturnFromAComputationalExceptionHandler.ql:44,5-13) +| test.c:10:1:10:1 | return ... | Do not return from a $@ signal handler. | test.c:13:10:13:15 | SIGFPE | computational exception | diff --git a/c/cert/test/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.qlref b/c/cert/test/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.qlref new file mode 100644 index 0000000000..0e2aa3f976 --- /dev/null +++ b/c/cert/test/rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.qlref @@ -0,0 +1 @@ +rules/SIG35-C/DoNotReturnFromAComputationalExceptionHandler.ql \ No newline at end of file diff --git a/c/cert/test/rules/SIG35-C/test.c b/c/cert/test/rules/SIG35-C/test.c new file mode 100644 index 0000000000..cc57bd4b8b --- /dev/null +++ b/c/cert/test/rules/SIG35-C/test.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +volatile sig_atomic_t eflag; + +void sighandle(int s) { // NON_COMPLIANT + eflag = 1; +} + +int f1(int argc, char *argv[]) { + signal(SIGFPE, sighandle); + + return 0; +} + +void sighandle2(int s) { // COMPLIANT + eflag = 1; + abort(); +} + +int f2(int argc, char *argv[]) { + signal(SIGFPE, sighandle2); + return 0; +} diff --git a/c/cert/test/rules/STR30-C/DoNotAttemptToModifyStringLiterals.expected b/c/cert/test/rules/STR30-C/DoNotAttemptToModifyStringLiterals.expected index 27ef66bc7a..d95b48e1c3 100644 --- a/c/cert/test/rules/STR30-C/DoNotAttemptToModifyStringLiterals.expected +++ b/c/cert/test/rules/STR30-C/DoNotAttemptToModifyStringLiterals.expected @@ -1,3 +1,18 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:47,65-73) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:48,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:69,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:82,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:106,11-19) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:106,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:106,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:111,11-19) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:111,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:111,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:144,11-19) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:144,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:144,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:155,53-61) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotAttemptToModifyStringLiterals.ql:156,5-13) | test.c:7:3:7:3 | a | This operation may write to a string that may be a string literal that was $@. | test.c:6:13:6:20 | codeql | created here | | test.c:30:3:30:3 | a | This operation may write to a string that may be a string literal that was $@. | test.c:29:13:29:18 | call to strchr | created here | | test.c:36:3:36:3 | b | This operation may write to a string that may be a string literal that was $@. | test.c:35:13:35:18 | call to strchr | created here | diff --git a/c/cert/test/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.expected b/c/cert/test/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.expected index d5529dd26d..9a87a6775b 100644 --- a/c/cert/test/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.expected +++ b/c/cert/test/rules/STR31-C/StringsHasSufficientSpaceForTheNullTerminator.expected @@ -1,4 +1,10 @@ -| test.c:10:20:10:24 | Co | Expression produces or consumes a string that may not have sufficient space for a null-terminator. | +WARNING: module 'DataFlow' has been deprecated and may be removed in future (StringsHasSufficientSpaceForTheNullTerminator.ql:62,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (StringsHasSufficientSpaceForTheNullTerminator.ql:62,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (StringsHasSufficientSpaceForTheNullTerminator.ql:68,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (StringsHasSufficientSpaceForTheNullTerminator.ql:68,54-62) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (StringsHasSufficientSpaceForTheNullTerminator.ql:62,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (StringsHasSufficientSpaceForTheNullTerminator.ql:68,5-18) +| test.c:10:20:10:24 | Cod | Expression produces or consumes a string that may not have sufficient space for a null-terminator. | | test.c:16:3:16:9 | call to strncpy | Expression produces or consumes a string that may not have sufficient space for a null-terminator. | | test.c:26:3:26:10 | call to snprintf | Expression produces or consumes a string that may not have sufficient space for a null-terminator. | | test.c:32:3:32:9 | call to strncat | Expression produces or consumes a string that may not have sufficient space for a null-terminator. | diff --git a/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected b/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected index bddddc6cb6..f537cc72ac 100644 --- a/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected +++ b/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected @@ -1,16 +1,32 @@ -| test.c:19:3:19:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:7:20:7:24 | Co | this expression | -| test.c:20:3:20:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:7:20:7:24 | Co | this expression | -| test.c:22:3:22:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:13:3:13:9 | call to strncpy | this expression | -| test.c:23:3:23:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:13:3:13:9 | call to strncpy | this expression | -| test.c:24:3:24:8 | call to strlen | String modified by $@ is passed to function expecting a null-terminated string. | test.c:13:3:13:9 | call to strncpy | this expression | -| test.c:33:3:33:9 | call to wprintf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:30:24:30:29 | Co | this expression | -| test.c:46:3:46:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:41:3:41:10 | call to snprintf | this expression | -| test.c:47:3:47:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:41:3:41:10 | call to snprintf | this expression | -| test.c:55:3:55:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:53:3:53:9 | call to strncat | this expression | -| test.c:56:3:56:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:53:3:53:9 | call to strncat | this expression | -| test.c:62:3:62:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:60:20:60:24 | Co | this expression | -| test.c:63:3:63:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:60:20:60:24 | Co | this expression | -| test.c:75:3:75:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:72:20:72:24 | Co | this expression | -| test.c:76:3:76:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:72:20:72:24 | Co | this expression | -| test.c:85:3:85:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:83:3:83:9 | call to strncpy | this expression | -| test.c:86:3:86:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:83:3:83:9 | call to strncpy | this expression | +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:69,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:71,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:79,39-47) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:80,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:86,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:88,34-42) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:88,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:128,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:128,26-34) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (NonNullTerminatedToFunctionThatExpectsAString.ql:125,17-30) +| test.c:20:3:20:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:8:20:8:24 | Cod | this expression | +| test.c:21:3:21:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:8:20:8:24 | Cod | this expression | +| test.c:23:3:23:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:14:3:14:9 | call to strncpy | this expression | +| test.c:24:3:24:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:14:3:14:9 | call to strncpy | this expression | +| test.c:25:3:25:8 | call to strlen | String modified by $@ is passed to function expecting a null-terminated string. | test.c:14:3:14:9 | call to strncpy | this expression | +| test.c:34:3:34:9 | call to wprintf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:31:24:31:29 | Cod | this expression | +| test.c:47:3:47:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:42:3:42:10 | call to snprintf | this expression | +| test.c:48:3:48:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:42:3:42:10 | call to snprintf | this expression | +| test.c:56:3:56:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:54:3:54:9 | call to strncat | this expression | +| test.c:57:3:57:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:54:3:54:9 | call to strncat | this expression | +| test.c:63:3:63:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:61:20:61:24 | Cod | this expression | +| test.c:64:3:64:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:61:20:61:24 | Cod | this expression | +| test.c:76:3:76:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:73:20:73:24 | Cod | this expression | +| test.c:77:3:77:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:73:20:73:24 | Cod | this expression | +| test.c:86:3:86:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:84:3:84:9 | call to strncpy | this expression | +| test.c:87:3:87:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:84:3:84:9 | call to strncpy | this expression | +| test.c:95:3:95:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:93:17:93:21 | Cod | this expression | +| test.c:95:3:95:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:94:3:94:9 | call to strncpy | this expression | +| test.c:98:3:98:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:93:17:93:21 | Cod | this expression | +| test.c:98:3:98:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:94:3:94:9 | call to strncpy | this expression | +| test.c:122:3:122:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:117:17:117:21 | Cod | this expression | +| test.c:122:3:122:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:118:3:118:9 | call to strncpy | this expression | diff --git a/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected.qcc b/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected.qcc new file mode 100644 index 0000000000..fe51f625a8 --- /dev/null +++ b/c/cert/test/rules/STR32-C/NonNullTerminatedToFunctionThatExpectsAString.expected.qcc @@ -0,0 +1,15 @@ +| test.c:19:3:19:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:7:20:7:24 | Co | this expression | +| test.c:20:3:20:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:7:20:7:24 | Co | this expression | +| test.c:22:3:22:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:13:3:13:9 | call to strncpy | this expression | +| test.c:23:3:23:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:13:3:13:9 | call to strncpy | this expression | +| test.c:24:3:24:8 | call to strlen | String modified by $@ is passed to function expecting a null-terminated string. | test.c:13:3:13:9 | call to strncpy | this expression | +| test.c:46:3:46:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:41:3:41:10 | call to snprintf | this expression | +| test.c:47:3:47:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:41:3:41:10 | call to snprintf | this expression | +| test.c:55:3:55:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:53:3:53:9 | call to strncat | this expression | +| test.c:56:3:56:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:53:3:53:9 | call to strncat | this expression | +| test.c:62:3:62:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:60:20:60:24 | Co | this expression | +| test.c:63:3:63:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:60:20:60:24 | Co | this expression | +| test.c:75:3:75:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:72:20:72:24 | Co | this expression | +| test.c:76:3:76:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:72:20:72:24 | Co | this expression | +| test.c:85:3:85:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:83:3:83:9 | call to strncpy | this expression | +| test.c:86:3:86:8 | call to printf | String modified by $@ is passed to function expecting a null-terminated string. | test.c:83:3:83:9 | call to strncpy | this expression | diff --git a/c/cert/test/rules/STR32-C/test.c b/c/cert/test/rules/STR32-C/test.c index 288ef7e5e0..ea72abe201 100644 --- a/c/cert/test/rules/STR32-C/test.c +++ b/c/cert/test/rules/STR32-C/test.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -84,4 +85,39 @@ f5() { printf("%s", a1_nnt); // NON_COMPLIANT printf(a1_nnt); // NON_COMPLIANT -} \ No newline at end of file +} + +void test_fn_reported_in_31_simple() { + char *str; + str = (char *)malloc(3); + char a31[3] = "Cod"; // is NOT null terminated + strncpy(str, a31, 3); + printf(str); // NON_COMPLIANT + size_t cur_msg_size = 1024; + str = realloc(str, (cur_msg_size / 2 + 1) * sizeof(char)); + printf(str); // NON_COMPLIANT +} + +void test_fn_reported_in_31_simple_safe() { + char *str; + str = (char *)malloc(3); + char a31[3] = "Cod"; // is NOT null terminated + strncpy(str, a31, 3); + size_t cur_msg_size = 1024; + size_t temp_size = cur_msg_size / 2 + 1; + str = realloc(str, temp_size * sizeof(char)); + str[temp_size - 1] = L'\0'; // Properly null-terminate str + printf(str); // COMPLIANT +} + +void test_fn_reported_in_31_simple_relloc() { + char *str; + size_t cur_msg_size = 1024; + str = (char *)malloc(cur_msg_size); + char a31[3] = "Cod"; // is NOT null terminated + strncpy(str, a31, 3); + str[cur_msg_size - 1] = L'\0'; // Properly null-terminate str + size_t temp_size = cur_msg_size / 2 + 1; + str = realloc(str, temp_size * sizeof(char)); + printf(str); // NON_COMPLIANT +} diff --git a/c/cert/test/rules/STR32-C/test.c.qcc b/c/cert/test/rules/STR32-C/test.c.qcc new file mode 100644 index 0000000000..1c6761a701 --- /dev/null +++ b/c/cert/test/rules/STR32-C/test.c.qcc @@ -0,0 +1,87 @@ +#include +#include +#include + +void f1() { + char a1_nt[7] = "CodeQL"; // is null terminated + char a1_nnt[3] = "Cod"; // is NOT null termianted + + char a1[9]; + char a2[10]; + char a9[10]; + + strncpy(a2, a1, 5); // not null terminated because n < length(src) + strncpy(a9, a1, 10); // is null terminated; n > length(src) + + printf("%s", a1_nt); // COMPLIANT + printf(a1_nt); // COMPLIANT + + printf("%s", a1_nnt); // NON_COMPLIANT + printf(a1_nnt); // NON_COMPLIANT + + printf("%s", a2); // NON_COMPLIANT + printf(a2); // NON_COMPLIANT + strlen(a2); // NON_COMPLIANT + + printf(a9); // COMPLIANT + printf(a9); // COMPLIANT + + wchar_t wa1_nt[7] = L"CodeQL"; // is null terminated + wchar_t wa1_nnt[3] = L"Cod"; // is NOT null termianted + wprintf(wa1_nt); // COMPLIANT + // FALSE_NEGATIVES due to https://github.com/github/codeql/issues/12914 + wprintf(wa1_nnt); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +void f2() { + char a1[10]; + char a2[10]; + + snprintf(a1, 10, "CodeQL %d", 3); // will be null terminated + snprintf(a2, 11, "CodeQL %d", 3); // will not be null terminated + + printf("%s", a1); // COMPLIANT + printf(a1); // COMPLIANT + + printf("%s", a2); // NON_COMPLIANT + printf(a2); // NON_COMPLIANT +} + +void f3() { + char a1[2]; + + strncat(a1, "CodeQL", 5); // will not be null terminated + + printf(a1); // NON_COMPLIANT + printf("%s", a1); // NON_COMPLIANT +} + +void f4() { + char a1_nnt[3] = "Cod"; // is NOT null termianted + + printf("%s", a1_nnt); // NON_COMPLIANT + printf(a1_nnt); // NON_COMPLIANT + + a1_nnt[2] = '\0'; + + printf("%s", a1_nnt); // COMPLIANT + printf(a1_nnt); // COMPLIANT +} + +f5() { + char a1_nnt[3] = "Cod"; // is NOT null termianted + char a2[10] = "CodeQL"; + + printf("%s", a1_nnt); // NON_COMPLIANT + printf(a1_nnt); // NON_COMPLIANT + + a1_nnt[2] = '\0'; + + printf("%s", a1_nnt); // COMPLIANT + printf(a1_nnt); // COMPLIANT + + strncpy(a1_nnt, a2, 1); // not null terminated because n < length(src) + + printf("%s", a1_nnt); // NON_COMPLIANT + printf(a1_nnt); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.expected b/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.expected deleted file mode 100644 index 1c6424dc0c..0000000000 --- a/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.expected +++ /dev/null @@ -1,21 +0,0 @@ -| test.c:7:7:7:14 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:28:11:28:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:29:3:29:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:29:11:29:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:31:11:31:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:32:11:32:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:33:3:33:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:33:11:33:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:34:3:34:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:34:11:34:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:35:3:35:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:35:11:35:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:36:3:36:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:36:11:36:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:37:11:37:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:38:11:38:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:39:3:39:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:39:11:39:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:40:12:40:13 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:42:11:42:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | -| test.c:43:11:43:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | diff --git a/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.qlref b/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.qlref deleted file mode 100644 index 379d3b3f68..0000000000 --- a/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/STR34-C/CastCharBeforeConvertingToLargerSizes.ql \ No newline at end of file diff --git a/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.testref b/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.testref new file mode 100644 index 0000000000..0e13e05dc3 --- /dev/null +++ b/c/cert/test/rules/STR34-C/CastCharBeforeConvertingToLargerSizes.testref @@ -0,0 +1 @@ +c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql \ No newline at end of file diff --git a/c/cert/test/rules/STR34-C/test.c b/c/cert/test/rules/STR34-C/test.c deleted file mode 100644 index d4bd825c8e..0000000000 --- a/c/cert/test/rules/STR34-C/test.c +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include - -int f1() { - char *c_str; - int c; - c = *c_str++; // NON_COMPLIANT - return (c); -} - -int f2() { - unsigned char *c_str; - int c; - c = *c_str++; // COMPLIANT - return (c); -} - -int f3(void) { - char *c_str; - int c; - c = (unsigned char)*c_str++; // COMPLIANT - return (c); -} - -void f4() { - char *t; - - isalnum(*t); // NON_COMPLIANT - isalpha(*t); // NON_COMPLIANT - // isascii(*t); // Not part of the C Standard - isblank(*t); // NON_COMPLIANT - iscntrl(*t); // NON_COMPLIANT - isdigit(*t); // NON_COMPLIANT - isgraph(*t); // NON_COMPLIANT - islower(*t); // NON_COMPLIANT - isprint(*t); // NON_COMPLIANT - ispunct(*t); // NON_COMPLIANT - isspace(*t); // NON_COMPLIANT - isupper(*t); // NON_COMPLIANT - isxdigit(*t); // NON_COMPLIANT - // toascii(i); // Not part of the C Standard - toupper(*t); // NON_COMPLIANT - tolower(*t); // NON_COMPLIANT -} - -void f5() { - unsigned char *t; - - isalnum(*t); // COMPLIANT - isalpha(*t); // COMPLIANT - // isascii(*t); // Not part of the C Standard - isblank(*t); // COMPLIANT - iscntrl(*t); // COMPLIANT - isdigit(*t); // COMPLIANT - isgraph(*t); // COMPLIANT - islower(*t); // COMPLIANT - isprint(*t); // COMPLIANT - ispunct(*t); // COMPLIANT - isspace(*t); // COMPLIANT - isupper(*t); // COMPLIANT - isxdigit(*t); // COMPLIANT - // toascii(i); // Not part of the C Standard - toupper(*t); // COMPLIANT - tolower(*t); // COMPLIANT -} - -void f6() { - char *t; - - isalnum((unsigned char)*t); // COMPLIANT - isalpha((unsigned char)*t); // COMPLIANT - // isascii((unsigned char*)*t); // Not part of the C Standard - isblank((unsigned char)*t); // COMPLIANT - iscntrl((unsigned char)*t); // COMPLIANT - isdigit((unsigned char)*t); // COMPLIANT - isgraph((unsigned char)*t); // COMPLIANT - islower((unsigned char)*t); // COMPLIANT - isprint((unsigned char)*t); // COMPLIANT - ispunct((unsigned char)*t); // COMPLIANT - isspace((unsigned char)*t); // COMPLIANT - isupper((unsigned char)*t); // COMPLIANT - isxdigit((unsigned char)*t); // COMPLIANT - // toascii((unsigned int) i); // Not part of the C Standard - toupper((unsigned char)*t); // COMPLIANT - tolower((unsigned char)*t); // COMPLIANT -} - -void f7() { - int t; - - // Note these are all NON_COMPLIANT under STR37-C - - isalnum(t); // COMPLIANT - isalpha(t); // COMPLIANT - // isascii(t); // Not part of the C Standard - isblank(t); // COMPLIANT - iscntrl(t); // COMPLIANT - isdigit(t); // COMPLIANT - isgraph(t); // COMPLIANT - islower(t); // COMPLIANT - isprint(t); // COMPLIANT - ispunct(t); // COMPLIANT - isspace(t); // COMPLIANT - isupper(t); // COMPLIANT - isxdigit(t); // COMPLIANT - // toascii(i); // Not part of the C Standard - toupper(t); // COMPLIANT - tolower(t); // COMPLIANT -} diff --git a/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected index b655289f4e..43008e02d0 100644 --- a/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected +++ b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected @@ -1,28 +1,28 @@ -| test.c:7:3:7:9 | call to isalnum | $@ to character-handling function may not be representable as an unsigned char. | test.c:7:11:7:12 | (int)... | Argument | -| test.c:8:3:8:13 | call to isalpha | $@ to character-handling function may not be representable as an unsigned char. | test.c:8:11:8:12 | (int)... | Argument | -| test.c:10:3:10:9 | call to isblank | $@ to character-handling function may not be representable as an unsigned char. | test.c:10:11:10:12 | (int)... | Argument | -| test.c:11:3:11:9 | call to iscntrl | $@ to character-handling function may not be representable as an unsigned char. | test.c:11:11:11:12 | (int)... | Argument | -| test.c:12:3:12:13 | call to isdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:12:11:12:12 | (int)... | Argument | -| test.c:13:3:13:13 | call to isgraph | $@ to character-handling function may not be representable as an unsigned char. | test.c:13:11:13:12 | (int)... | Argument | -| test.c:14:3:14:13 | call to islower | $@ to character-handling function may not be representable as an unsigned char. | test.c:14:11:14:12 | (int)... | Argument | -| test.c:15:3:15:13 | call to isprint | $@ to character-handling function may not be representable as an unsigned char. | test.c:15:11:15:12 | (int)... | Argument | -| test.c:16:3:16:9 | call to ispunct | $@ to character-handling function may not be representable as an unsigned char. | test.c:16:11:16:12 | (int)... | Argument | -| test.c:17:3:17:13 | call to __isspace | $@ to character-handling function may not be representable as an unsigned char. | test.c:17:11:17:12 | (int)... | Argument | -| test.c:18:3:18:13 | call to isupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:18:11:18:12 | (int)... | Argument | -| test.c:19:3:19:10 | call to isxdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:19:12:19:13 | (int)... | Argument | -| test.c:21:3:21:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:21:11:21:12 | (int)... | Argument | -| test.c:22:3:22:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:22:11:22:12 | (int)... | Argument | +| test.c:7:3:7:9 | call to isalnum | $@ to character-handling function may not be representable as an unsigned char. | test.c:7:11:7:12 | * ... | Argument | +| test.c:8:3:8:13 | isalpha(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:8:11:8:12 | * ... | Argument | +| test.c:10:3:10:9 | call to isblank | $@ to character-handling function may not be representable as an unsigned char. | test.c:10:11:10:12 | * ... | Argument | +| test.c:11:3:11:9 | call to iscntrl | $@ to character-handling function may not be representable as an unsigned char. | test.c:11:11:11:12 | * ... | Argument | +| test.c:12:3:12:13 | isdigit(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:12:3:12:13 | (...) | Argument | +| test.c:13:3:13:13 | isgraph(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:13:3:13:13 | (...) | Argument | +| test.c:14:3:14:13 | islower(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:14:3:14:13 | (...) | Argument | +| test.c:15:3:15:13 | isprint(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:15:3:15:13 | (...) | Argument | +| test.c:16:3:16:9 | call to ispunct | $@ to character-handling function may not be representable as an unsigned char. | test.c:16:11:16:12 | * ... | Argument | +| test.c:17:3:17:13 | call to __isspace | $@ to character-handling function may not be representable as an unsigned char. | test.c:17:11:17:12 | * ... | Argument | +| test.c:18:3:18:13 | isupper(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:18:3:18:13 | (...) | Argument | +| test.c:19:3:19:10 | call to isxdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:19:12:19:13 | * ... | Argument | +| test.c:21:3:21:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:21:11:21:12 | * ... | Argument | +| test.c:22:3:22:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:22:11:22:12 | * ... | Argument | | test.c:70:3:70:9 | call to isalnum | $@ to character-handling function may not be representable as an unsigned char. | test.c:70:11:70:11 | t | Argument | -| test.c:71:3:71:12 | call to isalpha | $@ to character-handling function may not be representable as an unsigned char. | test.c:71:11:71:11 | t | Argument | +| test.c:71:3:71:12 | isalpha(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:71:11:71:11 | t | Argument | | test.c:73:3:73:9 | call to isblank | $@ to character-handling function may not be representable as an unsigned char. | test.c:73:11:73:11 | t | Argument | | test.c:74:3:74:9 | call to iscntrl | $@ to character-handling function may not be representable as an unsigned char. | test.c:74:11:74:11 | t | Argument | -| test.c:75:3:75:12 | call to isdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:75:11:75:11 | t | Argument | -| test.c:76:3:76:12 | call to isgraph | $@ to character-handling function may not be representable as an unsigned char. | test.c:76:11:76:11 | t | Argument | -| test.c:77:3:77:12 | call to islower | $@ to character-handling function may not be representable as an unsigned char. | test.c:77:11:77:11 | t | Argument | -| test.c:78:3:78:12 | call to isprint | $@ to character-handling function may not be representable as an unsigned char. | test.c:78:11:78:11 | t | Argument | +| test.c:75:3:75:12 | isdigit(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:75:3:75:12 | (...) | Argument | +| test.c:76:3:76:12 | isgraph(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:76:3:76:12 | (...) | Argument | +| test.c:77:3:77:12 | islower(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:77:3:77:12 | (...) | Argument | +| test.c:78:3:78:12 | isprint(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:78:3:78:12 | (...) | Argument | | test.c:79:3:79:9 | call to ispunct | $@ to character-handling function may not be representable as an unsigned char. | test.c:79:11:79:11 | t | Argument | | test.c:80:3:80:12 | call to __isspace | $@ to character-handling function may not be representable as an unsigned char. | test.c:80:11:80:11 | t | Argument | -| test.c:81:3:81:12 | call to isupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:81:11:81:11 | t | Argument | +| test.c:81:3:81:12 | isupper(a) | $@ to character-handling function may not be representable as an unsigned char. | test.c:81:3:81:12 | (...) | Argument | | test.c:82:3:82:10 | call to isxdigit | $@ to character-handling function may not be representable as an unsigned char. | test.c:82:12:82:12 | t | Argument | | test.c:84:3:84:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:84:11:84:11 | t | Argument | | test.c:85:3:85:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:85:11:85:11 | t | Argument | diff --git a/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.clang b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.clang new file mode 100644 index 0000000000..3ad77b8c58 --- /dev/null +++ b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.clang @@ -0,0 +1,28 @@ +| test.c:7:3:7:13 | isalnum(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:7:3:7:13 | (...) | Argument | +| test.c:8:3:8:13 | isalpha(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:8:3:8:13 | (...) | Argument | +| test.c:10:3:10:13 | isblank(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:10:3:10:13 | (...) | Argument | +| test.c:11:3:11:13 | iscntrl(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:11:3:11:13 | (...) | Argument | +| test.c:12:3:12:13 | isdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:12:3:12:13 | (...) | Argument | +| test.c:13:3:13:13 | isgraph(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:13:3:13:13 | (...) | Argument | +| test.c:14:3:14:13 | islower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:14:3:14:13 | (...) | Argument | +| test.c:15:3:15:13 | isprint(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:15:3:15:13 | (...) | Argument | +| test.c:16:3:16:13 | ispunct(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:16:3:16:13 | (...) | Argument | +| test.c:17:3:17:13 | isspace(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:17:3:17:13 | (...) | Argument | +| test.c:18:3:18:13 | isupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:18:3:18:13 | (...) | Argument | +| test.c:19:3:19:14 | isxdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:19:3:19:14 | (...) | Argument | +| test.c:21:3:21:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:21:11:21:12 | * ... | Argument | +| test.c:22:3:22:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:22:11:22:12 | * ... | Argument | +| test.c:70:3:70:12 | isalnum(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:70:3:70:12 | (...) | Argument | +| test.c:71:3:71:12 | isalpha(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:71:3:71:12 | (...) | Argument | +| test.c:73:3:73:12 | isblank(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:73:3:73:12 | (...) | Argument | +| test.c:74:3:74:12 | iscntrl(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:74:3:74:12 | (...) | Argument | +| test.c:75:3:75:12 | isdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:75:3:75:12 | (...) | Argument | +| test.c:76:3:76:12 | isgraph(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:76:3:76:12 | (...) | Argument | +| test.c:77:3:77:12 | islower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:77:3:77:12 | (...) | Argument | +| test.c:78:3:78:12 | isprint(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:78:3:78:12 | (...) | Argument | +| test.c:79:3:79:12 | ispunct(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:79:3:79:12 | (...) | Argument | +| test.c:80:3:80:12 | isspace(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:80:3:80:12 | (...) | Argument | +| test.c:81:3:81:12 | isupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:81:3:81:12 | (...) | Argument | +| test.c:82:3:82:13 | isxdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:82:3:82:13 | (...) | Argument | +| test.c:84:3:84:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:84:11:84:11 | t | Argument | +| test.c:85:3:85:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:85:11:85:11 | t | Argument | diff --git a/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.gcc b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.gcc new file mode 100644 index 0000000000..3ad77b8c58 --- /dev/null +++ b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.gcc @@ -0,0 +1,28 @@ +| test.c:7:3:7:13 | isalnum(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:7:3:7:13 | (...) | Argument | +| test.c:8:3:8:13 | isalpha(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:8:3:8:13 | (...) | Argument | +| test.c:10:3:10:13 | isblank(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:10:3:10:13 | (...) | Argument | +| test.c:11:3:11:13 | iscntrl(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:11:3:11:13 | (...) | Argument | +| test.c:12:3:12:13 | isdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:12:3:12:13 | (...) | Argument | +| test.c:13:3:13:13 | isgraph(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:13:3:13:13 | (...) | Argument | +| test.c:14:3:14:13 | islower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:14:3:14:13 | (...) | Argument | +| test.c:15:3:15:13 | isprint(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:15:3:15:13 | (...) | Argument | +| test.c:16:3:16:13 | ispunct(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:16:3:16:13 | (...) | Argument | +| test.c:17:3:17:13 | isspace(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:17:3:17:13 | (...) | Argument | +| test.c:18:3:18:13 | isupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:18:3:18:13 | (...) | Argument | +| test.c:19:3:19:14 | isxdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:19:3:19:14 | (...) | Argument | +| test.c:21:3:21:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:21:11:21:12 | * ... | Argument | +| test.c:22:3:22:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:22:11:22:12 | * ... | Argument | +| test.c:70:3:70:12 | isalnum(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:70:3:70:12 | (...) | Argument | +| test.c:71:3:71:12 | isalpha(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:71:3:71:12 | (...) | Argument | +| test.c:73:3:73:12 | isblank(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:73:3:73:12 | (...) | Argument | +| test.c:74:3:74:12 | iscntrl(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:74:3:74:12 | (...) | Argument | +| test.c:75:3:75:12 | isdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:75:3:75:12 | (...) | Argument | +| test.c:76:3:76:12 | isgraph(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:76:3:76:12 | (...) | Argument | +| test.c:77:3:77:12 | islower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:77:3:77:12 | (...) | Argument | +| test.c:78:3:78:12 | isprint(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:78:3:78:12 | (...) | Argument | +| test.c:79:3:79:12 | ispunct(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:79:3:79:12 | (...) | Argument | +| test.c:80:3:80:12 | isspace(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:80:3:80:12 | (...) | Argument | +| test.c:81:3:81:12 | isupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:81:3:81:12 | (...) | Argument | +| test.c:82:3:82:13 | isxdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:82:3:82:13 | (...) | Argument | +| test.c:84:3:84:9 | call to toupper | $@ to character-handling function may not be representable as an unsigned char. | test.c:84:11:84:11 | t | Argument | +| test.c:85:3:85:9 | call to tolower | $@ to character-handling function may not be representable as an unsigned char. | test.c:85:11:85:11 | t | Argument | diff --git a/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.qcc b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.qcc new file mode 100644 index 0000000000..7cbab798dc --- /dev/null +++ b/c/cert/test/rules/STR37-C/ToCharacterHandlingFunctionsRepresentableAsUChar.expected.qcc @@ -0,0 +1,28 @@ +| test.c:7:3:7:13 | isalnum(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:7:3:7:13 | (...) | Argument | +| test.c:8:3:8:13 | isalpha(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:8:3:8:13 | (...) | Argument | +| test.c:10:3:10:13 | isblank(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:10:3:10:13 | (...) | Argument | +| test.c:11:3:11:13 | iscntrl(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:11:3:11:13 | (...) | Argument | +| test.c:12:3:12:13 | isdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:12:3:12:13 | (...) | Argument | +| test.c:13:3:13:13 | isgraph(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:13:3:13:13 | (...) | Argument | +| test.c:14:3:14:13 | islower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:14:3:14:13 | (...) | Argument | +| test.c:15:3:15:13 | isprint(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:15:3:15:13 | (...) | Argument | +| test.c:16:3:16:13 | ispunct(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:16:3:16:13 | (...) | Argument | +| test.c:17:3:17:13 | isspace(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:17:3:17:13 | (...) | Argument | +| test.c:18:3:18:13 | isupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:18:3:18:13 | (...) | Argument | +| test.c:19:3:19:14 | isxdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:19:3:19:14 | (...) | Argument | +| test.c:21:3:21:13 | toupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:21:3:21:13 | (...) | Argument | +| test.c:22:3:22:13 | tolower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:22:3:22:13 | (...) | Argument | +| test.c:70:3:70:12 | isalnum(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:70:3:70:12 | (...) | Argument | +| test.c:71:3:71:12 | isalpha(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:71:3:71:12 | (...) | Argument | +| test.c:73:3:73:12 | isblank(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:73:3:73:12 | (...) | Argument | +| test.c:74:3:74:12 | iscntrl(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:74:3:74:12 | (...) | Argument | +| test.c:75:3:75:12 | isdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:75:3:75:12 | (...) | Argument | +| test.c:76:3:76:12 | isgraph(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:76:3:76:12 | (...) | Argument | +| test.c:77:3:77:12 | islower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:77:3:77:12 | (...) | Argument | +| test.c:78:3:78:12 | isprint(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:78:3:78:12 | (...) | Argument | +| test.c:79:3:79:12 | ispunct(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:79:3:79:12 | (...) | Argument | +| test.c:80:3:80:12 | isspace(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:80:3:80:12 | (...) | Argument | +| test.c:81:3:81:12 | isupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:81:3:81:12 | (...) | Argument | +| test.c:82:3:82:13 | isxdigit(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:82:3:82:13 | (...) | Argument | +| test.c:84:3:84:12 | toupper(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:84:3:84:12 | (...) | Argument | +| test.c:85:3:85:12 | tolower(c) | $@ to character-handling function may not be representable as an unsigned char. | test.c:85:3:85:12 | (...) | Argument | diff --git a/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected b/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected index f9499d3be5..e575109ede 100644 --- a/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected +++ b/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected @@ -1,12 +1,12 @@ -| test.c:15:3:15:9 | call to strncpy | Call to function $@ with a wide character string $@ where a narrow character string $@ is expected. | test.c:6:7:6:13 | strncpy | strncpy | test.c:15:11:15:12 | w2 | argument | test.c:6:15:6:18 | (unnamed parameter 0) | parameter | -| test.c:15:3:15:9 | call to strncpy | Call to function $@ with a wide character string $@ where a narrow character string $@ is expected. | test.c:6:7:6:13 | strncpy | strncpy | test.c:15:15:15:16 | w1 | argument | test.c:6:33:6:42 | (unnamed parameter 1) | parameter | -| test.c:16:3:16:9 | call to strncpy | Call to function $@ with a wide character string $@ where a narrow character string $@ is expected. | test.c:6:7:6:13 | strncpy | strncpy | test.c:16:11:16:12 | w2 | argument | test.c:6:15:6:18 | (unnamed parameter 0) | parameter | -| test.c:26:3:26:9 | call to wcsncpy | Call to function $@ with a narrow character string $@ where a wide character string $@ is expected. | test.c:7:10:7:16 | wcsncpy | wcsncpy | test.c:26:11:26:12 | n2 | argument | test.c:7:18:7:24 | (unnamed parameter 0) | parameter | -| test.c:26:3:26:9 | call to wcsncpy | Call to function $@ with a narrow character string $@ where a wide character string $@ is expected. | test.c:7:10:7:16 | wcsncpy | wcsncpy | test.c:26:15:26:16 | n1 | argument | test.c:7:45:7:51 | (unnamed parameter 1) | parameter | -| test.c:27:3:27:9 | call to wcsncpy | Call to function $@ with a narrow character string $@ where a wide character string $@ is expected. | test.c:7:10:7:16 | wcsncpy | wcsncpy | test.c:27:15:27:16 | n1 | argument | test.c:7:45:7:51 | (unnamed parameter 1) | parameter | -| test.c:32:3:32:9 | call to strncpy | Call to function $@ with a wide character string $@ where a narrow character string $@ is expected. | test.c:6:7:6:13 | strncpy | strncpy | test.c:32:11:32:12 | w2 | argument | test.c:6:15:6:18 | (unnamed parameter 0) | parameter | -| test.c:32:3:32:9 | call to strncpy | Call to function $@ with a wide character string $@ where a narrow character string $@ is expected. | test.c:6:7:6:13 | strncpy | strncpy | test.c:32:15:32:16 | w1 | argument | test.c:6:33:6:42 | (unnamed parameter 1) | parameter | -| test.c:33:3:33:9 | call to strncpy | Call to function $@ with a wide character string $@ where a narrow character string $@ is expected. | test.c:6:7:6:13 | strncpy | strncpy | test.c:33:11:33:12 | w2 | argument | test.c:6:15:6:18 | (unnamed parameter 0) | parameter | -| test.c:36:3:36:9 | call to wcsncpy | Call to function $@ with a narrow character string $@ where a wide character string $@ is expected. | test.c:7:10:7:16 | wcsncpy | wcsncpy | test.c:36:11:36:12 | n2 | argument | test.c:7:18:7:24 | (unnamed parameter 0) | parameter | -| test.c:36:3:36:9 | call to wcsncpy | Call to function $@ with a narrow character string $@ where a wide character string $@ is expected. | test.c:7:10:7:16 | wcsncpy | wcsncpy | test.c:36:15:36:16 | n1 | argument | test.c:7:45:7:51 | (unnamed parameter 1) | parameter | -| test.c:37:3:37:9 | call to wcsncpy | Call to function $@ with a narrow character string $@ where a wide character string $@ is expected. | test.c:7:10:7:16 | wcsncpy | wcsncpy | test.c:37:15:37:16 | n1 | argument | test.c:7:45:7:51 | (unnamed parameter 1) | parameter | +| test.c:11:3:11:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:11:11:11:12 | w2 | argument | +| test.c:11:3:11:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:11:15:11:16 | w1 | argument | +| test.c:12:3:12:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:12:11:12:12 | w2 | argument | +| test.c:22:3:22:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:22:11:22:12 | n2 | argument | +| test.c:22:3:22:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:22:15:22:16 | n1 | argument | +| test.c:23:3:23:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:23:15:23:16 | n1 | argument | +| test.c:28:3:28:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:28:11:28:12 | w2 | argument | +| test.c:28:3:28:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:28:15:28:16 | w1 | argument | +| test.c:29:3:29:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:29:11:29:12 | w2 | argument | +| test.c:32:3:32:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:32:11:32:12 | n2 | argument | +| test.c:32:3:32:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:32:15:32:16 | n1 | argument | +| test.c:33:3:33:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:33:15:33:16 | n1 | argument | diff --git a/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected.qcc b/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected.qcc new file mode 100644 index 0000000000..704cff7a94 --- /dev/null +++ b/c/cert/test/rules/STR38-C/DoNotConfuseNarrowAndWideFunctions.expected.qcc @@ -0,0 +1,9 @@ +| test.c:22:3:22:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:22:11:22:12 | n2 | argument | +| test.c:22:3:22:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:22:15:22:16 | n1 | argument | +| test.c:23:3:23:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:23:15:23:16 | n1 | argument | +| test.c:28:3:28:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:28:11:28:12 | w2 | argument | +| test.c:28:3:28:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:28:15:28:16 | w1 | argument | +| test.c:29:3:29:9 | call to strncpy | Call to function `strncpy` with a wide character string $@ where a narrow character string is expected. | test.c:29:11:29:12 | w2 | argument | +| test.c:32:3:32:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:32:11:32:12 | n2 | argument | +| test.c:32:3:32:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:32:15:32:16 | n1 | argument | +| test.c:33:3:33:9 | call to wcsncpy | Call to function `wcsncpy` with a narrow character string $@ where a wide character string is expected. | test.c:33:15:33:16 | n1 | argument | diff --git a/c/cert/test/rules/STR38-C/test.c b/c/cert/test/rules/STR38-C/test.c index e4464a6d71..99bfc22b1b 100644 --- a/c/cert/test/rules/STR38-C/test.c +++ b/c/cert/test/rules/STR38-C/test.c @@ -1,10 +1,6 @@ #include - -// defined in and but we get absolute -// paths using the current alert so they are defined here. -// to prevent absolute paths from being generated. -char *strncpy(char *__restrict, const char *__restrict, size_t); -wchar_t *wcsncpy(wchar_t *__restrict, const wchar_t *__restrict, size_t); +#include +#include void f1() { wchar_t w1[] = L"codeql"; diff --git a/c/cert/test/rules/STR38-C/test.c.qcc b/c/cert/test/rules/STR38-C/test.c.qcc new file mode 100644 index 0000000000..59f8891e0a --- /dev/null +++ b/c/cert/test/rules/STR38-C/test.c.qcc @@ -0,0 +1,35 @@ +#include +#include +#include + +void f1() { + wchar_t w1[] = L"codeql"; + wchar_t w2[] = L"codeql"; + char n1[] = "codeql"; + char n2[] = "codeql"; + // FALSE_NEGATIVES due to https://github.com/github/codeql/issues/12914 + strncpy(w2, w1, 1); // NON_COMPLIANT[FALSE_NEGATIVE] (2x) + strncpy(w2, n1, 1); // NON_COMPLIANT[FALSE_NEGATIVE] (1x) + strncpy(n2, n1, 1); // COMPLIANT +} + +void f2() { + wchar_t w1[] = L"codeql"; + wchar_t w2[] = L"codeql"; + char n1[] = "codeql"; + char n2[] = "codeql"; + + wcsncpy(n2, n1, 1); // NON_COMPLIANT (2x) + wcsncpy(w2, n1, 1); // NON_COMPLIANT (1x) + wcsncpy(w2, w1, 1); // COMPLIANT +} + +void f3(wchar_t *w1, wchar_t *w2, char *n1, char *n2) { + strncpy(w2, w1, 1); // NON_COMPLIANT (2x) + strncpy(w2, n1, 1); // NON_COMPLIANT (1x) + strncpy(n2, n1, 1); // COMPLIANT + + wcsncpy(n2, n1, 1); // NON_COMPLIANT (2x) + wcsncpy(w2, n1, 1); // NON_COMPLIANT (1x) + wcsncpy(w2, w1, 1); // COMPLIANT +} \ No newline at end of file diff --git a/c/common/src/codeql-pack.lock.yml b/c/common/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/c/common/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/common/src/codingstandards/c/Errno.qll b/c/common/src/codingstandards/c/Errno.qll index 0f0cd7c10d..768927f505 100644 --- a/c/common/src/codingstandards/c/Errno.qll +++ b/c/common/src/codingstandards/c/Errno.qll @@ -2,26 +2,37 @@ import cpp -/* +/** * An errno-setting function */ - abstract class ErrnoSettingFunction extends Function { } -/* +/** * An errno-setting function that return out-of-band errors indicators + * as listed in the MISRA standard */ - -class OutOfBandErrnoSettingFunction extends ErrnoSettingFunction { - OutOfBandErrnoSettingFunction() { +class OutOfBandErrnoSettingFunctionMisra extends ErrnoSettingFunction { + OutOfBandErrnoSettingFunctionMisra() { this.hasGlobalName(["ftell", "fgetpos", "fsetpos", "mbrtowc", "wcrtomb", "wcsrtombs"]) } } -/* - * An errno-setting function that return in-band errors indicators +/** + * An errno-setting function that return out-of-band errors indicators + * as listed in the CERT standard */ +class OutOfBandErrnoSettingFunctionCert extends Function { + OutOfBandErrnoSettingFunctionCert() { + this.hasGlobalName([ + "ftell", "fgetpos", "fsetpos", "mbrtowc", "mbsrtowcs", "signal", "wcrtomb", "wcsrtombs", + "mbrtoc16", "mbrtoc32", "c16rtomb", "c32rtomb" + ]) + } +} +/** + * An errno-setting function that return in-band errors indicators + */ class InBandErrnoSettingFunction extends ErrnoSettingFunction { InBandErrnoSettingFunction() { this.hasGlobalName([ @@ -32,10 +43,27 @@ class InBandErrnoSettingFunction extends ErrnoSettingFunction { } } -/* - * A assignment expression setting `errno` to 0 +/** + * A call to an `InBandErrnoSettingFunction` + */ +class InBandErrnoSettingFunctionCall extends FunctionCall { + InBandErrnoSettingFunctionCall() { this.getTarget() instanceof InBandErrnoSettingFunction } +} + +/** + * An expression reading the value of `errno` */ +class ErrnoRead extends Expr { + ErrnoRead() { + this = any(MacroInvocation ma | ma.getMacroName() = "errno").getAnExpandedElement() + or + this.(FunctionCall).getTarget().hasName(["perror", "strerror"]) + } +} +/** + * A assignment expression setting `errno` to 0 + */ class ErrnoZeroed extends AssignExpr { ErrnoZeroed() { this.getLValue() = any(MacroInvocation ma | ma.getMacroName() = "errno").getExpr() and @@ -43,23 +71,22 @@ class ErrnoZeroed extends AssignExpr { } } -/* +/** * A guard controlled by a errno comparison */ - abstract class ErrnoGuard extends StmtParent { abstract ControlFlowNode getZeroedSuccessor(); abstract ControlFlowNode getNonZeroedSuccessor(); } -class ErrnoIfGuard extends EqualityOperation, ErrnoGuard { +class ErrnoIfGuard extends ErrnoGuard { ControlStructure i; ErrnoIfGuard() { - this.getAnOperand() = any(MacroInvocation ma | ma.getMacroName() = "errno").getExpr() and - this.getAnOperand().getValue() = "0" and - i.getControllingExpr() = this + i.getControllingExpr() = this and + this.(EqualityOperation).getAnOperand*() = + any(MacroInvocation ma | ma.getMacroName() = "errno").getExpr() } Stmt getThenSuccessor() { @@ -77,11 +104,29 @@ class ErrnoIfGuard extends EqualityOperation, ErrnoGuard { } override ControlFlowNode getZeroedSuccessor() { - if this instanceof EQExpr then result = this.getThenSuccessor() else result = getElseSuccessor() + ( + if this instanceof EQExpr + then result = this.getThenSuccessor() + else result = getElseSuccessor() + ) and + ( + this.(EqualityOperation).getAnOperand().getValue() = "0" + or + this = any(MacroInvocation ma | ma.getMacroName() = "errno").getExpr() + ) } override ControlFlowNode getNonZeroedSuccessor() { - if this instanceof NEExpr then result = this.getThenSuccessor() else result = getElseSuccessor() + ( + if this instanceof NEExpr + then result = this.getThenSuccessor() + else result = getElseSuccessor() + ) and + ( + this.(EqualityOperation).getAnOperand().getValue() = "0" + or + this = any(MacroInvocation ma | ma.getMacroName() = "errno").getExpr() + ) } } diff --git a/c/common/src/codingstandards/c/Expr.qll b/c/common/src/codingstandards/c/Expr.qll index eadc870486..52a9f0719f 100644 --- a/c/common/src/codingstandards/c/Expr.qll +++ b/c/common/src/codingstandards/c/Expr.qll @@ -3,8 +3,19 @@ import cpp /* A full expression as defined in ISO/IEC 9899:2011 6.8 point 4 and Annex C point 1 item 5. */ class FullExpr extends Expr { FullExpr() { - not this.getParent() instanceof Expr and - not exists(Variable v | v.getInitializer().getExpr() = this) + exists(ExprStmt s | this = s.getExpr()) + or + exists(Loop l | this = l.getControllingExpr()) + or + exists(ConditionalStmt s | this = s.getControllingExpr()) + or + exists(ForStmt s | this = s.getUpdate()) + or + exists(ReturnStmt s | this = s.getExpr()) + or + this instanceof AggregateLiteral + or + this = any(Variable v).getInitializer().getExpr() } } diff --git a/c/common/src/codingstandards/c/Extensions.qll b/c/common/src/codingstandards/c/Extensions.qll new file mode 100644 index 0000000000..4f16a1f09a --- /dev/null +++ b/c/common/src/codingstandards/c/Extensions.qll @@ -0,0 +1,188 @@ +import cpp +import codingstandards.cpp.Extensions + +/** + * Common base class for modeling compiler extensions. + */ +abstract class CCompilerExtension extends CompilerExtension { + abstract string getMessage(); +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins +abstract class CConditionalDefineExtension extends CCompilerExtension, PreprocessorIfdef { + string feature; + + CConditionalDefineExtension() { + feature = + [ + "__has_builtin", "__has_constexpr_builtin", "__has_feature", "__has_extension", + "__has_attribute", "__has_declspec_attribute", "__is_identifier", "__has_include", + "__has_include_next", "__has_warning" + ] and + exists(toString().indexOf(feature)) + } + + override string getMessage() { + result = + "Call to builtin function '" + feature + + "' is a compiler extension and is not portable to other compilers." + } +} + +// Reference: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros +class CMacroBasedExtension extends CCompilerExtension, Macro { + CMacroBasedExtension() { + getBody() in [ + "__BASE_FILE__", "__FILE_NAME__", "__COUNTER__", "__INCLUDE_LEVEL__", "_TIMESTAMP__", + "__clang__", "__clang_major__", "__clang_minor__", "__clang_patchlevel__", + "__clang_version__", "__clang_literal_encoding__", "__clang_wide_literal_encoding__" + ] + } + + override string getMessage() { + result = + "Use of builtin macro '" + getBody() + + "' is a compiler extension and is not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes +class CAttributeExtension extends CCompilerExtension, Attribute { + CAttributeExtension() { + getName() in [ + "ext_vector_type", "vector_size", "access", "aligned", "deprecated", "cold", "unused", + "fallthrough", "read_only", "alias" + ] + } + + override string getMessage() { + result = + "Use of attribute '" + getName() + + "' is a compiler extension and is not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html#g_t_005f_005fsync-Builtins +class CFunctionExtension extends CCompilerExtension, FunctionCall { + CFunctionExtension() { + // these must be somewhat broad because of how they vary + // in implementation / naming + getTarget().getName().indexOf("__sync_fetch") = 0 or + getTarget().getName().indexOf("__sync_add") = 0 or + getTarget().getName().indexOf("__sync_sub") = 0 or + getTarget().getName().indexOf("__sync_or") = 0 or + getTarget().getName().indexOf("__sync_and") = 0 or + getTarget().getName().indexOf("__sync_xor") = 0 or + getTarget().getName().indexOf("__sync_nand") = 0 or + getTarget().getName().indexOf("__sync_bool") = 0 or + getTarget().getName().indexOf("__sync_val") = 0 or + getTarget().getName().indexOf("__sync_lock") = 0 or + // the built-in extensions + getTarget().getName().indexOf("__builtin_") = 0 + } + + override string getMessage() { + result = + "Call to builtin function '" + getTarget().getName() + + "' is a compiler extension and is not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Alignment.html#Alignment +class CFunctionLikeExtension extends CCompilerExtension, AlignofExprOperator { + CFunctionLikeExtension() { exists(getValueText().indexOf("__alignof__")) } + + override string getMessage() { + result = "'__alignof__' is a compiler extension and is not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs +class CStmtExprExtension extends CCompilerExtension, StmtExpr { + override string getMessage() { + result = + "Statement expressions are a compiler extension and are not portable to other compilers." + } +} + +// Use of ternary like the following: `int a = 0 ?: 0;` where the +// one of the branches is omitted +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Conditionals.html#Conditionals +class CTerseTernaryExtension extends CCompilerExtension, ConditionalExpr { + CTerseTernaryExtension() { getCondition() = getElse() or getCondition() = getThen() } + + override string getMessage() { + result = + "Ternaries with omitted middle operands are a compiler extension and is not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html#g_t_005f_005fint128 +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Decimal-Float.html#Decimal-Float +class CRealTypeExtensionExtension extends CCompilerExtension, DeclarationEntry { + CRealTypeExtensionExtension() { + getType() instanceof Decimal128Type or + getType() instanceof Decimal32Type or + getType() instanceof Decimal64Type or + getType() instanceof Float128Type + } + + override string getMessage() { + result = "Decimal floats are a compiler extension and are not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html#g_t_005f_005fint128 +class CIntegerTypeExtension extends CCompilerExtension, DeclarationEntry { + CIntegerTypeExtension() { getType() instanceof Int128Type } + + override string getMessage() { + result = "128-bit integers are a compiler extension and are not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Long-Long.html#Long-Long +class CLongLongType extends CCompilerExtension, DeclarationEntry { + CLongLongType() { getType() instanceof LongLongType } + + override string getMessage() { + result = + "Double-Word integers are a compiler extension and are not portable to other compilers." + } +} + +class CZeroLengthArraysExtension extends CCompilerExtension, DeclarationEntry { + CZeroLengthArraysExtension() { getType().(ArrayType).getArraySize() = 0 } + + override string getMessage() { + result = "Zero length arrays are a compiler extension and are not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html#Empty-Structures +class CEmptyStructExtension extends CCompilerExtension, Struct { + CEmptyStructExtension() { not exists(getAMember(_)) } + + override string getMessage() { + result = "Empty structures are a compiler extension and are not portable to other compilers." + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html#Variable-Length +class CVariableLengthArraysExtension extends CCompilerExtension, Field { + CVariableLengthArraysExtension() { + getType() instanceof ArrayType and + not getType().(ArrayType).hasArraySize() and + // Not the final member of the struct, which is allowed to be variably sized + not exists(int lastIndex, Class declaringStruct | + declaringStruct = getDeclaringType() and + lastIndex = count(declaringStruct.getACanonicalMember()) - 1 and + this = declaringStruct.getCanonicalMember(lastIndex) + ) + } + + override string getMessage() { + result = + "Variable length arrays are a compiler extension and are not portable to other compilers." + } +} diff --git a/c/common/src/codingstandards/c/Generic.qll b/c/common/src/codingstandards/c/Generic.qll new file mode 100644 index 0000000000..19d5aad443 --- /dev/null +++ b/c/common/src/codingstandards/c/Generic.qll @@ -0,0 +1,150 @@ +import cpp +import codingstandards.cpp.Macro +import codingstandards.cpp.MatchingParenthesis + +string genericRegexp() { result = "\\b_Generic\\s*\\(\\s*(.+),.*" } + +bindingset[input] +string deparenthesize(string input) { + input = "(" + result + ")" and + result = input.substring(1, input.length() - 1) +} + +class GenericMacro extends Macro { + string ctrlExpr; + + GenericMacro() { ctrlExpr = getBody().regexpCapture(genericRegexp(), 1).trim() } + + string getAParameter() { result = this.(FunctionLikeMacro).getAParameter() } + + string getControllingExprString() { + if exists(string s | s = deparenthesize(ctrlExpr)) + then result = deparenthesize(ctrlExpr).trim() + else result = ctrlExpr + } + + /** + * Whether the controlling expression of the `_Generic` expr in this macro's controlling + * expression refers to one of this macro's parameters. + */ + predicate hasControllingExprFromMacroParameter() { + getControllingExprString().matches(getAParameter()) + } +} + +class GenericMacroString extends string { + GenericMacroString() { this = any(Macro m).getBody() and this.matches("%_Generic%") } +} + +import MatchingParenthesis + +class ParsedGenericMacro extends Macro { + ParsedRoot macroBody; + Parsed genericBody; + string beforeGenericBody; + string afterGenericBody; + + ParsedGenericMacro() { + macroBody.getInputString() = this.getBody() and + exists(ParsedText genericText | + genericText.getText().matches("%_Generic%") and + genericBody = genericText.getParent().getChild(genericText.getChildIdx() + 1) and + genericBody.getRoot() = macroBody + ) and + beforeGenericBody = + textFrom(macroBody.getStartToken(), genericBody.getStartToken().getPrevious()) and + ( + if exists(genericBody.getEndToken().getNext()) + then afterGenericBody = textFrom(genericBody.getEndToken().getNext(), macroBody.getEndToken()) + else afterGenericBody = "" + ) + } + + string getAParameter() { result = this.(FunctionLikeMacro).getAParameter() } + + int getAParsedGenericCommaSeparatorOffset() { + exists(ParsedText text | + text.getParent() = genericBody and + result = text.getStartToken().getStartPos() + text.getText().indexOf(",") + ) + } + + int getAParsedGenericColonSeparatorOffset() { + exists(ParsedText text | + text.getParent() = genericBody and + result = text.getStartToken().getStartPos() + text.getText().indexOf(":") + ) + } + + int getParsedGenericCommaSeparatorOffset(int i) { + result = rank[i](int index | index = getAParsedGenericCommaSeparatorOffset()) + } + + bindingset[start, end] + int getParsedGenericColon(int start, int end) { + result = + min(int offset | + offset = getAParsedGenericColonSeparatorOffset() and + offset >= start and + offset <= end + ) + } + + predicate hasParsedFullSelectionRange(int idx, int start, int end) { + idx = 1 and + start = genericBody.getStartToken().getEndPos() and + end = getParsedGenericCommaSeparatorOffset(idx) + or + not exists(getParsedGenericCommaSeparatorOffset(idx)) and + start = getParsedGenericCommaSeparatorOffset(idx - 1) and + end = genericBody.getEndToken().getStartPos() + or + start = getParsedGenericCommaSeparatorOffset(idx - 1) and + end = getParsedGenericCommaSeparatorOffset(idx) + } + + string getSelectionString(int idx) { + exists(int start, int rawStart, int end | + hasParsedFullSelectionRange(idx, rawStart, end) and + ( + if exists(getParsedGenericColon(rawStart, end)) + then start = getParsedGenericColon(rawStart, end) + else start = rawStart + ) and + result = genericBody.getInputString().substring(start, end) + ) + } + + string getControllingExprString() { result = getSelectionString(1).trim() } + + bindingset[str, word] + private int countWordInString(string word, string str) { + result = + max(int occurrence | + exists(str.regexpFind("\\b" + word + "\\b", occurrence, _)) or occurrence = -1 + | + occurrence + 1 + ) + } + + int expansionsOutsideExpr(string parameter) { + parameter = getAParameter() and + result = + countWordInString(parameter, beforeGenericBody) + + countWordInString(parameter, afterGenericBody) + } + + int expansionsInsideSelection(string parameter, int idx) { + parameter = getAParameter() and + result = countWordInString(parameter, getSelectionString(idx)) + } + + int expansionsInsideControllingExpr(string parameter) { + result = expansionsInsideSelection(parameter, 1) + } + + int expansionsInsideAssociation(string parameter, int idx) { + not idx = 0 and + result = expansionsInsideSelection(parameter, idx + 1) + } +} diff --git a/c/common/src/codingstandards/c/IdentifierLinkage.qll b/c/common/src/codingstandards/c/IdentifierLinkage.qll new file mode 100644 index 0000000000..085ebf5a7b --- /dev/null +++ b/c/common/src/codingstandards/c/IdentifierLinkage.qll @@ -0,0 +1,47 @@ +import cpp + +newtype TIdentifierLinkage = + TIdentifierLinkageExternal() or + TIdentifierLinkageInternal() or + TIdentifierLinkageNone() + +/** + * In C, identifiers have internal linkage, or external linkage, or no linkage (6.2.2.1). + * + * The linkage of an identifier is used to, among other things, determine the storage duration + * and/or lifetime of that identifier. Storage durations and lifetimes are often used to define + * rules in the various coding standards. + */ +class IdentifierLinkage extends TIdentifierLinkage { + predicate isExternal() { this = TIdentifierLinkageExternal() } + + predicate isInternal() { this = TIdentifierLinkageInternal() } + + predicate isNone() { this = TIdentifierLinkageNone() } + + string toString() { + this.isExternal() and result = "external linkage" + or + this.isInternal() and result = "internal linkage" + or + this.isNone() and result = "no linkage" + } +} + +/** + * Determine the linkage of a variable: external, or static, or none. + * + * The linkage of a variable is determined by its scope and storage class. Note that other types of + * identifiers (e.g. functions) may also have linkage, but that behavior is not covered in this + * predicate. + */ +IdentifierLinkage linkageOfVariable(Variable v) { + // 6.2.2.3, file scope identifiers marked static have internal linkage. + v.isTopLevel() and v.isStatic() and result.isInternal() + or + // 6.2.2.4 describes generally non-static file scope identifiers, which have external linkage. + v.isTopLevel() and not v.isStatic() and result.isExternal() + or + // Note: Not all identifiers have linkage, see 6.2.2.6 + not v.isTopLevel() and result.isNone() +} diff --git a/c/common/src/codingstandards/c/Identifiers.qll b/c/common/src/codingstandards/c/Identifiers.qll deleted file mode 100644 index 580cd8cfdd..0000000000 --- a/c/common/src/codingstandards/c/Identifiers.qll +++ /dev/null @@ -1,19 +0,0 @@ -import cpp - -//Identifiers that are candidates for checking uniqueness -class InterestingIdentifiers extends Declaration { - InterestingIdentifiers() { - not this.isFromTemplateInstantiation(_) and - not this.isFromUninstantiatedTemplate(_) and - not this instanceof TemplateParameter and - not this.hasDeclaringType() and - not this instanceof Operator and - not this.hasName("main") and - exists(this.getADeclarationLocation()) - } - - //this definition of significant relies on the number of significant characters for a macro name (C99) - //this is used on macro name comparisons only - //not necessarily against other types of identifiers - string getSignificantNameComparedToMacro() { result = this.getName().prefix(63) } -} diff --git a/c/common/src/codingstandards/c/Objects.qll b/c/common/src/codingstandards/c/Objects.qll new file mode 100644 index 0000000000..9a0206771b --- /dev/null +++ b/c/common/src/codingstandards/c/Objects.qll @@ -0,0 +1,398 @@ +import cpp +import codingstandards.c.StorageDuration +import codingstandards.c.IdentifierLinkage +import semmle.code.cpp.valuenumbering.HashCons +import codingstandards.cpp.Clvalues + +/** + * A libary for handling "Objects" in C. + * + * Objects may be stored in registers or memory, they have an address, a type, a storage duration, + * and a lifetime (which is different than storage duration). Objects which are structs or arrays + * have subobjects, which share the storage duration and lifetime of the parent object. + * + * Note: lifetime analysis is not performed in this library, but is available in + * the module `codingstandards.cpp.lifetimes.LifetimeProfile`. In the future, these libraries could + * be merged for more complete analysis. + * + * To get objects in a project, use the `ObjectIdentity` class which finds the following types of + * objects: + * - global variables + * - local variables + * - literals + * - malloc calls + * - certain temporary object expressions + * + * And direct references to these objects can be found via the member predicate `getAnAccess()`. + * However, much of a project's code will not refer to these objects directly, but rather, refer to + * their subobjects. The class `ObjectIdentity` exposes several member predicates for finding when + * these subobjects are used: + * - `getASubobjectType()` + * - `getASubobjectAccess()` + * - `getASubobjectAddressExpr()` + * + * These methods do not use flow analysis, and will not return a conclusive list of accesses. To + * get better results here, this library should be integrated with flow analysis or the library + * `LifetimeProfile.qll`. + * + * Additionally, subobjects are currently not tracked individually. In the future subobjects could + * be tracked as a root object and an access chain to refer to them. For now, however, finding *any* + * subobject access is sufficient for many analyses. + * + * To get the storage duration, `ObjectIdentity` exposes the member predicate + * `getStorageDuration()` with the following options: + * - `obj.getStorageDuration().isAutomatic()`: Stack objects + * - `obj.getStorageDuration().isStatic()`: Global objects + * - `obj.getStorageDuration().isThread()`: Threadlocal objects + * - `obj.getStorageDuration().isAllocated()`: Dynamic objects + * + * Note that lifetimes are not storage durations. The only lifetime tracking currently implemented + * is `hasTemporaryLifetime()`, which is a subset of automatic storage duration objects, and may + * be filtered out, or selected directly with `TemporaryObjectIdentity`. + */ +final class ObjectIdentity = ObjectIdentityBase; + +/** + * A base class for objects in C, along with the source location where the object can be identified + * in the project code (thus, this class extends `Element`), which may be variable, or may be an + * expression such as a literal or a malloc call. + * + * Extend this class to define a new type of object identity. To create a class which filters the + * set of object identities, users of this library should extend the final subclass + * `ObjectIdentity` instead. + */ +abstract class ObjectIdentityBase extends Element { + /** + * The type of this object. + * + * Note that for allocated objects, this is inferred from the sizeof() statement or the variable + * it is assigned to. + */ + abstract Type getType(); + + /* The storage duration of this object: static, thread, automatic, or allocated. */ + abstract StorageDuration getStorageDuration(); + + /** + * Get the nested objects within this object (members, array element types). + * + * Note that if a struct has a pointer member, the pointer itself is a subobject, but the value + * it points to is not. Therefore `struct { int* x; }` has a subobject of type `int*`, but not + * `int`. + */ + Type getASubObjectType() { result = getADirectSubobjectType*(getType()) } + + /** + * Get expressions which trivially access this object. Does not perform flow analysis. + * + * For dynamically allocated objects, this is a dereference of the malloc call. + */ + abstract Expr getAnAccess(); + + /** + * Get expressions which trivially access this object or a subobject of this object. Does not + * perform flow analysis. + * + * For dynamically allocated objects, this is a dereference of the malloc call or direct access + * of the result of dereferencing the malloc call. + */ + Expr getASubobjectAccess() { result = getASubobjectAccessOf(getAnAccess()) } + + /** + * Get expressions which trivially take the address of this object or a subobject of this object. + * Does not perform flow analysis. + */ + Expr getASubobjectAddressExpr() { + exists(Expr subobject | + subobject = getASubobjectAccess() and + ( + // Holds for address-of expressions. + result = any(AddressOfExpr e | e.getOperand() = subobject) + or + // Holds for array-to-pointer conversions, which evaluate to a usable subobject address. + exists(ArrayToPointerConversion c | c.getExpr() = subobject) and + // Note that `arr[x]` has an array-to-pointer conversion, and returns the `x`th item by + // value, not the address of the `x`th item. Therefore, exclude `arr` if `arr` is part of + // an expression `arr[x]`. + not exists(ArrayExpr a | a.getArrayBase() = subobject) and + result = subobject + ) + ) + } + + /** + * Holds if the object has temporary lifetime. This is not a storage duration, but only objects + * with automatic storage duration have temporary lifetime. + */ + abstract predicate hasTemporaryLifetime(); +} + +/** + * Finds expressions `e.x` or `e[x]` for expression `e`, recursively. Does not resolve pointers. + * + * Note that this does not hold for `e->x` or `e[x]` where `e` is a pointer. + */ +private Expr getASubobjectAccessOf(Expr e) { + result = e + or + result.(DotFieldAccess).getQualifier() = getASubobjectAccessOf(e) + or + result.(ArrayExpr).getArrayBase() = getASubobjectAccessOf(e) and + not result.(ArrayExpr).getArrayBase().getUnspecifiedType() instanceof PointerType +} + +/** + * Find the object types that are embedded within the current type. + * + * For example, a block of memory with type `T[]` has subobjects of type `T`, and a struct with a + * member of `T member;` has a subobject of type `T`. + * + * Note that subobjects may be pointers, but the value they point to is not a subobject. For + * instance, `struct { int* x; }` has a subobject of type `int*`, but not `int`. + */ +Type getADirectSubobjectType(Type type) { + result = type.stripTopLevelSpecifiers().(Struct).getAMember().getADeclarationEntry().getType() + or + result = type.stripTopLevelSpecifiers().(ArrayType).getBaseType() +} + +/** + * An object in memory which may be identified by the variable that holds it. + * + * This may be a local variable, a global variable, or a parameter, etc. However, it cannot be a + * member of a struct or union, as these do not have storage duration. + */ +class VariableObjectIdentity extends Variable, ObjectIdentityBase { + VariableObjectIdentity() { + // Exclude members; member definitions does not allocate storage and thus do not have a storage + // duration. They are therefore not objects. To get the storage duration of members, use one of + // the predicates related to sub objects, e.g. `getASubObjectType()`. + not isMember() + } + + override StorageDuration getStorageDuration() { + // 6.2.4.4, objects declared _Thread_local have thread storage duration. + isThreadLocal() and result.isThread() + or + // 6.2.4.3, Non _ThreadLocal objects with internal or external linkage or declared static have + // static storage duration. + not isThreadLocal() and + (hasLinkage() or isStatic()) and + result.isStatic() + or + // 6.2.4.3, Non _ThreadLocal objects no linkage that are not static have automatic storage + // duration. + not isThreadLocal() and + not hasLinkage() and + not isStatic() and + result.isAutomatic() + } + + override Type getType() { + // Caution here: If we use `Variable.super.getType()` then override resolution is skipped, and + // it uses the base predicate defined as `none()`. By casting this to `Variable` and calling + // `getType()`, all overrides (harmlessly, *including this one*...) are considered, which means + // we defer to the subclasses such as `GlobalVariable` overrides of `getType()`, which is what + // we want. + result = this.(Variable).getType() + } + + /* The storage duration of a variable depends on its linkage. */ + IdentifierLinkage getLinkage() { result = linkageOfVariable(this) } + + predicate hasLinkage() { not getLinkage().isNone() } + + override VariableAccess getAnAccess() { result = Variable.super.getAnAccess() } + + override predicate hasTemporaryLifetime() { + none() // Objects identified by a variable do not have temporary lifetime. + } +} + +/** + * A string literal is an object with static storage duration. + * + * 6.4.5.6, multibyte character sequences initialize an array of static storage duration. + */ +class LiteralObjectIdentity extends Literal, ObjectIdentityBase { + override StorageDuration getStorageDuration() { result.isStatic() } + + override Type getType() { result = Literal.super.getType() } + + override Expr getAnAccess() { result = this } + + override predicate hasTemporaryLifetime() { + none() // String literal objects do not have temporary lifetime. + } +} + +/** + * An object identifiable as a struct or array literal, which is an lvalue that may have static or + * automatic storage duration depending on context. + * + * 6.5.2.5.5, compound literals outside of a function have static storage duration, while literals + * inside a function have automatic storage duration. + */ +class AggregateLiteralObjectIdentity extends AggregateLiteral, ObjectIdentityBase { + override StorageDuration getStorageDuration() { + if exists(getEnclosingFunction()) then result.isAutomatic() else result.isStatic() + } + + override Type getType() { result = AggregateLiteral.super.getType() } + + override Expr getAnAccess() { result = this } + + override predicate hasTemporaryLifetime() { + // Confusing; a struct literal is an lvalue, and therefore does not have temporary lifetime. + none() + } +} + +/** + * An object identified by a call to `malloc`. + * + * Note: the malloc expression returns an address to this object, not the object itself. Therefore, + * `getAnAccess()` returns cases where this malloc result is dereferenced, and not the malloc call + * itself. + * + * Note that the predicates for tracking accesses, subobject accesses, and address expresisons may + * be less reliable as dynamic memory is fundamentally more difficult to track. However, this class + * attempts to give reasonable results. In the future, this could be improved by integrating with + * LifetimeProfile.qll or by integrating flow analysis. + * + * Additionally, the type of this object is inferred based on its size and use. + */ +class AllocatedObjectIdentity extends AllocationExpr, ObjectIdentityBase { + AllocatedObjectIdentity() { + this.(FunctionCall).getTarget().(AllocationFunction).requiresDealloc() + } + + override StorageDuration getStorageDuration() { result.isAllocated() } + + /** Attempt to infer the type of the allocated memory */ + override Type getType() { result = this.getAllocatedElementType() } + + /** Find dereferences of direct aliases of this pointer result. */ + override Expr getAnAccess() { result.(PointerDereferenceExpr).getOperand() = getAnAlias() } + + /** + * Find the following subobject accesses, given a pointer alias `x`: + * - `(*x)` + * - `(*x).y` + * - `(*x)[i]` + * - `x->y` + * - `x[i]` + * - `x->y.z` + * - `x[i].y` + * - all direct accesses (`foo.x`, `foo[i]`) of the above + */ + override Expr getASubobjectAccess() { + result = getASubobjectAccessOf(getAnAccess()) + or + exists(PointerFieldAccess pfa | + pfa.getQualifier() = getASubobjectAddressExpr() and + result = getASubobjectAccessOf(pfa) + ) + or + exists(ArrayExpr arrayExpr | + arrayExpr.getArrayBase() = getASubobjectAddressExpr() and + result = getASubobjectAccessOf(arrayExpr) + ) + } + + /** + * Given a pointer alias `x`, finds `x` itself. Additionally, defers to the default class + * behavior, which finds address-of (`&`) and array-to-pointer conversions of all subobject + * accesses. (See `AllocatedObjectIdentity.getASubobjectAccess()`.) + */ + override Expr getASubobjectAddressExpr() { + result = getAnAlias() + or + result = super.getASubobjectAddressExpr() + } + + /** + * Find an obvious direct reference to the result of a `malloc()` function call. This includes + * the function call itself, but additionally: + * - For `T* x = malloc(...)`, accesses to variable `x` are likely aliases of the malloc result + * - For `(expr) = malloc(...)` future lexically identical uses of `expr` are likely aliases of + * the malloc result. + * + * This is used so that member predicates such as `getAnAccess()`, `getASubobjectAccess()` can + * find cases such as: + * + * ```c + * int *x = malloc(sizeof(int)); + * return *x; // accesses the malloc result + * ``` + */ + Expr getAnAlias() { + result = this + or + exists(AssignExpr assignExpr | + assignExpr.getRValue() = this and + hashCons(result) = hashCons(assignExpr.getLValue()) + ) + or + exists(Variable v | + v.getInitializer().getExpr() = this and + result = v.getAnAccess() + ) + } + + override predicate hasTemporaryLifetime() { + none() // Allocated objects do not have "temporary lifetime." + } +} + +/** + * A struct or union type that contains an array type, used to find objects with temporary + * lifetime. + */ +private class StructOrUnionTypeWithArrayField extends Struct { + StructOrUnionTypeWithArrayField() { + this.getAField().getUnspecifiedType() instanceof ArrayType + or + // nested struct or union containing an array type + this.getAField().getUnspecifiedType().(Struct) instanceof StructOrUnionTypeWithArrayField + } +} + +/** + * 6.2.4.7, A non-lvalue expression with struct or or union type that has a field member of array + * type, refers to an object with automatic storage duration (and has temporary lifetime). + * + * The spec uses the lanugage "refers to." This is likely intended to mean that the expression + * `foo().x` does not create a new temporary object, but rather "refers to" the temporary object + * storing the value of the expression `foo()`. + * + * Separate this predicate to avoid non-monotonic recursion (`C() { not exists(C c | ... ) }`). + */ +private class TemporaryObjectIdentityExpr extends Expr { + TemporaryObjectIdentityExpr() { + getType() instanceof StructOrUnionTypeWithArrayField and + not isCLValue(this) + } +} + +/** + * 6.2.4.7, A non-lvalue expression with struct or or union type that has a field member of array + * type, is an object with automatic storage duration (and has temporary lifetime). + */ +class TemporaryObjectIdentity extends ObjectIdentityBase instanceof TemporaryObjectIdentityExpr { + TemporaryObjectIdentity() { + // See comment in `TemporaryObjectIdentityExpr` for why we check `getASubobjectAccess()` here. + not exists(TemporaryObjectIdentityExpr parent | + this = getASubobjectAccessOf(parent) and + not this = parent + ) + } + + override StorageDuration getStorageDuration() { result.isAutomatic() } + + override Type getType() { result = this.(Expr).getType() } + + override Expr getAnAccess() { result = this } + + override predicate hasTemporaryLifetime() { any() } +} diff --git a/c/common/src/codingstandards/c/Ordering.qll b/c/common/src/codingstandards/c/Ordering.qll index 955cba5e50..575dc6f3fd 100644 --- a/c/common/src/codingstandards/c/Ordering.qll +++ b/c/common/src/codingstandards/c/Ordering.qll @@ -13,7 +13,6 @@ module Ordering { /** * Holds if `e1` is sequenced before `e2` as defined by Annex C in ISO/IEC 9899:2011 * This limits to expression and we do not consider the sequence points that are not amenable to modelling: - * - after a full declarator as described in 6.7.6 point 3. * - before a library function returns (see 7.1.4 point 3). * - after the actions associated with each formatted I/O function conversion specifier (see 7.21.6 point 1 & 7.29.2 point 1). * - between the expr before and after a call to a comparison function, @@ -28,36 +27,36 @@ module Ordering { // before the actual call. exists(Call call | ( - call.getAnArgument() = e1 + call.getAnArgument().getAChild*() = e1 or // Postfix expression designating the called function // We current only handle call through function pointers because the postfix expression // of regular function calls is not available. That is, identifying `f` in `f(...)` - call.(ExprCall).getExpr() = e1 + call.(ExprCall).getExpr().getAChild*() = e1 ) and call.getTarget() = e2.getEnclosingFunction() ) or - // 6.5.13 point 4 & 6.5.14 point 4 - The operators guarantee left-to-righ evaluation and there is + // 6.5.13 point 4 & 6.5.14 point 4 - The operators guarantee left-to-right evaluation and there is // a sequence point between the first and second operand if the latter is evaluated. exists(BinaryLogicalOperation blop | blop instanceof LogicalAndExpr or blop instanceof LogicalOrExpr | - blop.getLeftOperand() = e1 and blop.getRightOperand() = e2 + blop.getLeftOperand().getAChild*() = e1 and blop.getRightOperand().getAChild*() = e2 ) or - // 6.5.17 point 2 - There is a sequence pointt between the left operand and the right operand. + // 6.5.17 point 2 - There is a sequence point between the left operand and the right operand. exists(CommaExpr ce, Expr lhs, Expr rhs | lhs = ce.getLeftOperand() and rhs = ce.getRightOperand() | - lhs = e1.getParent*() and rhs = e2.getParent*() + lhs.getAChild*() = e1 and rhs.getAChild*() = e2 ) or // 6.5.15 point 4 - There is a sequence point between the first operand and the evaluation of the second or third. exists(ConditionalExpr cond | - cond.getCondition() = e1 and - (cond.getThen() = e2 or cond.getElse() = e2) + cond.getCondition().getAChild*() = e1 and + (cond.getThen().getAChild*() = e2 or cond.getElse().getAChild*() = e2) ) or // Between the evaluation of a full expression and the next to be evaluated full expression. @@ -68,6 +67,24 @@ module Ordering { // The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. // See 6.5.16 e2.(Assignment).getAnOperand().getAChild*() = e1 + or + // There is a sequence point after a full declarator as described in 6.7.6 point 3. + exists(DeclStmt declStmt, int i, int j | i < j | + declStmt + .getDeclarationEntry(i) + .(VariableDeclarationEntry) + .getVariable() + .getInitializer() + .getExpr() + .getAChild*() = e1 and + declStmt + .getDeclarationEntry(j) + .(VariableDeclarationEntry) + .getVariable() + .getInitializer() + .getExpr() + .getAChild*() = e2 + ) ) } diff --git a/c/common/src/codingstandards/c/OutOfBounds.qll b/c/common/src/codingstandards/c/OutOfBounds.qll new file mode 100644 index 0000000000..1f1680f56c --- /dev/null +++ b/c/common/src/codingstandards/c/OutOfBounds.qll @@ -0,0 +1,1351 @@ +/** + * This module provides classes and predicates for analyzing the size of buffers + * or objects from their base or a byte-offset, and identifying the potential for + * expressions accessing those buffers to overflow. + */ + +import cpp +import codingstandards.cpp.types.Pointers +import codingstandards.c.Variable +import codingstandards.cpp.Allocations +import codingstandards.cpp.Overflow +import codingstandards.cpp.PossiblyUnsafeStringOperation +import codingstandards.cpp.SimpleRangeAnalysisCustomizations +private import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +module OOB { + /** + * Holds if `result` is either `name` or a string matching a pattern such as + * `__builtin_*name*_chk` or similar. This predicate exists to model internal functions + * such as `__builtin___memcpy_chk` under a common `memcpy` name in the table. + */ + bindingset[name, result] + string getNameOrInternalName(string name) { + result.regexpMatch("^(?:__.*_+)?" + name + "(?:_[^s].*)?$") + } + + /** + * MISRA-C Rule 21.17 function table of names and parameter indices + * which covers functions from that rely on null-terminated strings. + * + * This table is a subset of `libraryFunctionNameParamTable`. + * + * Note: These functions do not share a common semantic pattern of source and destination + * parameters with the other functions explicitly defined in `libraryFunctionNameParamTable`, + * although they do share a common issue of parsing non-null-terminated strings. + * The `SimpleStringLibraryFunction` base class provides an appropriate + * interface for analyzing the functions in the below table. + */ + private Function libraryFunctionNameParamTableSimpleString( + string name, int dst, int src, int src_sz, int dst_sz + ) { + result.getName() = getNameOrInternalName(name) and + src_sz = -1 and + dst_sz = -1 and + ( + name = "strcat" and + dst = 0 and + src = 1 + or + name = "strchr" and + dst = -1 and + src = 0 + or + name = ["strcmp", "strcoll"] and + dst = -1 and + src = [0, 1] + or + name = "strcpy" and + dst = 0 and + src = 1 + or + name = "strcspn" and + dst = -1 and + src = [0, 1] + or + name = "strlen" and + dst = -1 and + src = 0 + or + name = "strpbrk" and + dst = -1 and + src = [0, 1] + or + name = "strrchr" and + dst = -1 and + src = 0 + or + name = "strspn" and + dst = -1 and + src = [0, 1] + or + name = "strstr" and + dst = -1 and + src = [0, 1] + or + // do not specify a src and dst to avoid buffer size assumptions + name = ["strtok", "strtok_r"] and + dst = -1 and + src = [0, 1] + ) + } + + /** + * A relation of the indices of buffer and size parameters of standard library functions + * which are defined in rules CERT ARR38-C and MISRA-C rules 21.17 and 21.18. + */ + private Function libraryFunctionNameParamTable( + string name, int dst, int src, int src_sz, int dst_sz + ) { + result = libraryFunctionNameParamTableSimpleString(name, dst, src, src_sz, dst_sz) + or + result.getName() = getNameOrInternalName(name) and + ( + name = ["fgets", "fgetws"] and + dst = 0 and + src = -1 and + src_sz = -1 and + dst_sz = 1 + or + name = ["mbstowcs", "wcstombs"] and + dst = 0 and + src = 1 and + src_sz = -1 and + dst_sz = 2 + or + name = ["mbrtoc16", "mbrtoc32"] and + dst = 0 and + src = 1 and + src_sz = 2 and + dst_sz = -1 + or + name = ["mbsrtowcs", "wcsrtombs"] and + dst = 0 and + src = 1 and + src_sz = -1 and + dst_sz = 2 + or + name = ["mbtowc", "mbrtowc"] and + dst = 0 and + src = 1 and + src_sz = 2 and + dst_sz = -1 + or + name = ["mblen", "mbrlen"] and + dst = -1 and + src = 0 and + src_sz = 1 and + dst_sz = -1 + or + name = ["memchr", "wmemchr"] and + dst = -1 and + src = 0 and + src_sz = 2 and + dst_sz = -1 + or + name = ["memset", "wmemset"] and + dst = 0 and + src = -1 and + src_sz = -1 and + dst_sz = 2 + or + name = ["strftime", "wcsftime"] and + dst = 0 and + src = -1 and + src_sz = -1 and + dst_sz = 1 + or + name = ["strxfrm", "wcsxfrm"] and + dst = 0 and + src = 1 and + src_sz = -1 and + dst_sz = 2 + or + name = ["strncat", "wcsncat"] and + dst = 0 and + src = 1 and + src_sz = 2 and + dst_sz = -1 + or + name = ["snprintf", "vsnprintf", "swprintf", "vswprintf"] and + dst = 0 and + src = -1 and + src_sz = -1 and + dst_sz = 1 + or + name = "setvbuf" and + dst = -1 and + src = 1 and + src_sz = 3 and + dst_sz = -1 + or + name = ["memcpy", "wmemcpy", "memmove", "wmemmove", "memcmp", "wmemcmp", "strncmp", "wcsncmp"] and + dst = 0 and + src = 1 and + src_sz = 2 and + dst_sz = 2 + or + name = ["strncpy", "wcsncpy"] and + dst = 0 and + src = 1 and + src_sz = -1 and + dst_sz = 2 + or + name = "qsort" and + dst = 0 and + src = -1 and + src_sz = -1 and + dst_sz = 1 + or + name = "bsearch" and + dst = -1 and + src = 1 and + src_sz = -1 and + dst_sz = 2 + or + name = "fread" and + dst = 0 and + src = -1 and + src_sz = -1 and + dst_sz = 2 + or + name = "fwrite" and + dst = -1 and + src = 0 and + src_sz = 2 and + dst_sz = -1 + ) + } + + /** + * A library function that accesses one or more buffers supplied via arguments. + */ + class BufferAccessLibraryFunction extends Function { + BufferAccessLibraryFunction() { this = libraryFunctionNameParamTable(_, _, _, _, _) } + + /** + * Returns the indices of parameters that are a destination buffer. + */ + int getWriteParamIndex() { + this = libraryFunctionNameParamTable(_, result, _, _, _) and + result >= 0 + } + + /** + * Returns the indices of parameters that are a source buffer. + */ + int getReadParamIndex() { + this = libraryFunctionNameParamTable(_, _, result, _, _) and + result >= 0 + } + + /** + * Returns the index of the parameter that is the source buffer size. + */ + int getReadSizeParamIndex() { + this = libraryFunctionNameParamTable(_, _, _, result, _) and + result >= 0 + } + + /** + * Returns the index of the parameter that is the destination buffer size. + */ + int getWriteSizeParamIndex() { + this = libraryFunctionNameParamTable(_, _, _, _, result) and + result >= 0 + } + + /** + * Gets a parameter than is a source (read) buffer. + */ + Parameter getReadParam() { result = this.getParameter(this.getReadParamIndex()) } + + /** + * Gets a parameter than is a destination (write) buffer. + */ + Parameter getWriteParam() { result = this.getParameter(this.getWriteParamIndex()) } + + /** + * Gets a parameter than is a source (read) buffer size. + */ + Parameter getReadSizeParam() { result = this.getParameter(this.getReadSizeParamIndex()) } + + /** + * Gets a parameter than is a destination (write) buffer size. + */ + Parameter getWriteSizeParam() { result = this.getParameter(this.getWriteSizeParamIndex()) } + + /** + * Gets the size of an element in the destination buffer class + */ + int getWriteParamElementSize(Parameter p) { + p = this.getWriteParam() and + p.getType().getUnspecifiedType().(DerivedType).getBaseType().getSize().maximum(1) = result + } + + /** + * Gets the size of an element in the source buffer class + */ + int getReadParamElementSize(Parameter p) { + p = this.getReadParam() and + p.getType().getUnspecifiedType().(DerivedType).getBaseType().getSize().maximum(1) = result + } + + /** + * Holds if `i` is the index of a parameter of this function that requires arguments to be null-terminated. + * This predicate should be overriden by extending classes to specify null-terminated parameters, if necessary. + */ + predicate getANullTerminatedParameterIndex(int i) { + // by default, require null-terminated parameters for src but + // only if the type of src is a plain char pointer or wchar_t. + this.getReadParamIndex() = i and + exists(Type baseType | + baseType = this.getReadParam().getUnspecifiedType().(PointerType).getBaseType() and + ( + baseType.getUnspecifiedType() instanceof PlainCharType or + baseType.getUnspecifiedType() instanceof Wchar_t + ) + ) + } + + /** + * Holds if `i` is the index of a parameter of this function that is a size multiplier. + * This predicate should be overriden by extending classes to specify size multiplier parameters, if necessary. + */ + predicate getASizeMultParameterIndex(int i) { + // by default, there is no size multiplier parameter + // exceptions: fread, fwrite, bsearch, qsort + none() + } + + /** + * Holds if `i` is the index of a parameter of this function that expects an element count rather than buffer size argument. + * This predicate should be overriden by extending classes to specify length parameters, if necessary. + */ + predicate getALengthParameterIndex(int i) { + // by default, size parameters do not exclude the size of a null terminator + none() + } + + /** + * Holds if the read or write parameter at index `i` is allowed to be null. + * This predicate should be overriden by extending classes to specify permissibly null parameters, if necessary. + */ + predicate getAPermissiblyNullParameterIndex(int i) { + // by default, pointer parameters are not allowed to be null + none() + } + } + + /** + * A library function that accesses one or more string buffers and has no + * additional parameters for specifying the size of the buffers. + */ + class SimpleStringLibraryFunction extends BufferAccessLibraryFunction { + SimpleStringLibraryFunction() { + this = libraryFunctionNameParamTableSimpleString(_, _, _, -1, -1) + } + + override predicate getANullTerminatedParameterIndex(int i) { + // by default, require null-terminated parameters for src but + // only if the type of src is a plain char pointer. + this.getReadParamIndex() = i and + this.getReadParam().getUnspecifiedType().(PointerType).getBaseType().getUnspecifiedType() + instanceof PlainCharType + } + } + + /** + * A `BufferAccessLibraryFunction` that performs string concatenation. + */ + abstract class StringConcatenationFunctionLibraryFunction extends BufferAccessLibraryFunction { + override predicate getANullTerminatedParameterIndex(int i) { + // `strcat` and variants require null-terminated params for both src and dst + i = [0, 1] + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `strcat` + */ + class StrcatLibraryFunction extends StringConcatenationFunctionLibraryFunction { + StrcatLibraryFunction() { this.getName() = getNameOrInternalName("strcat") } + } + + /** + * A `BufferAccessLibraryFunction` modelling `strncat` or `wcsncat` + */ + class StrncatLibraryFunction extends StringConcatenationFunctionLibraryFunction { + StrncatLibraryFunction() { this.getName() = getNameOrInternalName(["strncat", "wcsncat"]) } + + override predicate getALengthParameterIndex(int i) { + // `strncat` and `wcsncat` exclude the size of a null terminator + i = 2 + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `strncpy` + */ + class StrncpyLibraryFunction extends StringConcatenationFunctionLibraryFunction { + StrncpyLibraryFunction() { this.getName() = getNameOrInternalName("strncpy") } + + override predicate getANullTerminatedParameterIndex(int i) { + // `strncpy` does not require null-terminated parameters + none() + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `strncmp` + */ + class StrncmpLibraryFunction extends BufferAccessLibraryFunction { + StrncmpLibraryFunction() { this.getName() = getNameOrInternalName("strncmp") } + + override predicate getANullTerminatedParameterIndex(int i) { + // `strncmp` does not require null-terminated parameters + none() + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `mbtowc` and `mbrtowc` + */ + class MbtowcLibraryFunction extends BufferAccessLibraryFunction { + MbtowcLibraryFunction() { this.getName() = getNameOrInternalName(["mbtowc", "mbrtowc"]) } + + override predicate getAPermissiblyNullParameterIndex(int i) { + // `mbtowc` requires null-terminated parameters for both src and dst + i = [0, 1] + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `mblen` and `mbrlen` + */ + class MblenLibraryFunction extends BufferAccessLibraryFunction { + MblenLibraryFunction() { this.getName() = getNameOrInternalName(["mblen", "mbrlen"]) } + + override predicate getAPermissiblyNullParameterIndex(int i) { i = 0 } + } + + /** + * A `BufferAccessLibraryFunction` modelling `setvbuf` + */ + class SetvbufLibraryFunction extends BufferAccessLibraryFunction { + SetvbufLibraryFunction() { this.getName() = getNameOrInternalName("setvbuf") } + + override predicate getAPermissiblyNullParameterIndex(int i) { i = 1 } + + override predicate getANullTerminatedParameterIndex(int i) { + // `setvbuf` does not require a null-terminated buffer + none() + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `snprintf`, `vsnprintf`, `swprintf`, and `vswprintf`. + * This class overrides the `getANullTerminatedParameterIndex` predicate to include the `format` parameter. + */ + class PrintfLibraryFunction extends BufferAccessLibraryFunction { + PrintfLibraryFunction() { + this.getName() = getNameOrInternalName(["snprintf", "vsnprintf", "swprintf", "vswprintf"]) + } + + override predicate getANullTerminatedParameterIndex(int i) { + // `snprintf` and variants require a null-terminated format string + i = 2 + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `fread` and `fwrite`. + */ + class FreadFwriteLibraryFunction extends BufferAccessLibraryFunction { + FreadFwriteLibraryFunction() { this.getName() = getNameOrInternalName(["fread", "fwrite"]) } + + override predicate getASizeMultParameterIndex(int i) { + // `fread` and `fwrite` have a size multiplier parameter + i = 1 + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `bsearch` + */ + class BsearchLibraryFunction extends BufferAccessLibraryFunction { + BsearchLibraryFunction() { this.getName() = getNameOrInternalName("bsearch") } + + override predicate getASizeMultParameterIndex(int i) { + // `bsearch` has a size multiplier parameter + i = 3 + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `qsort` + */ + class QsortLibraryFunction extends BufferAccessLibraryFunction { + QsortLibraryFunction() { this.getName() = getNameOrInternalName("qsort") } + + override predicate getASizeMultParameterIndex(int i) { + // `qsort` has a size multiplier parameter + i = 2 + } + } + + /** + * A `BufferAccessLibraryFunction` modelling `strtok` + */ + class StrtokLibraryFunction extends BufferAccessLibraryFunction { + StrtokLibraryFunction() { this.getName() = getNameOrInternalName(["strtok", "strtok_r"]) } + + override predicate getAPermissiblyNullParameterIndex(int i) { + // `strtok` does not require a non-null `str` parameter + i = 0 + } + } + + /** + * An construction of a pointer to a buffer. + */ + abstract class BufferAccess extends Expr { + abstract predicate hasABuffer(Expr buffer, Expr size, int sizeMult); + + Expr getARelevantExpr() { + hasABuffer(result, _, _) + or + hasABuffer(_, result, _) + } + } + + class PointerArithmeticBufferAccess extends BufferAccess instanceof PointerArithmeticExpr { + override predicate hasABuffer(Expr buffer, Expr size, int sizeMult) { + buffer = this.(PointerArithmeticExpr).getPointer() and + size = this.(PointerArithmeticExpr).getOperand() and + sizeMult = + buffer.getType().getUnspecifiedType().(DerivedType).getBaseType().getSize().maximum(1) + } + } + + class ArrayBufferAccess extends BufferAccess, ArrayExpr { + override predicate hasABuffer(Expr buffer, Expr size, int sizeMult) { + buffer = this.getArrayBase() and + size = this.getArrayOffset() and + sizeMult = + buffer.getType().getUnspecifiedType().(DerivedType).getBaseType().getSize().maximum(1) + } + } + + /** + * A `FunctionCall` to a `BufferAccessLibraryFunction` that provides predicates for + * reasoning about buffer overflow and other buffer access violations. + */ + class BufferAccessLibraryFunctionCall extends FunctionCall, BufferAccess { + BufferAccessLibraryFunctionCall() { this.getTarget() instanceof BufferAccessLibraryFunction } + + override predicate hasABuffer(Expr buffer, Expr size, int sizeMult) { + buffer = this.getWriteArg() and + size = this.getWriteSizeArg(sizeMult) + or + buffer = this.getReadArg() and + size = this.getReadSizeArg(sizeMult) + } + + Expr getReadArg() { + result = this.getArgument(this.getTarget().(BufferAccessLibraryFunction).getReadParamIndex()) + } + + Expr getWriteArg() { + result = this.getArgument(this.getTarget().(BufferAccessLibraryFunction).getWriteParamIndex()) + } + + Expr getReadSizeArg(int mult) { + result = + this.getArgument(this.getTarget().(BufferAccessLibraryFunction).getReadSizeParamIndex()) and + getReadSizeArgMult() = mult + } + + Expr getWriteSizeArg(int mult) { + result = + this.getArgument(this.getTarget().(BufferAccessLibraryFunction).getWriteSizeParamIndex()) and + getWriteSizeArgMult() = mult + } + + int getReadSizeArgMult() { + result = + this.getTarget().(BufferAccessLibraryFunction).getReadParamElementSize(_) * + getSizeMultArgValue() + } + + int getWriteSizeArgMult() { + result = + this.getTarget().(BufferAccessLibraryFunction).getWriteParamElementSize(_) * + getSizeMultArgValue() + } + + int getSizeMultArgValue() { + // Note: This predicate currently expects the size multiplier argument to be a constant. + // This implementation could be improved with range-analysis or data-flow to determine the argument value. + exists(int i | + this.getTarget().(BufferAccessLibraryFunction).getASizeMultParameterIndex(i) and + result = this.getArgument(i).getValue().toInt() + ) + or + not this.getTarget().(BufferAccessLibraryFunction).getASizeMultParameterIndex(_) and + result = 1 + } + } + + /** + * A `FunctionCall` to a `SimpleStringLibraryFunction` + */ + class SimpleStringLibraryFunctionCall extends BufferAccessLibraryFunctionCall { + SimpleStringLibraryFunctionCall() { this.getTarget() instanceof SimpleStringLibraryFunction } + } + + bindingset[dest] + private Expr getSourceConstantExpr(Expr dest) { + exists(result.getValue().toInt()) and + DataFlow::localExprFlow(result, dest) + } + + /** + * Gets the smallest of the upper bound of `e` or the largest source value (i.e. "stated value") that flows to `e`. + * Because range-analysis can over-widen bounds, take the minimum of range analysis and data-flow sources. + * + * If there is no source value that flows to `e`, this predicate does not hold. + * + * This predicate, if `e` is the size argument to malloc, would return `20` for the following example: + * ``` + * size_t sz = condition ? 10 : 20; + * malloc(sz); + * ``` + */ + private int getMaxStatedValue(Expr e) { + result = upperBound(e).minimum(max(getSourceConstantExpr(e).getValue().toInt())) + } + + /** + * Gets the smallest of the upper bound of `e` or the smallest source value (i.e. "stated value") that flows to `e`. + * Because range-analysis can over-widen bounds, take the minimum of range analysis and data-flow sources. + * + * If there is no source value that flows to `e`, this predicate does not hold. + * + * This predicate, if `e` is the size argument to malloc, would return `10` for the following example: + * ``` + * size_t sz = condition ? 10 : 20; + * malloc(sz); + * ``` + */ + bindingset[e] + private int getMinStatedValue(Expr e) { + result = upperBound(e).minimum(min(getSourceConstantExpr(e).getValue().toInt())) + } + + /** + * A class for reasoning about the offset of a variable from the original value flowing to it + * as a result of arithmetic or pointer arithmetic expressions. + */ + bindingset[expr] + private int getArithmeticOffsetValue(Expr expr, Expr base) { + result = getMinStatedValue(expr.(PointerArithmeticExpr).getOperand()) and + base = expr.(PointerArithmeticExpr).getPointer() + or + // &(array[index]) expressions + result = + getMinStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand()) and + base = expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getPointer() + or + result = getMinStatedValue(expr.(AddExpr).getRightOperand()) and + base = expr.(AddExpr).getLeftOperand() + or + result = -getMinStatedValue(expr.(SubExpr).getRightOperand()) and + base = expr.(SubExpr).getLeftOperand() + or + expr instanceof IncrementOperation and + result = 1 and + base = expr.(IncrementOperation).getOperand() + or + expr instanceof DecrementOperation and + result = -1 and + base = expr.(DecrementOperation).getOperand() + or + // fall-back if `expr` is not an arithmetic or pointer arithmetic expression + not expr instanceof PointerArithmeticExpr and + not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and + not expr instanceof AddExpr and + not expr instanceof SubExpr and + not expr instanceof IncrementOperation and + not expr instanceof DecrementOperation and + base = expr and + result = 0 + } + + private int constOrZero(Expr e) { + result = e.getValue().toInt() + or + not exists(e.getValue().toInt()) and result = 0 + } + + abstract class PointerToObjectSource extends Expr { + /** + * Gets the expression that points to the object. + */ + abstract Expr getPointer(); + + /** + * Gets the expression, if any, that defines the size of the object. + */ + abstract Expr getSizeExpr(); + + /** + * Gets the size of the object, if it is statically known. + */ + abstract int getFixedSize(); + + /** + * Holds if the object is not null-terminated. + */ + abstract predicate isNotNullTerminated(); + } + + private class DynamicAllocationSource extends PointerToObjectSource instanceof AllocationExpr, + FunctionCall + { + DynamicAllocationSource() { + // exclude OperatorNewAllocationFunction to only deal with raw malloc-style calls, + // which do not apply a multiple to the size of the allocation passed to them. + not this.(FunctionCall).getTarget() instanceof OperatorNewAllocationFunction + } + + override Expr getPointer() { result = this } + + override Expr getSizeExpr() { + // AllocationExpr may sometimes return a subexpression of the size expression + // in order to separate the size from a sizeof expression in a MulExpr. + exists(AllocationFunction f | + f = this.(FunctionCall).getTarget() and + result = this.(FunctionCall).getArgument(f.getSizeArg()) + ) + } + + /** + * Returns either `getSizeExpr()`, or, if a value assigned to a variable flows + * to `getSizeExpr()` or an `AddExpr` within it, the value assigned to that variable. + * + * If an `AddExpr` exists in the value assignment or `getSizeExpr()`, and that `AddExpr` + * has a constant right operand, then value of that operand is `offset`. Otherwise, `offset` is 0. + * + * If no `AddExpr` exists, `base = result`. Otherwise, `base` is the left operand of the `AddExpr`. + * If the left operand of the `AddExpr` comes from a variable assignment, `base` is assigned value. + * + * This predicate serves as a rough heuristic for cases such as the following: + * 1. `size_t sz = strlen(src) + 1; malloc(sz);` + * 2. `size_t sz = strlen(src); malloc(sz + 1);` + */ + Expr getSizeExprSource(Expr base, int offset) { + if this.getSizeExpr() instanceof AddExpr + then + exists(AddExpr ae | + exists(Variable v | + // case 1: variable access + const in the size expression + this.getSizeExpr() = ae and + result = v.getAnAssignedValue() and + base = ae.getLeftOperand() and + offset = constOrZero(ae.getRightOperand()) and + DataFlow::localExprFlow(result, base) + or + // case 2: expr + const in the variable assignment + v.getAnAssignedValue() = ae and + result = ae and + base = ae.getLeftOperand() and + offset = constOrZero(ae.getRightOperand()) and + DataFlow::localExprFlow(result, this.getSizeExpr()) + ) + or + // case 3: function call + const + result = ae and + this.getSizeExpr() = ae and + ae.getLeftOperand() = base and + ae.getLeftOperand() instanceof FunctionCall and + offset = constOrZero(ae.getRightOperand()) + ) + else ( + offset = 0 and + // case 3: a variable is read in the size expression + // if the VariableAccess does not have a computable constant value, + // the source node could still be useful for data-flow and GVN comparisons + if this.getSizeExpr() instanceof VariableAccess + then + exists(Variable v | + v = this.getSizeExpr().(VariableAccess).getTarget() and + not v instanceof Field and + DataFlow::localExprFlow(v.getAnAssignedValue(), base) and + result = base + ) + else ( + // Case 4: no variable access in the size expression + // This case is equivalent to getSizeExpr. + base = this.getSizeExpr() and + result = base + ) + ) + } + + override int getFixedSize() { result = getMaxStatedValue(getSizeExpr()) } + + override predicate isNotNullTerminated() { none() } + } + + /** + * A `PointerToObjectSource` which is an `AddressOfExpr` to a variable + * that is not a field or pointer type. + */ + private class AddressOfExprSource extends PointerToObjectSource instanceof AddressOfExpr { + AddressOfExprSource() { + exists(Variable v | + v = this.getOperand().(VariableAccess).getTarget() and + not v.getUnderlyingType() instanceof PointerType and + not v instanceof Field + ) + } + + override Expr getPointer() { result = this } + + override Expr getSizeExpr() { none() } + + override int getFixedSize() { + result = min(this.(AddressOfExpr).getOperand().getType().getSize()) + } + + override predicate isNotNullTerminated() { none() } + } + + /** + * A `PointerToObjectSource` which is a `VariableAccess` to a static buffer + */ + private class StaticBufferAccessSource extends PointerToObjectSource instanceof VariableAccess { + StaticBufferAccessSource() { + not this.getTarget() instanceof Field and + not this.getTarget().getUnspecifiedType() instanceof PointerType and + this.getTarget().getUnderlyingType().(ArrayType).getSize() > 0 + } + + override Expr getPointer() { result = this } + + override Expr getSizeExpr() { none() } + + override int getFixedSize() { + result = this.(VariableAccess).getTarget().getUnderlyingType().(ArrayType).getSize() + } + + override predicate isNotNullTerminated() { + // StringLiteral::getOriginalLength uses Expr::getValue, which implicitly truncates string literal + // values to the length fitting the buffer they are assigned to, thus breaking the 'obvious' check. + // Note: `CharArrayInitializedWithStringLiteral` falsely reports the string literal length in certain cases + // (e.g. when the string literal contains escape characters or on certain compilers), resulting in false-negatives + exists(CharArrayInitializedWithStringLiteral init | + init = this.(VariableAccess).getTarget().getInitializer().getExpr() and + init.getStringLiteralLength() + 1 > init.getContainerLength() + ) + or + // if the buffer is not initialized and does not have any memset call zeroing it, it is not null-terminated. + // note that this heuristic does not evaluate the order of the memset calls made and whether they dominate + // any use of the buffer by functions requiring it to be null-terminated. + ( + this.(VariableAccess).getTarget().getUnspecifiedType().(ArrayType).getBaseType() instanceof + PlainCharType + or + this.(VariableAccess).getTarget().getUnspecifiedType().(ArrayType).getBaseType() instanceof + Wchar_t + ) and + not this.(VariableAccess).getTarget() instanceof GlobalVariable and + not exists(this.(VariableAccess).getTarget().getInitializer()) and + // exclude any BufferAccessLibraryFunction that writes to the buffer and does not require + // a null-terminated buffer argument for its write argument + not exists( + BufferAccessLibraryFunctionCall fc, BufferAccessLibraryFunction f, int writeParamIndex + | + f = fc.getTarget() and + writeParamIndex = f.getWriteParamIndex() and + not f.getANullTerminatedParameterIndex(writeParamIndex) and + fc.getArgument(writeParamIndex) = this.(VariableAccess).getTarget().getAnAccess() + ) and + // exclude any buffers that have an assignment, deref, or array expr with a zero constant + // note: heuristically implemented using getAChild*() + not exists(AssignExpr assign | + assign.getRValue().getValue().toInt() = 0 and + assign.getLValue().getAChild*() = this.(VariableAccess).getTarget().getAnAccess() + ) + // note: the case of initializers that are not string literals and non-zero constants is not handled here. + // e.g. char buf[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; (not null-terminated) + // char buf[10] = { 1 }; (not null-terminated) + } + } + + /** + * A `PointerToObjectSource` which is a string literal that is not + * part of an variable initializer (to deduplicate `StaticBufferAccessSource`) + */ + private class StringLiteralSource extends PointerToObjectSource instanceof StringLiteral { + StringLiteralSource() { not this instanceof CharArrayInitializedWithStringLiteral } + + override Expr getPointer() { result = this } + + override Expr getSizeExpr() { none() } + + override int getFixedSize() { + // (length of the string literal + null terminator) * (size of the base type) + result = + this.(StringLiteral).getOriginalLength() * + this.(StringLiteral).getUnderlyingType().(DerivedType).getBaseType().getSize() + } + + override predicate isNotNullTerminated() { none() } + } + + private module PointerToObjectSourceOrSizeToBufferAccessFunctionConfig implements + DataFlow::ConfigSig + { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof PointerToObjectSource + or + exists(PointerToObjectSource ptr | + source.asExpr() = ptr.getSizeExpr() or + source.asExpr() = ptr.(DynamicAllocationSource).getSizeExprSource(_, _) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(BufferAccess ba, Expr arg | + ( + arg = ba.(BufferAccessLibraryFunctionCall).getAnArgument() or + arg = ba.getARelevantExpr() + ) and + ( + sink.asExpr() = arg or + exists(getArithmeticOffsetValue(arg, 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 + // access of the array base, so remove the assignment edge at the expense of false-negatives. + exists(AssignExpr a | + node.asExpr() = a.getRValue().getAChild*() and + ( + a.getLValue() instanceof ArrayExpr or + a.getLValue() instanceof PointerDereferenceExpr + ) + ) + or + // remove flow from `src` to `dst` in a buffer access function call + // the standard library models such flow through functions such as memcpy, strcpy, etc. + exists(BufferAccessLibraryFunctionCall fc | node.asExpr() = fc.getReadArg().getAChild*()) + or + node.asDefiningArgument() instanceof AddressOfExpr + } + } + + private module PointerToObjectSourceOrSizeToBufferAccessFunctionFlow = + DataFlow::Global; + + private predicate hasFlowFromBufferOrSizeExprToUse(Expr source, Expr use) { + exists(Expr useOrChild | + exists(getArithmeticOffsetValue(use, useOrChild)) and + PointerToObjectSourceOrSizeToBufferAccessFunctionFlow::flow(DataFlow::exprNode(source), + DataFlow::exprNode(useOrChild)) + ) + } + + private predicate bufferUseComputableBufferSize( + Expr bufferUse, PointerToObjectSource source, int size + ) { + // flow from a PointerToObjectSource for which we can compute the exact size + size = source.getFixedSize() and + hasFlowFromBufferOrSizeExprToUse(source, bufferUse) + } + + private predicate bufferUseNonComputableSize(Expr bufferUse, Expr source) { + not bufferUseComputableBufferSize(bufferUse, source, _) and + hasFlowFromBufferOrSizeExprToUse(source.(DynamicAllocationSource), bufferUse) + } + + /** + * Relates `sizeExpr`, a buffer access size expresion, to `source`, which is either `sizeExpr` + * if `sizeExpr` has a stated value, or a `DynamicAllocationSource::getSizeExprSource` for which + * we can compute the exact size and that has flow to `sizeExpr`. + */ + private predicate sizeExprComputableSize(Expr sizeExpr, Expr source, int size) { + // computable direct value, e.g. array_base[10], where "10" is sizeExpr and source. + size = getMinStatedValue(sizeExpr) and + source = sizeExpr + or + // computable source value that flows to the size expression, e.g. in cases such as the following: + // size_t sz = 10; + // malloc(sz); + // ... sz passed interprocedurally to another function ... + // use(p, sz + 1); + size = source.(DynamicAllocationSource).getFixedSize() + getArithmeticOffsetValue(sizeExpr, _) and + hasFlowFromBufferOrSizeExprToUse(source.(DynamicAllocationSource).getSizeExprSource(_, _), + sizeExpr) + } + + /** + * If the size is not computable locally, then it is either: + * + * 1. A dynamic allocation, from which we can get `getSizeExprSource()', from which + * we can either check specific logic (e.g. string length with offset) or compare GVNs. + * 2. An unrelateable size expression, which we might, however, be able to compute the bounds + * of and check against the buffer size, if that is known. + * + * In case 2, this predicate does not hold. + * + * NOTE: This predicate does not actually perform the above mentioned heuristics. + */ + predicate sizeExprNonComputableSize( + Expr bufferSizeArg, Expr alloc, Expr allocSize, Expr allocSizeBase, int offset + ) { + bufferSizeArg = any(BufferAccess access).getARelevantExpr() and + not sizeExprComputableSize(bufferSizeArg, alloc, _) and + allocSize = alloc.(DynamicAllocationSource).getSizeExprSource(allocSizeBase, offset) and + hasFlowFromBufferOrSizeExprToUse(allocSize, bufferSizeArg) + } + + /** + * Holds if `arg` refers to the number of characters excluding a null terminator + */ + bindingset[fc, arg] + private predicate isArgNumCharacters(BufferAccessLibraryFunctionCall fc, Expr arg) { + exists(int i | + arg = fc.getArgument(i) and + fc.getTarget().(BufferAccessLibraryFunction).getALengthParameterIndex(i) + ) + } + + /** + * Returns '1' if `arg` refers to the number of characters excluding a null terminator, + * otherwise '0' if `arg` refers to the number of characters including a null terminator. + */ + bindingset[fc, arg] + private int argNumCharactersOffset(BufferAccess fc, Expr arg) { + if isArgNumCharacters(fc, arg) then result = 1 else result = 0 + } + + /** + * Holds if the call `fc` may result in an invalid buffer access due a read buffer being bigger + * than the write buffer. This heuristic is useful for cases such as strcpy(dst, src). + */ + predicate isReadBufferSizeGreaterThanWriteBufferSize( + Expr readBuffer, Expr writeBuffer, int readBufferSize, int writeBufferSize, + BufferAccessLibraryFunctionCall fc + ) { + readBuffer = fc.getReadArg() and + writeBuffer = fc.getWriteArg() and + exists(int readSizeMult, int writeSizeMult, int readBufferSizeBase, int writeBufferSizeBase | + // the read and write buffer sizes must be derived from computable constants + bufferUseComputableBufferSize(readBuffer, _, readBufferSizeBase) and + bufferUseComputableBufferSize(writeBuffer, _, writeBufferSizeBase) and + // calculate the buffer byte sizes (size base is the number of elements) + readSizeMult = fc.getReadSizeArgMult() and + writeSizeMult = fc.getWriteSizeArgMult() and + readBufferSize = readBufferSizeBase - readSizeMult * getArithmeticOffsetValue(readBuffer, _) and + writeBufferSize = + writeBufferSizeBase - writeSizeMult * getArithmeticOffsetValue(writeBuffer, _) and + // the read buffer size is larger than the write buffer size + readBufferSize > writeBufferSize and + ( + // if a size arg exists and it is computable, then it must be <= to the write buffer size + exists(fc.getWriteSizeArg(writeSizeMult)) + implies + ( + sizeExprComputableSize(fc.getWriteSizeArg(writeSizeMult), _, _) and + not exists(Expr writeSizeArg, int writeSizeArgValue | + writeSizeArg = fc.getWriteSizeArg(writeSizeMult) and + sizeExprComputableSize(writeSizeArg, _, writeSizeArgValue) and + writeSizeMult.(float) * + (writeSizeArgValue + argNumCharactersOffset(fc, writeSizeArg)).(float) <= + writeBufferSize + ) + ) + ) + ) + } + + /** + * Holds if `sizeArg` is the right operand of a `PointerSubExpr` + */ + predicate isSizeArgPointerSubExprRightOperand(Expr sizeArg) { + exists(PointerSubExpr subExpr | sizeArg = subExpr.getRightOperand()) + } + + /** + * Holds if the BufferAccess `bufferAccess` results in a buffer overflow due to a size argument + * or buffer access offset being greater in size than the buffer size being accessed or written to. + */ + predicate isSizeArgGreaterThanBufferSize( + Expr bufferArg, Expr sizeArg, PointerToObjectSource bufferSource, int computedBufferSize, + int computedSizeAccessed, BufferAccess bufferAccess + ) { + exists(float sizeMult, int bufferArgSize, int sizeArgValue | + bufferAccess.hasABuffer(bufferArg, sizeArg, sizeMult) and + bufferUseComputableBufferSize(bufferArg, bufferSource, bufferArgSize) and + // If the bufferArg is an access of a static buffer, do not look for "long distance" sources + (bufferArg instanceof StaticBufferAccessSource implies bufferSource = bufferArg) and + sizeExprComputableSize(sizeArg, _, sizeArgValue) and + computedBufferSize = bufferArgSize - sizeMult.(float) * getArithmeticOffsetValue(bufferArg, _) and + // Handle cases such as *(ptr - 1) + ( + if isSizeArgPointerSubExprRightOperand(sizeArg) + then + computedSizeAccessed = + sizeMult.(float) * + (-sizeArgValue + argNumCharactersOffset(bufferAccess, sizeArg)).(float) + else + computedSizeAccessed = + sizeMult.(float) * + (sizeArgValue + argNumCharactersOffset(bufferAccess, sizeArg)).(float) + ) and + computedBufferSize < computedSizeAccessed + ) + } + + /** + * Holds if the call `fc` may result in an invalid buffer access due to a buffer argument + * being accessed at an offset that is greater than the size of the buffer. + */ + predicate isBufferOffsetGreaterThanBufferSize( + Expr bufferArg, int bufferArgOffset, int bufferSize, BufferAccessLibraryFunctionCall fc + ) { + exists(int bufferElementSize | + ( + bufferArg = fc.getReadArg() and + bufferElementSize = fc.getReadSizeArgMult() + or + bufferArg = fc.getWriteArg() and + bufferElementSize = fc.getWriteSizeArgMult() + ) and + bufferUseComputableBufferSize(bufferArg, _, bufferSize) and + bufferArgOffset = getArithmeticOffsetValue(bufferArg, _) * bufferElementSize and + bufferArgOffset >= bufferSize + ) + } + + /** + * Holds if `a` and `b` are function calls to the same target function and + * have identical arguments (determined by their global value number or `VariableAccess` targets). + */ + bindingset[a, b] + private predicate areFunctionCallsSyntacticallySame(FunctionCall a, FunctionCall b) { + a.getTarget() = b.getTarget() and + ( + exists(a.getAnArgument()) + implies + not exists(int i, Expr argA, Expr argB | + i = [0 .. a.getTarget().getNumberOfParameters() - 1] + | + argA = a.getArgument(i) and + argB = b.getArgument(i) and + not globalValueNumber(argA) = globalValueNumber(argB) and + not argA.(VariableAccess).getTarget() = argB.(VariableAccess).getTarget() + ) + ) + } + + /** + * Holds if `a` and `b` have the same global value number or are syntactically identical function calls + */ + bindingset[a, b] + private predicate isGVNOrFunctionCallSame(Expr a, Expr b) { + globalValueNumber(a) = globalValueNumber(b) + or + areFunctionCallsSyntacticallySame(a, b) + } + + /** + * Holds if the BufferAccess is accessed with a `base + accessOffset` on a buffer that was + * allocated a size of the form `base + allocationOffset`. + */ + predicate isGVNOffsetGreaterThanBufferSize( + Expr bufferArg, Expr bufferSizeArg, Expr sourceSizeExpr, int sourceSizeExprOffset, + int sizeArgOffset, BufferAccessLibraryFunctionCall fc + ) { + exists( + DynamicAllocationSource source, Expr sourceSizeExprBase, int bufferArgOffset, int sizeMult + | + ( + bufferArg = fc.getWriteArg() and + bufferSizeArg = fc.getWriteSizeArg(sizeMult) + or + bufferArg = fc.getReadArg() and + bufferSizeArg = fc.getReadSizeArg(sizeMult) + ) and + sourceSizeExpr = source.getSizeExprSource(sourceSizeExprBase, sourceSizeExprOffset) and + bufferUseNonComputableSize(bufferArg, source) and + not globalValueNumber(sourceSizeExpr) = globalValueNumber(bufferSizeArg) and + exists(Expr sizeArgBase | + sizeArgOffset = getArithmeticOffsetValue(bufferSizeArg.getAChild*(), sizeArgBase) and + isGVNOrFunctionCallSame(sizeArgBase, sourceSizeExprBase) and + bufferArgOffset = getArithmeticOffsetValue(bufferArg, _) and + sourceSizeExprOffset + bufferArgOffset < sizeArgOffset + ) + ) + } + + /** + * Holds if the call `fc` may result in an invalid buffer access due to a standard library + * function being called with a null pointer as a buffer argument while expecting only non-null input. + */ + predicate isMandatoryBufferArgNull(Expr bufferArg, BufferAccessLibraryFunctionCall fc) { + exists(int i | + i = + [ + fc.getTarget().(BufferAccessLibraryFunction).getReadParamIndex(), + fc.getTarget().(BufferAccessLibraryFunction).getWriteParamIndex() + ] and + not fc.getTarget().(BufferAccessLibraryFunction).getAPermissiblyNullParameterIndex(i) and + bufferArg = fc.getArgument(i) and + getMinStatedValue(bufferArg) = 0 + ) + } + + /** + * Holds if the call `fc` may result in an invalid buffer access due to a standard library function + * receiving a non-null terminated buffer as a buffer argument and accessing it. + */ + predicate isNullTerminatorMissingFromArg( + Expr arg, PointerToObjectSource source, BufferAccessLibraryFunctionCall fc + ) { + exists(int i, Expr argChild | + fc.getTarget().(BufferAccessLibraryFunction).getANullTerminatedParameterIndex(i) and + fc.getArgument(i) = arg and + source.isNotNullTerminated() and + argChild = arg.getAChild*() and + // ignore cases like strcpy(irrelevant_func(non_null_terminated_str, ...), src) + not exists(FunctionCall other | + not other = fc and + other.getAnArgument().getAChild*() = argChild + ) and + hasFlowFromBufferOrSizeExprToUse(source, argChild) + ) + } + + predicate isSizeArgNotCheckedLessThanFixedBufferSize( + Expr bufferArg, Expr sizeArg, PointerToObjectSource bufferSource, int bufferArgSize, + BufferAccess bufferAccess, int sizeArgUpperBound, int sizeMult + ) { + bufferAccess.hasABuffer(bufferArg, sizeArg, sizeMult) and + bufferUseComputableBufferSize(bufferArg, bufferSource, bufferArgSize) and + // If the bufferArg is an access of a static buffer, do not look for "long distant" sources + (bufferArg instanceof StaticBufferAccessSource implies bufferSource = bufferArg) and + // Not a size expression for which we can compute a specific size + not sizeExprComputableSize(sizeArg, _, _) and + // Range analysis considers the upper bound to be larger than the buffer size + sizeArgUpperBound = upperBound(sizeArg) and + // Ignore bitwise & operations + not sizeArg instanceof BitwiseAndExpr and + sizeArgUpperBound * sizeMult > bufferArgSize and + // There isn't a relational operation guarding this access that seems to check the + // upper bound against a plausible terminal value + not exists(RelationalOperation relOp, Expr checkedUpperBound | + globalValueNumber(relOp.getLesserOperand()) = globalValueNumber(sizeArg) and + checkedUpperBound = relOp.getGreaterOperand() and + // There's no closer inferred bounds - otherwise we let range analysis check it + upperBound(checkedUpperBound) = exprMaxVal(checkedUpperBound) + ) + } + + predicate isSizeArgNotCheckedGreaterThanZero( + Expr bufferArg, Expr sizeArg, PointerToObjectSource bufferSource, BufferAccess bufferAccess + ) { + exists(float sizeMult | + bufferAccess.hasABuffer(bufferArg, sizeArg, sizeMult) and + ( + bufferUseComputableBufferSize(bufferArg, bufferSource, _) or + bufferUseNonComputableSize(bufferArg, bufferSource) + ) and + ( + // Not a size expression for which we can compute a specific size + not sizeExprComputableSize(sizeArg, _, _) and + // and with a lower bound that is less than zero, taking into account offsets + lowerBound(sizeArg) + getArithmeticOffsetValue(bufferArg, _) < 0 + or + // A size expression for which we can compute a specific size and that size is less than zero + sizeExprComputableSize(sizeArg, _, _) and + ( + if isSizeArgPointerSubExprRightOperand(sizeArg) + then -sizeArg.getValue().toInt() + getArithmeticOffsetValue(bufferArg, _) < 0 + else sizeArg.getValue().toInt() + getArithmeticOffsetValue(bufferArg, _) < 0 + ) + ) + ) + } + + private string bufferArgType(BufferAccessLibraryFunctionCall fc, Expr bufferArg) { + fc.getReadArg() = bufferArg and + result = "read buffer" + or + fc.getWriteArg() = bufferArg and + result = "write buffer" + } + + predicate problems( + BufferAccessLibraryFunctionCall fc, string message, Expr bufferArg, string bufferArgStr, + Expr sizeOrOtherBufferArg, string otherStr + ) { + exists(int bufferArgSize, int sizeArgValue | + isSizeArgGreaterThanBufferSize(bufferArg, sizeOrOtherBufferArg, _, bufferArgSize, + sizeArgValue, fc) and + bufferArgStr = bufferArgType(fc, bufferArg) and + message = + "The size of the $@ passed to " + fc.getTarget().getName() + " is " + bufferArgSize + + " bytes, but the " + "$@ is " + sizeArgValue + " bytes." and + otherStr = "size argument" + or + isBufferOffsetGreaterThanBufferSize(bufferArg, sizeArgValue, bufferArgSize, fc) and + bufferArgStr = bufferArgType(fc, bufferArg) and + message = + "The $@ passed to " + fc.getTarget().getName() + " is " + bufferArgSize + + " bytes, but an offset of " + sizeArgValue + " bytes is used to access it." and + otherStr = "" and + sizeOrOtherBufferArg = bufferArg + ) + or + isMandatoryBufferArgNull(bufferArg, fc) and + message = "The $@ passed to " + fc.getTarget().getName() + " is null." and + bufferArgStr = "argument" and + otherStr = "" and + sizeOrOtherBufferArg = bufferArg + or + isNullTerminatorMissingFromArg(bufferArg, _, fc) and + message = "The $@ passed to " + fc.getTarget().getName() + " might not be null-terminated." and + bufferArgStr = "argument" and + otherStr = "" and + sizeOrOtherBufferArg = bufferArg + or + exists(int readBufferSize, int writeBufferSize | + isReadBufferSizeGreaterThanWriteBufferSize(bufferArg, sizeOrOtherBufferArg, readBufferSize, + writeBufferSize, fc) and + message = + "The size of the $@ passed to " + fc.getTarget().getName() + " is " + readBufferSize + + " bytes, but the size of the $@ is only " + writeBufferSize + " bytes." and + bufferArgStr = "read buffer" and + otherStr = "write buffer" + ) + or + exists(int accessOffset, Expr source | + isGVNOffsetGreaterThanBufferSize(bufferArg, _, source, _, accessOffset, fc) and + message = + "The $@ passed to " + fc.getTarget().getName() + " is accessed at an excessive offset of " + + accessOffset + " element(s) from the $@." and + bufferArgStr = bufferArgType(fc, bufferArg) and + sizeOrOtherBufferArg = source and + otherStr = "allocation size base" + ) + } +} diff --git a/c/common/src/codingstandards/c/Pointers.qll b/c/common/src/codingstandards/c/Pointers.qll deleted file mode 100644 index 87ade425e1..0000000000 --- a/c/common/src/codingstandards/c/Pointers.qll +++ /dev/null @@ -1,38 +0,0 @@ -/** - * A module for representing pointers - */ - -import cpp -import codingstandards.cpp.Type - -/** - * A null pointer constant, which is either in the form `NULL` or `(void *)0`. - */ -predicate isNullPointerConstant(Expr e) { - e.findRootCause() instanceof NULLMacro - or - exists(CStyleCast c | - not c.isImplicit() and - c.getExpr() = e and - e instanceof Zero and - c.getType() instanceof VoidPointerType - ) -} - -predicate isCastNullPointerConstant(Cast c) { - isNullPointerConstant(c.getExpr()) and - c.getUnderlyingType() instanceof PointerType -} - -/** - * A type representing a pointer to object - */ -class PointerToObjectType extends PointerType { - PointerToObjectType() { - not ( - this.getUnderlyingType() instanceof FunctionPointerType or - this.getUnderlyingType() instanceof VoidPointerType or - this.getBaseType().getUnderlyingType() instanceof IncompleteType - ) - } -} diff --git a/c/common/src/codingstandards/c/SideEffects.qll b/c/common/src/codingstandards/c/SideEffects.qll index 3cea568e3e..09bf672a30 100644 --- a/c/common/src/codingstandards/c/SideEffects.qll +++ b/c/common/src/codingstandards/c/SideEffects.qll @@ -26,7 +26,7 @@ private class VolatileAccess extends GlobalSideEffect::Range, VariableAccess { this.getTarget().isVolatile() and // Exclude value computation of an lvalue expression soley used to determine the identity // of the object. As noted in the footnote of 6.5.16 point 3 it is implementation dependend - // whether the value of the assignment expression deterived from the left operand after the assignment + // whether the value of the assignment expression derived from the left operand after the assignment // is determined by reading the object. We assume it is not for assignments that are a child of an // expression statement because the value is not used and is required for the compliant MISRA-C:2012 case: // `extern volatile int v; v = v & 0x80;` diff --git a/c/common/src/codingstandards/c/Signal.qll b/c/common/src/codingstandards/c/Signal.qll new file mode 100644 index 0000000000..2a570b654f --- /dev/null +++ b/c/common/src/codingstandards/c/Signal.qll @@ -0,0 +1,61 @@ +import cpp +private import semmle.code.cpp.dataflow.DataFlow + +/** + * A signal corresponding to a computational exception + */ +class ComputationalExceptionSignal extends MacroInvocation { + ComputationalExceptionSignal() { this.getMacroName() = ["SIGFPE", "SIGILL", "SIGSEGV", "SIGBUS"] } +} + +/** + * A call to function `signal` + */ +class SignalCall extends FunctionCall { + SignalCall() { this.getTarget().hasGlobalName("signal") } +} + +/** + * A signal handler + */ +class SignalHandler extends Function { + SignalCall registration; + + SignalHandler() { + // is a signal handler + this = registration.getArgument(1).(FunctionAccess).getTarget() + } + + SignalCall getRegistration() { result = registration } + + FunctionCall getReassertingCall() { + result.getTarget().hasGlobalName("signal") and + this = result.getEnclosingFunction() and + ( + this.getRegistration().getArgument(0).getValue() = result.getArgument(0).getValue() + or + DataFlow::localFlow(DataFlow::parameterNode(this.getParameter(0)), + DataFlow::exprNode(result.getArgument(0))) + ) + } +} + +/** + * A call to `abort` or `_Exit` or `quick_exit` + */ +class AbortCall extends FunctionCall { + AbortCall() { this.getTarget().hasGlobalName(["abort", "_Exit", "quick_exit"]) } +} + +/** + * Models the type `sig_atomic_type` + */ +class SigAtomicType extends Type { + SigAtomicType() { + this.getName() = "sig_atomic_t" + or + this.(TypedefType).getBaseType() instanceof SigAtomicType + or + this.(SpecifiedType).getBaseType() instanceof SigAtomicType + } +} diff --git a/c/common/src/codingstandards/c/StorageDuration.qll b/c/common/src/codingstandards/c/StorageDuration.qll new file mode 100644 index 0000000000..4669d467bb --- /dev/null +++ b/c/common/src/codingstandards/c/StorageDuration.qll @@ -0,0 +1,31 @@ +import cpp + +class DeclarationWithStorageDuration extends Declaration { } + +newtype TStorageDuration = + StorageDurationStatic() or + StorageDurationAutomatic() or + StorageDurationThread() or + StorageDurationAllocated() + +class StorageDuration extends TStorageDuration { + predicate isStatic() { this = StorageDurationStatic() } + + predicate isAutomatic() { this = StorageDurationAutomatic() } + + predicate isThread() { this = StorageDurationThread() } + + predicate isAllocated() { this = StorageDurationAllocated() } + + string toString() { result = getStorageTypeName() + " storage duration" } + + string getStorageTypeName() { + isStatic() and result = "static" + or + isAutomatic() and result = "automatic" + or + isThread() and result = "thread" + or + isAllocated() and result = "allocated" + } +} diff --git a/c/common/src/codingstandards/c/SubObjects.qll b/c/common/src/codingstandards/c/SubObjects.qll new file mode 100644 index 0000000000..66f15cd18c --- /dev/null +++ b/c/common/src/codingstandards/c/SubObjects.qll @@ -0,0 +1,125 @@ +/** + * A library that expands upon the `Objects.qll` library, to support nested "Objects" such as + * `x.y.z` or `x[i][j]` within an object `x`. + * + * Objects in C are values in memory, that have a type and a storage duration. In the case of + * array objects and struct objects, the object will contain other objects. The these subobjects + * will share properties of the root object such as storage duration. This library can be used to, + * for instance, find all usages of a struct member to ensure that member is initialized before it + * is used. + * + * To use this library, select `SubObject` and find its usages in the AST via `getAnAccess()` (to + * find usages of the subobject by value) or `getAnAddressOfExpr()` (to find usages of the object + * by address). + * + * Note that a struct or array object may contain a pointer. In this case, the pointer itself is + * a subobject of the struct or array object, but the object that the pointer points to is not. + * This is because the pointed-to object does not necessarily have the same storage duration, + * lifetime, or linkage as the pointer and the object containing the pointer. + * + * Note as well that `getAnAccess()` on an array subobject will return all accesses to the array, + * not just accesses to a particular index. For this reason, `SubObject` exposes the predicate + * `isPrecise()`. If a subobject is precise, that means all results of `getAnAccess()` will + * definitely refer to the same object in memory. If it is not precise, the different accesses + * may refer to the same or different objects in memory. For instance, `x[i].y` and `x[j].y` are + * the same object if `i` and `j` are the same, but they are different objects if `i` and `j` are + * different. + */ + +import codingstandards.c.Objects + +newtype TSubObject = + TObjectRoot(ObjectIdentity i) or + TObjectMember(SubObject struct, MemberVariable m) { + m = struct.getType().(Struct).getAMemberVariable() + } or + TObjectIndex(SubObject array) { array.getType() instanceof ArrayType } + +class SubObject extends TSubObject { + string toString() { + exists(ObjectIdentity i | + this = TObjectRoot(i) and + result = i.toString() + ) + or + exists(SubObject struct, Variable m | + this = TObjectMember(struct, m) and + result = struct.toString() + "." + m.getName() + ) + or + exists(SubObject array | + this = TObjectIndex(array) and + result = array.toString() + ) + } + + Type getType() { + exists(ObjectIdentity i | + this = TObjectRoot(i) and + result = i.getType() + ) + or + exists(Variable m | + this = TObjectMember(_, m) and + result = m.getType() + ) + or + exists(SubObject array | + this = TObjectIndex(array) and + result = array.getType().(ArrayType).getBaseType() + ) + } + + /** + * Holds for object roots and for member accesses on that root, not for array accesses. + * + * This is useful for cases where we do not wish to treat `x[y]` and `x[z]` as the same object. + */ + predicate isPrecise() { not getParent*() = TObjectIndex(_) } + + SubObject getParent() { + exists(SubObject struct, MemberVariable m | + this = TObjectMember(struct, m) and + result = struct + ) + or + exists(SubObject array | + this = TObjectIndex(array) and + result = array + ) + } + + Expr getAnAccess() { + exists(ObjectIdentity i | + this = TObjectRoot(i) and + result = i.getAnAccess() + ) + or + exists(MemberVariable m | + this = TObjectMember(_, m) and + result = m.getAnAccess() and + // Only consider `DotFieldAccess`es, not `PointerFieldAccess`es, as the latter + // are not subobjects of the root object: + result.(DotFieldAccess).getQualifier() = getParent().getAnAccess() + ) + or + this = TObjectIndex(_) and + result.(ArrayExpr).getArrayBase() = getParent().getAnAccess() + } + + AddressOfExpr getAnAddressOfExpr() { result.getOperand() = this.getAnAccess() } + + /** + * Get the "root" object identity to which this subobject belongs. For instance, in the + * expression `x.y.z`, the root object is `x`. This subobject will share properties with the root + * object such as storage duration, lifetime, and linkage. + */ + ObjectIdentity getRootIdentity() { + exists(ObjectIdentity i | + this = TObjectRoot(i) and + result = i + ) + or + result = getParent().getRootIdentity() + } +} diff --git a/c/common/src/codingstandards/c/TgMath.qll b/c/common/src/codingstandards/c/TgMath.qll new file mode 100644 index 0000000000..8d68cd2574 --- /dev/null +++ b/c/common/src/codingstandards/c/TgMath.qll @@ -0,0 +1,102 @@ +import cpp + +private string getATgMathMacroName(boolean allowComplex, int numberOfParameters) { + allowComplex = true and + numberOfParameters = 1 and + result = + [ + "acos", "acosh", "asin", "asinh", "atan", "atanh", "carg", "cimag", "conj", "cos", "cosh", + "cproj", "creal", "exp", "fabs", "log", "sin", "sinh", "sqrt", "tan", "tanh" + ] + or + allowComplex = true and + numberOfParameters = 2 and + result = "pow" + or + allowComplex = false and + numberOfParameters = 1 and + result = + [ + "cbrt", "ceil", "erf", "erfc", "exp2", "expm1", "floor", "ilogb", "lgamma", "llrint", + "llround", "log10", "log1p", "log2", "logb", "lrint", "lround", "nearbyint", "rint", "round", + "tgamma", "trunc", + ] + or + allowComplex = false and + numberOfParameters = 2 and + result = + [ + "atan2", "copysign", "fdim", "fmax", "fmin", "fmod", "frexp", "hypot", "ldexp", "nextafter", + "nexttoward", "remainder", "scalbn", "scalbln" + ] + or + allowComplex = false and + numberOfParameters = 3 and + result = ["fma", "remquo"] +} + +private predicate hasOutputArgument(string macroName, int index) { + macroName = "frexp" and index = 1 + or + macroName = "remquo" and index = 2 +} + +class TgMathInvocation extends MacroInvocation { + Call call; + boolean allowComplex; + int numberOfParameters; + + TgMathInvocation() { + this.getMacro().getName() = getATgMathMacroName(allowComplex, numberOfParameters) and + call = getBestCallInExpansion(this) + } + + /** Account for extra parameters added by gcc */ + private int getParameterOffset() { + // Gcc calls look something like: `__builtin_tgmath(cosf, cosd, cosl, arg)`, in this example + // there is a parameter offset of 3, so `getOperandArgument(0)` is equivalent to + // `call.getArgument(3)`. + result = call.getNumberOfArguments() - numberOfParameters + } + + Expr getOperandArgument(int i) { + i >= 0 and + result = call.getArgument(i + getParameterOffset()) and + //i in [0..numberOfParameters - 1] and + not hasOutputArgument(getMacro().getName(), i) + } + + /** Get all explicit conversions, except those added by clang in the macro body */ + Expr getExplicitlyConvertedOperandArgument(int i) { + exists(Expr explicitConv | + explicitConv = getOperandArgument(i).getExplicitlyConverted() and + // clang explicitly casts most arguments, but not some integer arguments such as in `scalbn`. + if call.getTarget().getName().matches("__tg_%") and explicitConv instanceof Conversion + then result = explicitConv.(Conversion).getExpr() + else result = explicitConv + ) + } + + int getNumberOfOperandArguments() { + result = numberOfParameters - count(int i | hasOutputArgument(getMacroName(), i)) + } + + Expr getAnOperandArgument() { result = getOperandArgument(_) } + + predicate allowsComplex() { allowComplex = true } +} + +private Call getACallInExpansion(MacroInvocation mi) { result = mi.getAnExpandedElement() } + +private Call getNameMatchedCallInExpansion(MacroInvocation mi) { + result = getACallInExpansion(mi) and result.getTarget().getName() = mi.getMacroName() +} + +private Call getBestCallInExpansion(MacroInvocation mi) { + count(getACallInExpansion(mi)) = 1 and result = getACallInExpansion(mi) + or + count(getNameMatchedCallInExpansion(mi)) = 1 and result = getNameMatchedCallInExpansion(mi) + or + count(getNameMatchedCallInExpansion(mi)) > 1 and + result = rank[1](Call c | c = getACallInExpansion(mi) | c order by c.getTarget().getName()) +} diff --git a/c/common/src/codingstandards/c/UndefinedBehavior.qll b/c/common/src/codingstandards/c/UndefinedBehavior.qll new file mode 100644 index 0000000000..47461aa613 --- /dev/null +++ b/c/common/src/codingstandards/c/UndefinedBehavior.qll @@ -0,0 +1,44 @@ +import cpp +import codingstandards.cpp.types.Pointers +import codingstandards.cpp.UndefinedBehavior + +/** + * Library for modeling undefined behavior. + */ +abstract class CUndefinedBehavior extends UndefinedBehavior { } + +/** + * A function which has the signature - but not the name - of a main function. + */ +class C99MainFunction extends Function { + C99MainFunction() { + this.getNumberOfParameters() = 2 and + this.getType().getUnderlyingType() instanceof IntType and + this.getParameter(0).getType().getUnderlyingType() instanceof IntType and + this.getParameter(1) + .getType() + .getUnderlyingType() + .(UnspecifiedPointerOrArrayType) + .getBaseType() + .(UnspecifiedPointerOrArrayType) + .getBaseType() instanceof CharType + or + this.getNumberOfParameters() = 0 and + // Must be explicitly declared as `int main(void)`. + this.getADeclarationEntry().hasVoidParamList() and + this.getType().getUnderlyingType() instanceof IntType + } +} + +class CUndefinedMainDefinition extends CUndefinedBehavior, Function { + CUndefinedMainDefinition() { + // for testing purposes, we use the prefix ____codeql_coding_standards` + (this.getName() = "main" or this.getName().indexOf("____codeql_coding_standards_main") = 0) and + not this instanceof C99MainFunction + } + + override string getReason() { + result = + "main function may trigger undefined behavior because it is not in one of the formats specified by the C standard." + } +} diff --git a/c/common/src/codingstandards/c/Variable.qll b/c/common/src/codingstandards/c/Variable.qll index c6061c99c1..09d86e0e25 100644 --- a/c/common/src/codingstandards/c/Variable.qll +++ b/c/common/src/codingstandards/c/Variable.qll @@ -6,3 +6,46 @@ class VlaVariable extends Variable { /* Extractor workaround do determine if a VLA array has the specifier volatile.*/ override predicate isVolatile() { this.getType().(ArrayType).getBaseType().isVolatile() } } + +/** + * A flexible array member + * ie member with the type array that is last in a struct + * has no size specified + */ +class FlexibleArrayMember extends FlexibleArrayMemberCandidate { + FlexibleArrayMember() { + exists(ArrayType t | + this.getType() = t and + not exists(t.getSize()) + ) + } +} + +/** + * A member with the type array that is last in a struct + * includes any sized array (either specified or not) + */ +class FlexibleArrayMemberCandidate extends MemberVariable { + FlexibleArrayMemberCandidate() { + this.getType() instanceof ArrayType and + exists(Struct s | + this.getDeclaringType() = s and + not exists(int i, int j | + s.getAMember(i) = this and + exists(s.getAMember(j)) and + j > i + ) + ) + } +} + +/** + * A struct that contains a flexible array member + */ +class FlexibleArrayStructType extends Struct { + FlexibleArrayMember member; + + FlexibleArrayStructType() { this = member.getDeclaringType() } + + FlexibleArrayMember getFlexibleArrayMember() { result = member } +} diff --git a/c/common/src/codingstandards/c/initialization/GlobalInitializationAnalysis.qll b/c/common/src/codingstandards/c/initialization/GlobalInitializationAnalysis.qll new file mode 100644 index 0000000000..2906883ae9 --- /dev/null +++ b/c/common/src/codingstandards/c/initialization/GlobalInitializationAnalysis.qll @@ -0,0 +1,95 @@ +import cpp +import codingstandards.c.Objects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type + +signature module GlobalInitializationAnalysisConfigSig { + /** A function which is not called or started as a thread */ + default predicate isRootFunction(Function f) { + not exists(Function f2 | f2.calls(f)) and + not f instanceof ThreadedFunction and + // Exclude functions which are used as function pointers. + not exists(FunctionAccess access | f = access.getTarget()) + } + + ObjectIdentity getAnInitializedObject(Expr e); + + ObjectIdentity getAUsedObject(Expr e); +} + +module GlobalInitalizationAnalysis { + final class FinalFunction = Function; + + final class FinalExpr = Expr; + + class RootFunction extends FinalFunction { + RootFunction() { Config::isRootFunction(this) } + } + + /** A function call which initializes a mutex or a condition */ + class ObjectInit extends FinalExpr { + ObjectIdentity owningObject; + + ObjectInit() { owningObject = Config::getAnInitializedObject(this) } + + ObjectIdentity getOwningObject() { result = owningObject } + } + + /** + * A function argument where that argument is used as a mutex or condition object. + */ + class ObjectUse extends FinalExpr { + ObjectIdentity owningObject; + + ObjectUse() { owningObject = Config::getAUsedObject(this) } + + ObjectIdentity getOwningObject() { result = owningObject } + } + + predicate requiresInitializedMutexObject( + Function func, ObjectUse mutexUse, ObjectIdentity owningObject + ) { + mutexUse.getEnclosingFunction() = func and + owningObject = mutexUse.getOwningObject() and + not exists(ObjectInit init | + init.getEnclosingFunction() = func and + init.getOwningObject() = owningObject and + mutexUse.getAPredecessor+() = init + ) + or + exists(FunctionCall call | + func = call.getEnclosingFunction() and + requiresInitializedMutexObject(call.getTarget(), mutexUse, owningObject) and + not exists(ObjectInit init | + call.getAPredecessor*() = init and + init.getOwningObject() = owningObject + ) + ) + or + exists(C11ThreadCreateCall call | + func = call.getEnclosingFunction() and + not owningObject.getStorageDuration().isThread() and + requiresInitializedMutexObject(call.getFunction(), mutexUse, owningObject) and + not exists(ObjectInit init | + call.getAPredecessor*() = init and + init.getOwningObject() = owningObject + ) + ) + } + + predicate uninitializedFrom(Expr e, ObjectIdentity obj, Function callRoot) { + exists(ObjectUse use | use = e | + obj = use.getOwningObject() and + requiresInitializedMutexObject(callRoot, use, obj) and + ( + if obj.getStorageDuration().isAutomatic() + then obj.getEnclosingElement+() = callRoot + else ( + obj.getStorageDuration().isThread() and callRoot instanceof ThreadedFunction + or + callRoot instanceof RootFunction + ) + ) + ) + } +} diff --git a/c/common/src/qlpack.yml b/c/common/src/qlpack.yml index 91e3027719..068e7c3f2f 100644 --- a/c/common/src/qlpack.yml +++ b/c/common/src/qlpack.yml @@ -1,3 +1,6 @@ -name: common-c-coding-standards -version: 2.9.0 -libraryPathDependencies: common-cpp-coding-standards +name: codeql/common-c-coding-standards +version: 2.49.0-dev +license: MIT +dependencies: + codeql/common-cpp-coding-standards: '*' + codeql/cpp-all: 4.0.3 diff --git a/c/common/test/codeql-pack.lock.yml b/c/common/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/c/common/test/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/common/test/includes/standard-library/math.h b/c/common/test/includes/standard-library/math.h index 14f28ec8c6..4acad05f16 100644 --- a/c/common/test/includes/standard-library/math.h +++ b/c/common/test/includes/standard-library/math.h @@ -11,30 +11,30 @@ extern "C" { #define __NEED_double_t #include -#if 100*__GNUC__+__GNUC_MINOR__ >= 303 -#define NAN __builtin_nanf("") -#define INFINITY __builtin_inff() +#if 100 * __GNUC__ + __GNUC_MINOR__ >= 303 +#define NAN __builtin_nanf("") +#define INFINITY __builtin_inff() #else -#define NAN (0.0f/0.0f) -#define INFINITY 1e5000f +#define NAN (0.0f / 0.0f) +#define INFINITY 1e5000f #endif #define HUGE_VALF INFINITY -#define HUGE_VAL ((double)INFINITY) +#define HUGE_VAL ((double)INFINITY) #define HUGE_VALL ((long double)INFINITY) -#define MATH_ERRNO 1 +#define MATH_ERRNO 1 #define MATH_ERREXCEPT 2 #define math_errhandling 2 -#define FP_ILOGBNAN (-1-0x7fffffff) +#define FP_ILOGBNAN (-1 - 0x7fffffff) #define FP_ILOGB0 FP_ILOGBNAN -#define FP_NAN 0 -#define FP_INFINITE 1 -#define FP_ZERO 2 +#define FP_NAN 0 +#define FP_INFINITE 1 +#define FP_ZERO 2 #define FP_SUBNORMAL 3 -#define FP_NORMAL 4 +#define FP_NORMAL 4 #ifdef __FP_FAST_FMA #define FP_FAST_FMA 1 @@ -52,58 +52,68 @@ int __fpclassify(double); int __fpclassifyf(float); int __fpclassifyl(long double); -static __inline unsigned __FLOAT_BITS(float __f) -{ - union {float __f; unsigned __i;} __u; - __u.__f = __f; - return __u.__i; +static __inline unsigned __FLOAT_BITS(float __f) { + union { + float __f; + unsigned __i; + } __u; + __u.__f = __f; + return __u.__i; } -static __inline unsigned long long __DOUBLE_BITS(double __f) -{ - union {double __f; unsigned long long __i;} __u; - __u.__f = __f; - return __u.__i; +static __inline unsigned long long __DOUBLE_BITS(double __f) { + union { + double __f; + unsigned long long __i; + } __u; + __u.__f = __f; + return __u.__i; } -#define fpclassify(x) ( \ - sizeof(x) == sizeof(float) ? __fpclassifyf(x) : \ - sizeof(x) == sizeof(double) ? __fpclassify(x) : \ - __fpclassifyl(x) ) - -#define isinf(x) ( \ - sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) == 0x7f800000 : \ - sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL>>1) == 0x7ffULL<<52 : \ - __fpclassifyl(x) == FP_INFINITE) - -#define isnan(x) ( \ - sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) > 0x7f800000 : \ - sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL>>1) > 0x7ffULL<<52 : \ - __fpclassifyl(x) == FP_NAN) - -#define isnormal(x) ( \ - sizeof(x) == sizeof(float) ? ((__FLOAT_BITS(x)+0x00800000) & 0x7fffffff) >= 0x01000000 : \ - sizeof(x) == sizeof(double) ? ((__DOUBLE_BITS(x)+(1ULL<<52)) & -1ULL>>1) >= 1ULL<<53 : \ - __fpclassifyl(x) == FP_NORMAL) - -#define isfinite(x) ( \ - sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) < 0x7f800000 : \ - sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL>>1) < 0x7ffULL<<52 : \ - __fpclassifyl(x) > FP_INFINITE) +#define fpclassify(x) \ + (sizeof(x) == sizeof(float) ? __fpclassifyf(x) \ + : sizeof(x) == sizeof(double) ? __fpclassify(x) \ + : __fpclassifyl(x)) + +#define isinf(x) \ + (sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) == 0x7f800000 \ + : sizeof(x) == sizeof(double) \ + ? (__DOUBLE_BITS(x) & -1ULL >> 1) == 0x7ffULL << 52 \ + : __fpclassifyl(x) == FP_INFINITE) + +#define isnan(x) \ + (sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) > 0x7f800000 \ + : sizeof(x) == sizeof(double) \ + ? (__DOUBLE_BITS(x) & -1ULL >> 1) > 0x7ffULL << 52 \ + : __fpclassifyl(x) == FP_NAN) + +#define isnormal(x) \ + (sizeof(x) == sizeof(float) \ + ? ((__FLOAT_BITS(x) + 0x00800000) & 0x7fffffff) >= 0x01000000 \ + : sizeof(x) == sizeof(double) \ + ? ((__DOUBLE_BITS(x) + (1ULL << 52)) & -1ULL >> 1) >= 1ULL << 53 \ + : __fpclassifyl(x) == FP_NORMAL) + +#define isfinite(x) \ + (sizeof(x) == sizeof(float) ? (__FLOAT_BITS(x) & 0x7fffffff) < 0x7f800000 \ + : sizeof(x) == sizeof(double) \ + ? (__DOUBLE_BITS(x) & -1ULL >> 1) < 0x7ffULL << 52 \ + : __fpclassifyl(x) > FP_INFINITE) int __signbit(double); int __signbitf(float); int __signbitl(long double); -#define signbit(x) ( \ - sizeof(x) == sizeof(float) ? (int)(__FLOAT_BITS(x)>>31) : \ - sizeof(x) == sizeof(double) ? (int)(__DOUBLE_BITS(x)>>63) : \ - __signbitl(x) ) +#define signbit(x) \ + (sizeof(x) == sizeof(float) ? (int)(__FLOAT_BITS(x) >> 31) \ + : sizeof(x) == sizeof(double) ? (int)(__DOUBLE_BITS(x) >> 63) \ + : __signbitl(x)) -#define isunordered(x,y) (isnan((x)) ? ((void)(y),1) : isnan((y))) +#define isunordered(x, y) (isnan((x)) ? ((void)(y), 1) : isnan((y))) -#define __ISREL_DEF(rel, op, type) \ -static __inline int __is##rel(type __x, type __y) \ -{ return !isunordered(__x,__y) && __x op __y; } +#define __ISREL_DEF(rel, op, type) \ + static __inline int __is##rel(type __x, type __y) { \ + return !isunordered(__x, __y) && __x op __y; \ + } __ISREL_DEF(lessf, <, float_t) __ISREL_DEF(less, <, double_t) @@ -121,317 +131,316 @@ __ISREL_DEF(greaterequalf, >=, float_t) __ISREL_DEF(greaterequal, >=, double_t) __ISREL_DEF(greaterequall, >=, long double) -#define __tg_pred_2(x, y, p) ( \ - sizeof((x)+(y)) == sizeof(float) ? p##f(x, y) : \ - sizeof((x)+(y)) == sizeof(double) ? p(x, y) : \ - p##l(x, y) ) +#define __tg_pred_2(x, y, p) \ + (sizeof((x) + (y)) == sizeof(float) ? p##f(x, y) \ + : sizeof((x) + (y)) == sizeof(double) ? p(x, y) \ + : p##l(x, y)) -#define isless(x, y) __tg_pred_2(x, y, __isless) -#define islessequal(x, y) __tg_pred_2(x, y, __islessequal) -#define islessgreater(x, y) __tg_pred_2(x, y, __islessgreater) -#define isgreater(x, y) __tg_pred_2(x, y, __isgreater) -#define isgreaterequal(x, y) __tg_pred_2(x, y, __isgreaterequal) +#define isless(x, y) __tg_pred_2(x, y, __isless) +#define islessequal(x, y) __tg_pred_2(x, y, __islessequal) +#define islessgreater(x, y) __tg_pred_2(x, y, __islessgreater) +#define isgreater(x, y) __tg_pred_2(x, y, __isgreater) +#define isgreaterequal(x, y) __tg_pred_2(x, y, __isgreaterequal) -double acos(double); -float acosf(float); +double acos(double); +float acosf(float); long double acosl(long double); -double acosh(double); -float acoshf(float); +double acosh(double); +float acoshf(float); long double acoshl(long double); -double asin(double); -float asinf(float); +double asin(double); +float asinf(float); long double asinl(long double); -double asinh(double); -float asinhf(float); +double asinh(double); +float asinhf(float); long double asinhl(long double); -double atan(double); -float atanf(float); +double atan(double); +float atanf(float); long double atanl(long double); -double atan2(double, double); -float atan2f(float, float); +double atan2(double, double); +float atan2f(float, float); long double atan2l(long double, long double); -double atanh(double); -float atanhf(float); +double atanh(double); +float atanhf(float); long double atanhl(long double); -double cbrt(double); -float cbrtf(float); +double cbrt(double); +float cbrtf(float); long double cbrtl(long double); -double ceil(double); -float ceilf(float); +double ceil(double); +float ceilf(float); long double ceill(long double); -double copysign(double, double); -float copysignf(float, float); +double copysign(double, double); +float copysignf(float, float); long double copysignl(long double, long double); -double cos(double); -float cosf(float); +double cos(double); +float cosf(float); long double cosl(long double); -double cosh(double); -float coshf(float); +double cosh(double); +float coshf(float); long double coshl(long double); -double erf(double); -float erff(float); +double erf(double); +float erff(float); long double erfl(long double); -double erfc(double); -float erfcf(float); +double erfc(double); +float erfcf(float); long double erfcl(long double); -double exp(double); -float expf(float); +double exp(double); +float expf(float); long double expl(long double); -double exp2(double); -float exp2f(float); +double exp2(double); +float exp2f(float); long double exp2l(long double); -double expm1(double); -float expm1f(float); +double expm1(double); +float expm1f(float); long double expm1l(long double); -double fabs(double); -float fabsf(float); +double fabs(double); +float fabsf(float); long double fabsl(long double); -double fdim(double, double); -float fdimf(float, float); +double fdim(double, double); +float fdimf(float, float); long double fdiml(long double, long double); -double floor(double); -float floorf(float); +double floor(double); +float floorf(float); long double floorl(long double); -double fma(double, double, double); -float fmaf(float, float, float); +double fma(double, double, double); +float fmaf(float, float, float); long double fmal(long double, long double, long double); -double fmax(double, double); -float fmaxf(float, float); +double fmax(double, double); +float fmaxf(float, float); long double fmaxl(long double, long double); -double fmin(double, double); -float fminf(float, float); +double fmin(double, double); +float fminf(float, float); long double fminl(long double, long double); -double fmod(double, double); -float fmodf(float, float); +double fmod(double, double); +float fmodf(float, float); long double fmodl(long double, long double); -double frexp(double, int *); -float frexpf(float, int *); +double frexp(double, int *); +float frexpf(float, int *); long double frexpl(long double, int *); -double hypot(double, double); -float hypotf(float, float); +double hypot(double, double); +float hypotf(float, float); long double hypotl(long double, long double); -int ilogb(double); -int ilogbf(float); -int ilogbl(long double); +int ilogb(double); +int ilogbf(float); +int ilogbl(long double); -double ldexp(double, int); -float ldexpf(float, int); +double ldexp(double, int); +float ldexpf(float, int); long double ldexpl(long double, int); -double lgamma(double); -float lgammaf(float); +double lgamma(double); +float lgammaf(float); long double lgammal(long double); -long long llrint(double); -long long llrintf(float); -long long llrintl(long double); +long long llrint(double); +long long llrintf(float); +long long llrintl(long double); -long long llround(double); -long long llroundf(float); -long long llroundl(long double); +long long llround(double); +long long llroundf(float); +long long llroundl(long double); -double log(double); -float logf(float); +double log(double); +float logf(float); long double logl(long double); -double log10(double); -float log10f(float); +double log10(double); +float log10f(float); long double log10l(long double); -double log1p(double); -float log1pf(float); +double log1p(double); +float log1pf(float); long double log1pl(long double); -double log2(double); -float log2f(float); +double log2(double); +float log2f(float); long double log2l(long double); -double logb(double); -float logbf(float); +double logb(double); +float logbf(float); long double logbl(long double); -long lrint(double); -long lrintf(float); -long lrintl(long double); +long lrint(double); +long lrintf(float); +long lrintl(long double); -long lround(double); -long lroundf(float); -long lroundl(long double); +long lround(double); +long lroundf(float); +long lroundl(long double); -double modf(double, double *); -float modff(float, float *); +double modf(double, double *); +float modff(float, float *); long double modfl(long double, long double *); -double nan(const char *); -float nanf(const char *); +double nan(const char *); +float nanf(const char *); long double nanl(const char *); -double nearbyint(double); -float nearbyintf(float); +double nearbyint(double); +float nearbyintf(float); long double nearbyintl(long double); -double nextafter(double, double); -float nextafterf(float, float); +double nextafter(double, double); +float nextafterf(float, float); long double nextafterl(long double, long double); -double nexttoward(double, long double); -float nexttowardf(float, long double); +double nexttoward(double, long double); +float nexttowardf(float, long double); long double nexttowardl(long double, long double); -double pow(double, double); -float powf(float, float); +double pow(double, double); +float powf(float, float); long double powl(long double, long double); -double remainder(double, double); -float remainderf(float, float); +double remainder(double, double); +float remainderf(float, float); long double remainderl(long double, long double); -double remquo(double, double, int *); -float remquof(float, float, int *); +double remquo(double, double, int *); +float remquof(float, float, int *); long double remquol(long double, long double, int *); -double rint(double); -float rintf(float); +double rint(double); +float rintf(float); long double rintl(long double); -double round(double); -float roundf(float); +double round(double); +float roundf(float); long double roundl(long double); -double scalbln(double, long); -float scalblnf(float, long); +double scalbln(double, long); +float scalblnf(float, long); long double scalblnl(long double, long); -double scalbn(double, int); -float scalbnf(float, int); +double scalbn(double, int); +float scalbnf(float, int); long double scalbnl(long double, int); -double sin(double); -float sinf(float); +double sin(double); +float sinf(float); long double sinl(long double); -double sinh(double); -float sinhf(float); +double sinh(double); +float sinhf(float); long double sinhl(long double); -double sqrt(double); -float sqrtf(float); +double sqrt(double); +float sqrtf(float); long double sqrtl(long double); -double tan(double); -float tanf(float); +double tan(double); +float tanf(float); long double tanl(long double); -double tanh(double); -float tanhf(float); +double tanh(double); +float tanhf(float); long double tanhl(long double); -double tgamma(double); -float tgammaf(float); +double tgamma(double); +float tgammaf(float); long double tgammal(long double); -double trunc(double); -float truncf(float); +double trunc(double); +float truncf(float); long double truncl(long double); - #if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) -#undef MAXFLOAT -#define MAXFLOAT 3.40282346638528859812e+38F +#undef MAXFLOAT +#define MAXFLOAT 3.40282346638528859812e+38F #endif #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define M_E 2.7182818284590452354 /* e */ -#define M_LOG2E 1.4426950408889634074 /* log_2 e */ -#define M_LOG10E 0.43429448190325182765 /* log_10 e */ -#define M_LN2 0.69314718055994530942 /* log_e 2 */ -#define M_LN10 2.30258509299404568402 /* log_e 10 */ -#define M_PI 3.14159265358979323846 /* pi */ -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#define M_PI_4 0.78539816339744830962 /* pi/4 */ -#define M_1_PI 0.31830988618379067154 /* 1/pi */ -#define M_2_PI 0.63661977236758134308 /* 2/pi */ -#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ -#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ -#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#define M_E 2.7182818284590452354 /* e */ +#define M_LOG2E 1.4426950408889634074 /* log_2 e */ +#define M_LOG10E 0.43429448190325182765 /* log_10 e */ +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#define M_PI 3.14159265358979323846 /* pi */ +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#define M_PI_4 0.78539816339744830962 /* pi/4 */ +#define M_1_PI 0.31830988618379067154 /* 1/pi */ +#define M_2_PI 0.63661977236758134308 /* 2/pi */ +#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ extern int signgam; -double j0(double); -double j1(double); -double jn(int, double); +double j0(double); +double j1(double); +double jn(int, double); -double y0(double); -double y1(double); -double yn(int, double); +double y0(double); +double y1(double); +double yn(int, double); #endif #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define HUGE 3.40282346638528859812e+38F +#define HUGE 3.40282346638528859812e+38F -double drem(double, double); -float dremf(float, float); +double drem(double, double); +float dremf(float, float); -int finite(double); -int finitef(float); +int finite(double); +int finitef(float); -double scalb(double, double); -float scalbf(float, float); +double scalb(double, double); +float scalbf(float, float); -double significand(double); -float significandf(float); +double significand(double); +float significandf(float); -double lgamma_r(double, int*); -float lgammaf_r(float, int*); +double lgamma_r(double, int *); +float lgammaf_r(float, int *); -float j0f(float); -float j1f(float); -float jnf(int, float); +float j0f(float); +float j1f(float); +float jnf(int, float); -float y0f(float); -float y1f(float); -float ynf(int, float); +float y0f(float); +float y1f(float); +float ynf(int, float); #endif #ifdef _GNU_SOURCE -long double lgammal_r(long double, int*); +long double lgammal_r(long double, int *); -void sincos(double, double*, double*); -void sincosf(float, float*, float*); -void sincosl(long double, long double*, long double*); +void sincos(double, double *, double *); +void sincosf(float, float *, float *); +void sincosl(long double, long double *, long double *); -double exp10(double); -float exp10f(float); +double exp10(double); +float exp10f(float); long double exp10l(long double); -double pow10(double); -float pow10f(float); +double pow10(double); +float pow10f(float); long double pow10l(long double); #endif diff --git a/c/common/test/includes/standard-library/stdatomic.h b/c/common/test/includes/standard-library/stdatomic.h index f07aa2697a..21e4f995f4 100644 --- a/c/common/test/includes/standard-library/stdatomic.h +++ b/c/common/test/includes/standard-library/stdatomic.h @@ -1,7 +1,70 @@ -#define atomic_compare_exchange_weak(a, b, c) 0 -#define atomic_compare_exchange_weak_explicit(a, b, c, d, e) 0 -#define atomic_load(a) 0 -#define atomic_load_explicit(a, b) -#define atomic_store(a, b) 0 -#define atomic_store_explicit(a,b,c) 0 -#define ATOMIC_VAR_INIT(value) (value) \ No newline at end of file +#define ATOMIC_VAR_INIT(value) (value) +#define atomic_init __c11_atomic_init +#define atomic_is_lock_free(obj) __c11_atomic_is_lock_free(sizeof(*(obj))) +typedef _Atomic(int) atomic_int; + +#define __ATOMIC_RELAXED 0 +#define __ATOMIC_CONSUME 1 +#define __ATOMIC_ACQUIRE 2 +#define __ATOMIC_RELEASE 3 +#define __ATOMIC_ACQ_REL 4 +#define __ATOMIC_SEQ_CST 5 + +typedef enum memory_order { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +} memory_order; + +void atomic_thread_fence(memory_order); +void atomic_signal_fence(memory_order); + +#define atomic_thread_fence(order) __c11_atomic_thread_fence(order) +#define atomic_signal_fence(order) __c11_atomic_signal_fence(order) + +#define atomic_store(object, desired) __c11_atomic_store(object, desired, __ATOMIC_SEQ_CST) +#define atomic_store_explicit __c11_atomic_store + +#define atomic_load(object) __c11_atomic_load(object, __ATOMIC_SEQ_CST) +#define atomic_load_explicit __c11_atomic_load + +#define atomic_exchange(object, desired) __c11_atomic_exchange(object, desired, __ATOMIC_SEQ_CST) +#define atomic_exchange_explicit __c11_atomic_exchange + +#define atomic_compare_exchange_strong(object, expected, desired) __c11_atomic_compare_exchange_strong(object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong_explicit __c11_atomic_compare_exchange_strong + +#define atomic_compare_exchange_weak(object, expected, desired) __c11_atomic_compare_exchange_weak(object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_weak_explicit __c11_atomic_compare_exchange_weak + +#define atomic_fetch_add(object, operand) __c11_atomic_fetch_add(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit __c11_atomic_fetch_add + +#define atomic_fetch_sub(object, operand) __c11_atomic_fetch_sub(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub_explicit __c11_atomic_fetch_sub + +#define atomic_fetch_or(object, operand) __c11_atomic_fetch_or(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_or_explicit __c11_atomic_fetch_or + +#define atomic_fetch_xor(object, operand) __c11_atomic_fetch_xor(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor_explicit __c11_atomic_fetch_xor + +#define atomic_fetch_and(object, operand) __c11_atomic_fetch_and(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_and_explicit __c11_atomic_fetch_and + +typedef struct atomic_flag { _Atomic(_Bool) _Value; } atomic_flag; + +_Bool atomic_flag_test_and_set(volatile atomic_flag *); +_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *, memory_order); + +void atomic_flag_clear(volatile atomic_flag *); +void atomic_flag_clear_explicit(volatile atomic_flag *, memory_order); + +#define atomic_flag_test_and_set(object) __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST) +#define atomic_flag_test_and_set_explicit(object, order) __c11_atomic_exchange(&(object)->_Value, 1, order) + +#define atomic_flag_clear(object) __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST) +#define atomic_flag_clear_explicit(object, order) __c11_atomic_store(&(object)->_Value, 0, order) \ No newline at end of file diff --git a/c/common/test/includes/standard-library/stdlib.h b/c/common/test/includes/standard-library/stdlib.h index b54a051fe9..1af95223d1 100644 --- a/c/common/test/includes/standard-library/stdlib.h +++ b/c/common/test/includes/standard-library/stdlib.h @@ -49,6 +49,7 @@ int at_quick_exit (void (*) (void)); _Noreturn void quick_exit (int); char *getenv (const char *); +char *getenv_s (size_t *restrict len, char *restrict value, size_t valuesz, const char *restrict name); int system (const char *); diff --git a/c/common/test/library/expr/FullExpr.expected b/c/common/test/library/expr/FullExpr.expected index 4785b90024..c712793f8b 100644 --- a/c/common/test/library/expr/FullExpr.expected +++ b/c/common/test/library/expr/FullExpr.expected @@ -1,4 +1,4 @@ -| fullexpr.c:8:18:11:37 | temporary object | +| fullexpr.c:8:18:11:37 | {...} | | fullexpr.c:13:3:13:5 | ... ++ | | fullexpr.c:15:7:15:7 | i | | fullexpr.c:17:10:17:10 | i | diff --git a/c/common/test/library/identifierlinkage/IdentifierLinkage.expected b/c/common/test/library/identifierlinkage/IdentifierLinkage.expected new file mode 100644 index 0000000000..c3f1bc39ef --- /dev/null +++ b/c/common/test/library/identifierlinkage/IdentifierLinkage.expected @@ -0,0 +1,10 @@ +| identifierlinkage.c:2:5:2:10 | g_ext1 | external linkage | +| identifierlinkage.c:3:12:3:17 | g_ext2 | external linkage | +| identifierlinkage.c:6:12:6:17 | g_int1 | internal linkage | +| identifierlinkage.c:9:5:9:10 | g_ext3 | external linkage | +| identifierlinkage.c:12:12:12:17 | g_int2 | internal linkage | +| identifierlinkage.c:15:12:15:12 | p | no linkage | +| identifierlinkage.c:16:7:16:13 | l_none1 | no linkage | +| identifierlinkage.c:17:14:17:20 | l_none2 | no linkage | +| identifierlinkage.c:18:14:18:19 | l_ext1 | external linkage | +| identifierlinkage.c:24:7:24:7 | m | no linkage | diff --git a/c/common/test/library/identifierlinkage/IdentifierLinkage.ql b/c/common/test/library/identifierlinkage/IdentifierLinkage.ql new file mode 100644 index 0000000000..37e5b4cd58 --- /dev/null +++ b/c/common/test/library/identifierlinkage/IdentifierLinkage.ql @@ -0,0 +1,5 @@ +import codingstandards.c.IdentifierLinkage + +from Variable v +where not v.getLocation().toString() = "file://:0:0:0:0" +select v, linkageOfVariable(v) diff --git a/c/common/test/library/identifierlinkage/identifierlinkage.c b/c/common/test/library/identifierlinkage/identifierlinkage.c new file mode 100644 index 0000000000..25265aa144 --- /dev/null +++ b/c/common/test/library/identifierlinkage/identifierlinkage.c @@ -0,0 +1,28 @@ +// Simple external linkage +int g_ext1; +extern int g_ext2; + +// Simple internal linkage +static int g_int1; + +// Redefined maintaining linkage +int g_ext3; +extern int g_ext3; + +static int g_int2; +extern int g_int2; + +void f(int p) { + int l_none1; + static int l_none2; + extern int l_ext1; +} + +// Structs are not variables +struct s { + // Struct members are variables with no linkage. + int m; +}; + +// Enums and enum constants are not variables and have no linkage. +enum e { E1 }; \ No newline at end of file diff --git a/c/common/test/library/objects/ObjectIdentity.expected b/c/common/test/library/objects/ObjectIdentity.expected new file mode 100644 index 0000000000..34be1974f5 --- /dev/null +++ b/c/common/test/library/objects/ObjectIdentity.expected @@ -0,0 +1,21 @@ +| objectidentity.c:3:5:3:14 | g_statstg1 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:4:12:4:21 | g_statstg2 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:5:12:5:21 | g_statstg3 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:8:13:8:22 | p_autostg1 | automatic storage duration | file://:0:0:0:0 | int | +| objectidentity.c:8:31:8:40 | l_autostg2 | automatic storage duration | file://:0:0:0:0 | int | +| objectidentity.c:12:14:12:23 | l_statstg1 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:13:14:13:23 | l_statstg2 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:17:15:17:24 | g_thrdstg1 | thread storage duration | file://:0:0:0:0 | int | +| objectidentity.c:18:22:18:31 | g_thrdstg2 | thread storage duration | file://:0:0:0:0 | int | +| objectidentity.c:19:22:19:31 | g_thrdstg3 | thread storage duration | file://:0:0:0:0 | int | +| objectidentity.c:21:24:21:33 | l_statstg3 | thread storage duration | file://:0:0:0:0 | int | +| objectidentity.c:22:24:22:33 | l_statstg4 | thread storage duration | file://:0:0:0:0 | int | +| objectidentity.c:35:11:35:20 | g_statstg4 | static storage duration | file://:0:0:0:0 | s * | +| objectidentity.c:35:35:35:37 | {...} | static storage duration | objectidentity.c:27:8:27:8 | s | +| objectidentity.c:35:36:35:36 | 0 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:36:7:36:16 | g_statstg5 | static storage duration | file://:0:0:0:0 | char * | +| objectidentity.c:36:20:36:26 | hello | static storage duration | file://:0:0:0:0 | char[6] | +| objectidentity.c:38:3:38:3 | 1 | static storage duration | file://:0:0:0:0 | int | +| objectidentity.c:39:3:39:9 | hello | static storage duration | file://:0:0:0:0 | char[6] | +| objectidentity.c:40:3:40:15 | {...} | automatic storage duration | objectidentity.c:27:8:27:8 | s | +| objectidentity.c:40:14:40:14 | 1 | static storage duration | file://:0:0:0:0 | int | diff --git a/c/common/test/library/objects/ObjectIdentity.ql b/c/common/test/library/objects/ObjectIdentity.ql new file mode 100644 index 0000000000..0e92e588ba --- /dev/null +++ b/c/common/test/library/objects/ObjectIdentity.ql @@ -0,0 +1,5 @@ +import codingstandards.c.Objects + +from ObjectIdentity obj +where obj.getFile().getBaseName() = "objectidentity.c" +select obj, obj.getStorageDuration(), obj.getType() diff --git a/c/common/test/library/objects/objectidentity.c b/c/common/test/library/objects/objectidentity.c new file mode 100644 index 0000000000..066f68b1fd --- /dev/null +++ b/c/common/test/library/objects/objectidentity.c @@ -0,0 +1,41 @@ +#include "threads.h" +// Basic static storage duration +int g_statstg1; +extern int g_statstg2; +static int g_statstg3; + +// Basic automatic storage duration +void f1(int p_autostg1) { int l_autostg2; } + +// Block identifiers with static storage duration +void f2(void) { + extern int l_statstg1; + static int l_statstg2; +} + +// Thread storage duration +_Thread_local g_thrdstg1; +extern _Thread_local g_thrdstg2; +static _Thread_local g_thrdstg3; +void f3() { + extern _Thread_local l_statstg3; + static _Thread_local l_statstg4; +} + +// Struct declarations do not allocate storage, and do not have a storage +// duration. +struct s { + int m; +}; + +// Enums and enum constants are not variables and have no linkage. +enum e { E1 }; + +// Various literals: +struct s *g_statstg4 = &(struct s){0}; +char *g_statstg5 = "hello"; +void f4(void) { + 1; + "hello"; + (struct s){1}; +} \ No newline at end of file diff --git a/c/common/test/qlpack.yml b/c/common/test/qlpack.yml index 23ab835710..da30625ddb 100644 --- a/c/common/test/qlpack.yml +++ b/c/common/test/qlpack.yml @@ -1,4 +1,6 @@ -name: common-c-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: common-c-coding-standards +name: codeql/common-c-coding-standards-tests +version: 2.49.0-dev extractor: cpp +license: MIT +dependencies: + codeql/common-c-coding-standards: '*' diff --git a/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.expected b/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.expected new file mode 100644 index 0000000000..d5f6f296d9 --- /dev/null +++ b/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.expected @@ -0,0 +1,2 @@ +| test.c:5:1:5:41 | #define BAD_MACRO_WITH_ARG(x) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG contains use of parameter x used in multiple contexts. | +| test.c:6:1:6:48 | #define BAD_MACRO_WITH_ARG_TWO(x,y) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG_TWO contains use of parameter x used in multiple contexts. | diff --git a/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql b/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql new file mode 100644 index 0000000000..5aa514e86d --- /dev/null +++ b/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.amixedusemacroargumentsubjecttoexpansion.AMixedUseMacroArgumentSubjectToExpansion + +class TestFileQuery extends AMixedUseMacroArgumentSubjectToExpansionSharedQuery, TestQuery { } diff --git a/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/test.c b/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/test.c new file mode 100644 index 0000000000..7eb5e204c7 --- /dev/null +++ b/c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/test.c @@ -0,0 +1,26 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#define GOOD_MACRO_WITH_ARG(X) ((X)*X##_scale) // COMPLIANT +#define MACRO 1 +#define BAD_MACRO_WITH_ARG(x) (x) + wow##x // NON_COMPLIANT +#define BAD_MACRO_WITH_ARG_TWO(x, y) (x) + wow##x // NON_COMPLIANT +#define MACROONE(x) #x // COMPLIANT +#define MACROTWO(x) x *x // COMPLIANT +#define MACROTHREE(x) "##\"\"'" + (x) // COMPLIANT +#define FOO(x) #x MACROONE(x) // COMPLIANT - no further arg expansion + +void f() { + + int x; + int x_scale; + int y; + int wowMACRO = 0; + + y = GOOD_MACRO_WITH_ARG(x); + wowMACRO = BAD_MACRO_WITH_ARG(MACRO); + wowMACRO = BAD_MACRO_WITH_ARG_TWO(MACRO, 1); + char s[] = MACROONE(MACRO); + y = MACROTWO(MACRO); + MACROTHREE(MACRO); + FOO(x); +} \ No newline at end of file diff --git a/c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.expected b/c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.expected new file mode 100644 index 0000000000..489a990582 --- /dev/null +++ b/c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.expected @@ -0,0 +1,4 @@ +| test.c:8:14:8:17 | call to atof | Call to banned function atof. | +| test.c:9:12:9:15 | call to atoi | Call to banned function atoi. | +| test.c:10:13:10:16 | call to atol | Call to banned function atol. | +| test.c:11:18:11:22 | call to atoll | Call to banned function atoll. | diff --git a/c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql b/c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql new file mode 100644 index 0000000000..6da5fe6097 --- /dev/null +++ b/c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.atofatoiatolandatollused.AtofAtoiAtolAndAtollUsed + +class TestFileQuery extends AtofAtoiAtolAndAtollUsedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/atofatoiatolandatollused/test.c b/c/common/test/rules/atofatoiatolandatollused/test.c new file mode 100644 index 0000000000..f8140af79a --- /dev/null +++ b/c/common/test/rules/atofatoiatolandatollused/test.c @@ -0,0 +1,13 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +#include +void f2(); +void f1() { + char l1[5] = "abcd"; + float l2 = atof(l1); // NON_COMLIANT + int l3 = atoi(l1); // NON_COMPLIANT + long l4 = atol(l1); // NON_COMPLIANT + long long l5 = atoll(l1); // NON_COMPLIANT + f2(); // COMPLIANT +} diff --git a/c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.expected b/c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.expected new file mode 100644 index 0000000000..f04b1b6ce9 --- /dev/null +++ b/c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.expected @@ -0,0 +1,4 @@ +| test.c:8:7:8:8 | x1 | Bit-field 'x1' is declared on type 'int'. | +| test.c:12:15:12:16 | x5 | Bit-field 'x5' is declared on type 'signed long'. | +| test.c:14:15:14:16 | x6 | Bit-field 'x6' is declared on type 'signed char'. | +| test.c:16:14:16:15 | x7 | Bit-field 'x7' is declared on type 'Color'. | diff --git a/c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql b/c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql new file mode 100644 index 0000000000..a3e1ecc76c --- /dev/null +++ b/c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.bitfieldshallhaveanappropriatetype.BitFieldShallHaveAnAppropriateType + +class TestFileQuery extends BitFieldShallHaveAnAppropriateTypeSharedQuery, TestQuery { } diff --git a/c/common/test/rules/bitfieldshallhaveanappropriatetype/test.c b/c/common/test/rules/bitfieldshallhaveanappropriatetype/test.c new file mode 100644 index 0000000000..c418e0e4fc --- /dev/null +++ b/c/common/test/rules/bitfieldshallhaveanappropriatetype/test.c @@ -0,0 +1,17 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +typedef unsigned int UINT16; + +enum Color { R, G, B }; + +struct SampleStruct { + int x1 : 2; // NON_COMPLIANT - not explicitly signed or unsigned + unsigned int x2 : 2; // COMPLIANT - explicitly unsigned + signed int x3 : 2; // COMPLIANT - explicitly signed + UINT16 x4 : 2; // COMPLIANT - type alias resolves to a compliant type + signed long x5 : 2; // NON_COMPLIANT - cannot declare bit field for long, even + // if it's signed + signed char x6 : 2; // NON_COMPLIANT - cannot declare bit field for char, even + // if it's signed + enum Color x7 : 3; // NON_COMPLIANT - cannot declare bit field for enum +} sample_struct; diff --git a/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected new file mode 100644 index 0000000000..c318f791e9 --- /dev/null +++ b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected @@ -0,0 +1,21 @@ +| test.c:9:7:9:14 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:30:11:30:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:31:3:31:13 | (unsigned int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:31:11:31:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:33:11:33:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:34:11:34:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:35:3:35:13 | (unsigned int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:35:11:35:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:36:3:36:13 | (unsigned int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:36:11:36:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:37:3:37:13 | (unsigned int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:37:11:37:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:38:3:38:13 | (unsigned int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:38:11:38:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:39:11:39:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:40:11:40:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:41:3:41:13 | (unsigned int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:41:11:41:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:42:12:42:13 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:44:11:44:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:45:11:45:12 | (int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | diff --git a/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.clang b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.clang new file mode 100644 index 0000000000..0378c8a6b5 --- /dev/null +++ b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.clang @@ -0,0 +1,15 @@ +| test.c:9:7:9:14 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:30:3:30:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:31:3:31:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:33:3:33:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:34:3:34:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:35:3:35:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:36:3:36:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:37:3:37:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:38:3:38:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:39:3:39:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:40:3:40:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:41:3:41:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:42:3:42:14 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:44:11:44:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:45:11:45:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | diff --git a/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.gcc b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.gcc new file mode 100644 index 0000000000..f729c9e42d --- /dev/null +++ b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.gcc @@ -0,0 +1,15 @@ +| test.c:9:7:9:14 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:30:3:30:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:31:3:31:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:33:3:33:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:34:3:34:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:35:3:35:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:36:3:36:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:37:3:37:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:38:3:38:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:39:3:39:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:40:3:40:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:41:3:41:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:42:3:42:14 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:43:11:43:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:44:11:44:12 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | diff --git a/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.qcc b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.qcc new file mode 100644 index 0000000000..551423495c --- /dev/null +++ b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected.qcc @@ -0,0 +1,15 @@ +| test.c:9:7:9:14 | * ... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:30:3:30:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:31:3:31:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:33:3:33:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:34:3:34:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:35:3:35:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:36:3:36:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:37:3:37:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:38:3:38:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:39:3:39:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:40:3:40:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:41:3:41:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:42:3:42:14 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:44:3:44:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.c:45:3:45:13 | (...) | Expression not converted to `unsigned char` before converting to a larger integer type. | diff --git a/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql new file mode 100644 index 0000000000..2a1e49774f --- /dev/null +++ b/c/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.castcharbeforeconvertingtolargersizes.CastCharBeforeConvertingToLargerSizes + +class TestFileQuery extends CastCharBeforeConvertingToLargerSizesSharedQuery, TestQuery { } diff --git a/c/common/test/rules/castcharbeforeconvertingtolargersizes/test.c b/c/common/test/rules/castcharbeforeconvertingtolargersizes/test.c new file mode 100644 index 0000000000..8865e477fb --- /dev/null +++ b/c/common/test/rules/castcharbeforeconvertingtolargersizes/test.c @@ -0,0 +1,111 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +#include + +int f1() { + char *c_str; + int c; + c = *c_str++; // NON_COMPLIANT + return (c); +} + +int f2() { + unsigned char *c_str; + int c; + c = *c_str++; // COMPLIANT + return (c); +} + +int f3(void) { + char *c_str; + int c; + c = (unsigned char)*c_str++; // COMPLIANT + return (c); +} + +void f4() { + char *t; + + isalnum(*t); // NON_COMPLIANT + isalpha(*t); // NON_COMPLIANT + // isascii(*t); // Not part of the C Standard + isblank(*t); // NON_COMPLIANT + iscntrl(*t); // NON_COMPLIANT + isdigit(*t); // NON_COMPLIANT + isgraph(*t); // NON_COMPLIANT + islower(*t); // NON_COMPLIANT + isprint(*t); // NON_COMPLIANT + ispunct(*t); // NON_COMPLIANT + isspace(*t); // NON_COMPLIANT + isupper(*t); // NON_COMPLIANT + isxdigit(*t); // NON_COMPLIANT + // toascii(i); // Not part of the C Standard + toupper(*t); // NON_COMPLIANT + tolower(*t); // NON_COMPLIANT +} + +void f5() { + unsigned char *t; + + isalnum(*t); // COMPLIANT + isalpha(*t); // COMPLIANT + // isascii(*t); // Not part of the C Standard + isblank(*t); // COMPLIANT + iscntrl(*t); // COMPLIANT + isdigit(*t); // COMPLIANT + isgraph(*t); // COMPLIANT + islower(*t); // COMPLIANT + isprint(*t); // COMPLIANT + ispunct(*t); // COMPLIANT + isspace(*t); // COMPLIANT + isupper(*t); // COMPLIANT + isxdigit(*t); // COMPLIANT + // toascii(i); // Not part of the C Standard + toupper(*t); // COMPLIANT + tolower(*t); // COMPLIANT +} + +void f6() { + char *t; + + isalnum((unsigned char)*t); // COMPLIANT + isalpha((unsigned char)*t); // COMPLIANT + // isascii((unsigned char*)*t); // Not part of the C Standard + isblank((unsigned char)*t); // COMPLIANT + iscntrl((unsigned char)*t); // COMPLIANT + isdigit((unsigned char)*t); // COMPLIANT + isgraph((unsigned char)*t); // COMPLIANT + islower((unsigned char)*t); // COMPLIANT + isprint((unsigned char)*t); // COMPLIANT + ispunct((unsigned char)*t); // COMPLIANT + isspace((unsigned char)*t); // COMPLIANT + isupper((unsigned char)*t); // COMPLIANT + isxdigit((unsigned char)*t); // COMPLIANT + // toascii((unsigned int) i); // Not part of the C Standard + toupper((unsigned char)*t); // COMPLIANT + tolower((unsigned char)*t); // COMPLIANT +} + +void f7() { + int t; + + // Note these are all NON_COMPLIANT under STR37-C + + isalnum(t); // COMPLIANT + isalpha(t); // COMPLIANT + // isascii(t); // Not part of the C Standard + isblank(t); // COMPLIANT + iscntrl(t); // COMPLIANT + isdigit(t); // COMPLIANT + isgraph(t); // COMPLIANT + islower(t); // COMPLIANT + isprint(t); // COMPLIANT + ispunct(t); // COMPLIANT + isspace(t); // COMPLIANT + isupper(t); // COMPLIANT + isxdigit(t); // COMPLIANT + // toascii(i); // Not part of the C Standard + toupper(t); // COMPLIANT + tolower(t); // COMPLIANT +} diff --git a/c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.expected b/c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.expected similarity index 100% rename from c/cert/test/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.expected rename to c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.expected diff --git a/c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.ql b/c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.ql new file mode 100644 index 0000000000..9e657b351a --- /dev/null +++ b/c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.closefilehandlewhennolongerneededshared.CloseFileHandleWhenNoLongerNeededShared + +class TestFileQuery extends CloseFileHandleWhenNoLongerNeededSharedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/closefilehandlewhennolongerneededshared/test.c b/c/common/test/rules/closefilehandlewhennolongerneededshared/test.c new file mode 100644 index 0000000000..a3ba27ce9f --- /dev/null +++ b/c/common/test/rules/closefilehandlewhennolongerneededshared/test.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +int f1a(const char *filename) { + FILE *f = fopen(filename, "r"); // NON_COMPLIANT + if (NULL == f) { + return -1; + } + /* ... */ + return 0; +} + +int f1b(const char *filename) { + FILE *f = freopen(filename, "w+", stdout); // NON_COMPLIANT + if (NULL == f) { + return -1; + } + /* ... */ + return 0; +} + +void f1c(const char *filename) { + FILE *f = freopen(filename, "w+", stdout); // NON_COMPLIANT + if (NULL == f) { + return; + } + /* ... */ + // FILE* out of scope not closed +} + +int f2a(const char *filename) { + FILE *f = fopen(filename, "r"); // COMPLIANT + if (NULL == f) { + return -1; + } + /* ... */ + if (fclose(f) == EOF) { + return -1; + } + return 0; +} + +int f2b(const char *filename) { + FILE *f = fopen(filename, "r"); // NON_COMPLIANT + if (NULL == f) { + return -1; + } + if (filename) { + if (fclose(f) == EOF) { + return -1; + } + } + // file not closed on this path + return 0; +} + +// scope prolonged +int f2c(const char *filename) { + FILE *g; + { + FILE *f = fopen(filename, "r"); // COMPLIANT + if (NULL == f) { + return -1; + } + /* ... */ + g = f; + // f out of scope + } + if (fclose(g) == EOF) { + return -1; + } + return 0; +} + +// interprocedural +int closing_helper(FILE *g) { + if (fclose(g) == EOF) { + return -1; + } + return 0; +} +int f2inter(const char *filename) { + FILE *f = fopen(filename, "r"); // COMPLIANT[FALSE_POSITIVE] + if (NULL == f) { + return -1; + } + return closing_helper(f); +} + +int f2cfg(const char *filename) { + FILE *f = freopen(filename, "r", stdout); // NON_COMPLIANT + if (NULL == f) { + return -1; + } + if (filename) { + if (fclose(f) == EOF) { + return -1; + } + } + // file not closed on one path + return 0; +} + +int f3(const char *filename) { + FILE *f = fopen(filename, "w"); // NON_COMPLIANT + if (NULL == f) { + exit(EXIT_FAILURE); + } + /* ... */ + exit(EXIT_SUCCESS); +} + +int f4(const char *filename) { + FILE *f = fopen(filename, "w"); // COMPLIANT + if (NULL == f) { + /* Handle error */ + } + /* ... */ + if (fclose(f) == EOF) { + /* Handle error */ + } + exit(EXIT_SUCCESS); +} + +int f5(const char *filename) { + int fd = open(filename, O_RDONLY, S_IRUSR); // NON_COMPLIANT + if (-1 == fd) { + return -1; + } + /* ... */ + return 0; +} + +int f6(const char *filename) { + int fd = open(filename, O_RDONLY, S_IRUSR); // COMPLIANT + if (-1 == fd) { + return -1; + } + /* ... */ + if (-1 == close(fd)) { + return -1; + } + return 0; +} diff --git a/c/common/test/rules/commaoperatorused/CommaOperatorUsed.ql b/c/common/test/rules/commaoperatorused/CommaOperatorUsed.ql index 53d559ae8b..2fe294762e 100644 --- a/c/common/test/rules/commaoperatorused/CommaOperatorUsed.ql +++ b/c/common/test/rules/commaoperatorused/CommaOperatorUsed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.commaoperatorused.CommaOperatorUsed + +class TestFileQuery extends CommaOperatorUsedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.expected b/c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.expected new file mode 100644 index 0000000000..614ad8a9de --- /dev/null +++ b/c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.expected @@ -0,0 +1,13 @@ +| test.c:11:7:11:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:12:7:12:18 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:18:7:18:19 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:19:7:19:19 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:25:7:25:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:26:7:26:17 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:33:7:33:20 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:34:7:34:20 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:40:7:40:19 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:41:7:41:19 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:47:7:47:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:48:7:48:17 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.c:53:20:53:31 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | diff --git a/c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql b/c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql new file mode 100644 index 0000000000..c77ee1c66a --- /dev/null +++ b/c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.constantunsignedintegerexpressionswraparound.ConstantUnsignedIntegerExpressionsWrapAround + +class TestFileQuery extends ConstantUnsignedIntegerExpressionsWrapAroundSharedQuery, TestQuery { } diff --git a/c/common/test/rules/constantunsignedintegerexpressionswraparound/test.c b/c/common/test/rules/constantunsignedintegerexpressionswraparound/test.c new file mode 100644 index 0000000000..a779b86b94 --- /dev/null +++ b/c/common/test/rules/constantunsignedintegerexpressionswraparound/test.c @@ -0,0 +1,55 @@ +#include + +// UINT_MIN and UULONG_MIN isn't defined, but it's going to be zero +#define UINT_MIN ((unsigned int)0) +#define UULONG_MIN ((unsigned long long)0) + +void test_unsigned_int() { + unsigned int a; + a = 1 + 1; // COMPLIANT - signed integer + a = 0 - 1; // COMPLIANT - signed integer + a = UINT_MIN - 1; // NON_COMPLIANT + a = UINT_MAX + 1; // NON_COMPLIANT + + const unsigned int const_min = UINT_MIN; + const unsigned int const_max = UINT_MAX; + a = const_min + 1; // COMPLIANT + a = const_max - 1; // COMPLIANT + a = const_min - 1; // NON_COMPLIANT + a = const_max + 1; // NON_COMPLIANT + +#define UNDERFLOW(x) (UINT_MIN - (x)) +#define OVERFLOW(x) (UINT_MAX + (x)) + a = UNDERFLOW(0); // COMPLIANT + a = OVERFLOW(0); // COMPLIANT + a = UNDERFLOW(1); // NON_COMPLIANT + a = OVERFLOW(1); // NON_COMPLIANT +} + +void test_long_long() { + unsigned long long a; + a = 1 + 1; // COMPLIANT + a = 0 - 1; // COMPLIANT + a = UULONG_MIN - 1; // NON_COMPLIANT + a = ULLONG_MAX + 1; // NON_COMPLIANT + + const unsigned long long const_min = UULONG_MIN; + const unsigned long long const_max = ULLONG_MAX; + a = const_min + 1; // COMPLIANT + a = const_max - 1; // COMPLIANT + a = const_min - 1; // NON_COMPLIANT + a = const_max + 1; // NON_COMPLIANT + +#define UNDERFLOW(x) (UULONG_MIN - (x)) +#define OVERFLOW(x) (ULLONG_MAX + (x)) + a = UNDERFLOW(0); // COMPLIANT + a = OVERFLOW(0); // COMPLIANT + a = UNDERFLOW(1); // NON_COMPLIANT + a = OVERFLOW(1); // NON_COMPLIANT +} + +void test_conversion() { + signed int a = + (signed int)(UINT_MAX + 1); // NON_COMPLIANT - still an unsigned integer + // constant expression +} \ No newline at end of file diff --git a/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected b/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected index 76be777469..d7dfc6c13f 100644 --- a/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected +++ b/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected @@ -1,20 +1,20 @@ problems -| test.c:8:8:8:12 | c_str | The object returned by the function getenv should not be modified. | test.c:15:16:15:21 | call to getenv | call to getenv | test.c:8:8:8:12 | c_str | c_str | -| test.c:64:5:64:9 | conv4 | The object returned by the function localeconv should not be modified. | test.c:61:11:61:20 | call to localeconv | call to localeconv | test.c:64:5:64:9 | conv4 | conv4 | -| test.c:73:5:73:8 | conv | The object returned by the function localeconv should not be modified. | test.c:69:25:69:34 | call to localeconv | call to localeconv | test.c:73:5:73:8 | conv | conv | +| test.c:11:8:11:12 | c_str | test.c:18:16:18:21 | call to getenv | test.c:11:8:11:12 | c_str | The object returned by the function getenv should not be modified. | +| test.c:67:5:67:9 | conv4 | test.c:64:11:64:20 | call to localeconv | test.c:67:5:67:9 | conv4 | The object returned by the function localeconv should not be modified. | +| test.c:76:5:76:8 | conv | test.c:72:25:72:34 | call to localeconv | test.c:76:5:76:8 | conv | The object returned by the function localeconv should not be modified. | edges -| test.c:5:18:5:22 | c_str | test.c:8:8:8:12 | c_str | -| test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | -| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | -| test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | -| test.c:69:25:69:34 | call to localeconv | test.c:73:5:73:8 | conv | +| test.c:8:18:8:22 | c_str | test.c:11:8:11:12 | c_str | provenance | | +| test.c:18:16:18:21 | call to getenv | test.c:24:9:24:12 | env1 | provenance | | +| test.c:24:9:24:12 | env1 | test.c:8:18:8:22 | c_str | provenance | | +| test.c:64:11:64:20 | call to localeconv | test.c:67:5:67:9 | conv4 | provenance | | +| test.c:72:25:72:34 | call to localeconv | test.c:76:5:76:8 | conv | provenance | | nodes -| test.c:5:18:5:22 | c_str | semmle.label | c_str | -| test.c:8:8:8:12 | c_str | semmle.label | c_str | -| test.c:15:16:15:21 | call to getenv | semmle.label | call to getenv | -| test.c:21:9:21:12 | env1 | semmle.label | env1 | -| test.c:61:11:61:20 | call to localeconv | semmle.label | call to localeconv | -| test.c:64:5:64:9 | conv4 | semmle.label | conv4 | -| test.c:69:25:69:34 | call to localeconv | semmle.label | call to localeconv | -| test.c:73:5:73:8 | conv | semmle.label | conv | +| test.c:8:18:8:22 | c_str | semmle.label | c_str | +| test.c:11:8:11:12 | c_str | semmle.label | c_str | +| test.c:18:16:18:21 | call to getenv | semmle.label | call to getenv | +| test.c:24:9:24:12 | env1 | semmle.label | env1 | +| test.c:64:11:64:20 | call to localeconv | semmle.label | call to localeconv | +| test.c:67:5:67:9 | conv4 | semmle.label | conv4 | +| test.c:72:25:72:34 | call to localeconv | semmle.label | call to localeconv | +| test.c:76:5:76:8 | conv | semmle.label | conv | subpaths diff --git a/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql b/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql index 29386391e5..53c27eb3ce 100644 --- a/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql +++ b/c/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.constlikereturnvalue.ConstLikeReturnValue + +class TestFileQuery extends ConstLikeReturnValueSharedQuery, TestQuery { } diff --git a/c/common/test/rules/constlikereturnvalue/test.c b/c/common/test/rules/constlikereturnvalue/test.c index cd7c101898..e28c05961f 100644 --- a/c/common/test/rules/constlikereturnvalue/test.c +++ b/c/common/test/rules/constlikereturnvalue/test.c @@ -1,4 +1,7 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. #include +#include #include #include diff --git a/c/common/test/rules/deadcode/DeadCode.expected b/c/common/test/rules/deadcode/DeadCode.expected new file mode 100644 index 0000000000..7757e2087d --- /dev/null +++ b/c/common/test/rules/deadcode/DeadCode.expected @@ -0,0 +1,11 @@ +| test.c:18:3:18:27 | declaration | This statement is dead code. | +| test.c:19:3:19:12 | ExprStmt | This statement is dead code. | +| test.c:20:3:20:12 | ExprStmt | This statement is dead code. | +| test.c:22:3:24:3 | if (...) ... | This statement is dead code. | +| test.c:34:3:35:3 | if (...) ... | This statement is dead code. | +| test.c:37:3:37:4 | { ... } | This statement is dead code. | +| test.c:38:3:40:3 | { ... } | This statement is dead code. | +| test.c:54:6:55:3 | { ... } | This statement is dead code. | +| test.c:65:46:66:3 | { ... } | This statement is dead code. | +| test.c:69:3:69:8 | ExprStmt | This statement is dead code. | +| test.c:71:3:71:21 | ExprStmt | This statement is dead code. | diff --git a/c/common/test/rules/deadcode/DeadCode.ql b/c/common/test/rules/deadcode/DeadCode.ql new file mode 100644 index 0000000000..dcd7fce840 --- /dev/null +++ b/c/common/test/rules/deadcode/DeadCode.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.deadcode.DeadCode + +class TestFileQuery extends DeadCodeSharedQuery, TestQuery { } diff --git a/c/common/test/rules/deadcode/test.c b/c/common/test/rules/deadcode/test.c new file mode 100644 index 0000000000..87ec74d924 --- /dev/null +++ b/c/common/test/rules/deadcode/test.c @@ -0,0 +1,74 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +int may_have_side_effects(); +int no_side_effects(int x) { return 1 + 2; } +int no_side_effects_nondeterministic(); + +int test_dead_code(int x) { + int live1 = may_have_side_effects(), + live2 = may_have_side_effects(); // COMPLIANT + int live3 = 0, + live4 = may_have_side_effects(); // COMPLIANT + int live5 = 0, live6 = 0; // COMPLIANT + live5 = 1; // COMPLIANT + live6 = 2; // COMPLIANT + + int dead1 = 0, dead2 = 0; // NON_COMPLIANT + dead1 = 1; // NON_COMPLIANT - useless assignment + dead2 = 1; // NON_COMPLIANT - useless assignment + + if (false) { // NON_COMPLIANT + dead2 = 10; // Only used in dead or unreachable code + } + + if (true) { // COMPLIANT + may_have_side_effects(); + } + + if (may_have_side_effects()) { // COMPLIANT + may_have_side_effects(); + } + + if (true) { // NON_COMPLIANT + } + + {} // NON_COMPLIANT + { // NON_COMPLIANT + 1 + 2; + } + + { // COMPLIANT + may_have_side_effects(); + } + + do { // COMPLIANT + may_have_side_effects(); + } while (may_have_side_effects()); + + do { // COMPLIANT + may_have_side_effects(); + } while (may_have_side_effects()); + + do { // NON_COMPLIANT + } while (no_side_effects_nondeterministic()); + + while (may_have_side_effects()) { // COMPLIANT + may_have_side_effects(); + } + + while (may_have_side_effects()) { // COMPLIANT + may_have_side_effects(); + } + + while (no_side_effects_nondeterministic()) { // NON_COMPLIANT + } + + may_have_side_effects(); // COMPLIANT + 1 + 2; // NON_COMPLIANT + + no_side_effects(x); // NON_COMPLIANT + + return live5 + live6; // COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.expected b/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.expected index 08e1688eb1..585c6d4c94 100644 --- a/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.expected +++ b/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.expected @@ -1,4 +1,3 @@ -| file://:0:0:0:0 | __va_list_tag | Reserved identifier '__va_list_tag' is declared. | | test.c:2:1:2:23 | #define _RESERVED_MACRO | Reserved identifier '_RESERVED_MACRO' is declared. | | test.c:11:8:11:9 | _s | Reserved identifier '_s' is declared. | | test.c:15:6:15:7 | _f | Reserved identifier '_f' is declared. | diff --git a/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.ql b/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.ql index c53a7c44b2..f091b0aaaa 100644 --- a/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.ql +++ b/c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.declaredareservedidentifier.DeclaredAReservedIdentifier + +class TestFileQuery extends DeclaredAReservedIdentifierSharedQuery, TestQuery { } diff --git a/c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.expected b/c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.expected new file mode 100644 index 0000000000..51136cfb5d --- /dev/null +++ b/c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.expected @@ -0,0 +1,2 @@ +| test.c:11:4:11:5 | l1 | Null may be dereferenced here because a null value was assigned $@. | test.c:4:21:4:21 | 0 | here | +| test.c:18:6:18:7 | l1 | Null may be dereferenced here because a null value was assigned $@. | test.c:4:21:4:21 | 0 | here | diff --git a/c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql b/c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql new file mode 100644 index 0000000000..c8dc62e67c --- /dev/null +++ b/c/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.dereferenceofnullpointer.DereferenceOfNullPointer + +class TestFileQuery extends DereferenceOfNullPointerSharedQuery, TestQuery { } diff --git a/c/common/test/rules/dereferenceofnullpointer/test.c b/c/common/test/rules/dereferenceofnullpointer/test.c new file mode 100644 index 0000000000..22314a8960 --- /dev/null +++ b/c/common/test/rules/dereferenceofnullpointer/test.c @@ -0,0 +1,31 @@ +#include + +void test_null(int p1) { + int *l1 = (void *)0; + + if (p1 > 10) { + // l1 is only conditionally initialized + l1 = malloc(10 * sizeof(int)); + } + + *l1; // NON_COMPLIANT - dereferenced and still null + + if (l1) { + *l1; // COMPLIANT - null check before dereference + } + + if (!l1) { + *l1; // NON_COMPLIANT - dereferenced and definitely null + } else { + *l1; // COMPLIANT - null check before dereference + } + + free(l1); // COMPLIANT - free of `NULL` is not undefined behavior +} + +void test_default_value_init() { + int *l1; // indeterminate and thus invalid but non-null state + + *l1; // COMPLIANT - considered an uninitialized pointer, + // not a null pointer +} \ No newline at end of file diff --git a/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected b/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected new file mode 100644 index 0000000000..64a72fe3b9 --- /dev/null +++ b/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected @@ -0,0 +1,31 @@ +| test.c:4:5:4:13 | case1_FOO | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:4:5:4:13 | case1_FOO | case1_FOO | test.c:3:5:3:13 | case1_foo | case1_foo | +| test.c:4:5:4:13 | case1_FOO | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:4:5:4:13 | case1_FOO | case1_FOO | test.c:5:5:5:13 | case1_fOo | case1_fOo | +| test.c:5:5:5:13 | case1_fOo | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:5:5:5:13 | case1_fOo | case1_fOo | test.c:3:5:3:13 | case1_foo | case1_foo | +| test.c:8:5:8:15 | case2_f_o_o | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:8:5:8:15 | case2_f_o_o | case2_f_o_o | test.c:7:5:7:13 | case2_foo | case2_foo | +| test.c:11:5:11:13 | case3_fO0 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:11:5:11:13 | case3_fO0 | case3_fO0 | test.c:10:5:10:13 | case3_fOO | case3_fOO | +| test.c:13:5:13:12 | case4_II | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:13:5:13:12 | case4_II | case4_II | test.c:15:5:15:12 | case4_Il | case4_Il | +| test.c:14:5:14:12 | case4_I1 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:14:5:14:12 | case4_I1 | case4_I1 | test.c:13:5:13:12 | case4_II | case4_II | +| test.c:14:5:14:12 | case4_I1 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:14:5:14:12 | case4_I1 | case4_I1 | test.c:15:5:15:12 | case4_Il | case4_Il | +| test.c:18:5:18:11 | case5_5 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:18:5:18:11 | case5_5 | case5_5 | test.c:17:5:17:11 | case5_S | case5_S | +| test.c:21:5:21:11 | case6_2 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:21:5:21:11 | case6_2 | case6_2 | test.c:20:5:20:11 | case6_Z | case6_Z | +| test.c:24:5:24:11 | case7_h | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:24:5:24:11 | case7_h | case7_h | test.c:23:5:23:11 | case7_n | case7_n | +| test.c:27:5:27:11 | case8_8 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:27:5:27:11 | case8_8 | case8_8 | test.c:26:5:26:11 | case8_B | case8_B | +| test.c:30:5:30:11 | case9_m | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:30:5:30:11 | case9_m | case9_m | test.c:29:5:29:12 | case9_rn | case9_rn | +| test.c:31:5:31:12 | case9_rh | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:31:5:31:12 | case9_rh | case9_rh | test.c:29:5:29:12 | case9_rn | case9_rn | +| test.c:34:5:34:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:34:5:34:15 | case10_xmmx | case10_xmmx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:34:5:34:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:34:5:34:15 | case10_xmmx | case10_xmmx | test.c:35:5:35:16 | case10_xmrnx | case10_xmrnx | +| test.c:34:5:34:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:34:5:34:15 | case10_xmmx | case10_xmmx | test.c:36:5:36:16 | case10_xrnmx | case10_xrnmx | +| test.c:35:5:35:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:35:5:35:16 | case10_xmrnx | case10_xmrnx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:35:5:35:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:35:5:35:16 | case10_xmrnx | case10_xmrnx | test.c:36:5:36:16 | case10_xrnmx | case10_xrnmx | +| test.c:35:5:35:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:35:5:35:16 | case10_xmrnx | case10_xmrnx | test.c:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | +| test.c:36:5:36:16 | case10_xrnmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:36:5:36:16 | case10_xrnmx | case10_xrnmx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:37:5:37:17 | case10_xrnrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:38:5:38:17 | case10_xrhrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:38:5:38:17 | case10_xrhrhx | case10_xrhrhx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:38:5:38:17 | case10_xrhrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:38:5:38:17 | case10_xrhrhx | case10_xrhrhx | test.c:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | +| test.c:39:5:39:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:39:5:39:16 | case10_xmrhx | case10_xmrhx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:39:5:39:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:39:5:39:16 | case10_xmrhx | case10_xmrhx | test.c:35:5:35:16 | case10_xmrnx | case10_xmrnx | +| test.c:39:5:39:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:39:5:39:16 | case10_xmrhx | case10_xmrhx | test.c:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | +| test.c:40:5:40:16 | case10_xrhmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:40:5:40:16 | case10_xrhmx | case10_xrhmx | test.c:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.c:40:5:40:16 | case10_xrhmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:40:5:40:16 | case10_xrhmx | case10_xrhmx | test.c:36:5:36:16 | case10_xrnmx | case10_xrnmx | +| test.c:42:15:42:22 | case11_O | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:42:15:42:22 | case11_O | case11_O | test.c:42:5:42:12 | case11_o | case11_o | +| test.c:45:5:45:14 | case12_8bB | The identifier $@ is not typographically unambiguous from the identifier $@ | test.c:45:5:45:14 | case12_8bB | case12_8bB | test.c:44:5:44:14 | case12_BBb | case12_BBb | diff --git a/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql b/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql new file mode 100644 index 0000000000..64357d95de --- /dev/null +++ b/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.differentidentifiersnottypographicallyunambiguous.DifferentIdentifiersNotTypographicallyUnambiguous + +class TestFileQuery extends DifferentIdentifiersNotTypographicallyUnambiguousSharedQuery, TestQuery { +} diff --git a/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.c b/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.c new file mode 100644 index 0000000000..c6bfd9ff9b --- /dev/null +++ b/c/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.c @@ -0,0 +1,67 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +int case1_foo; +int case1_FOO; // NON_COMPLIANT +int case1_fOo; // NON_COMPLIANT + +int case2_foo; +int case2_f_o_o; // NON_COMPLIANT + +int case3_fOO; +int case3_fO0; // NON_COMPLIANT + +int case4_II; +int case4_I1; // NON_COMPLIANT +int case4_Il; // NON_COMPLIANT + +int case5_S; +int case5_5; // NON_COMPLIANT + +int case6_Z; +int case6_2; // NON_COMPLIANT + +int case7_n; +int case7_h; // NON_COMPLIANT + +int case8_B; +int case8_8; // NON_COMPLIANT + +int case9_rn; +int case9_m; // NON_COMPLIANT +int case9_rh; // NON_COMPLIANT + +int case10_xrnrnx; +int case10_xmmx; // NON_COMPLIANT +int case10_xmrnx; // NON_COMPLIANT +int case10_xrnmx; // NON_COMPLIANT +int case10_xrnrhx; // NON_COMPLIANT +int case10_xrhrhx; // NON_COMPLIANT +int case10_xmrhx; // NON_COMPLIANT +int case10_xrhmx; // NON_COMPLIANT + +int case11_o, case11_O; // NON_COMPLIANT + +int case12_BBb; +int case12_8bB; // NON_COMPLIANT + +// Transitive rules are compliant + +// m -> rn -> rh +int case13_m; +int case13_rh; // COMPLIANT + +// b -> B -> 8 +int case14_b; +int case14_8; // COMPLIANT + +// z -> Z -> 2 +int case15_z; +int case15_2; // COMPLIANT + +// s -> S -> 5 +int case16_s; +int case16_5; // COMPLIANT + +// o -> O -> 0 +int case17_o; +int case17_0; \ No newline at end of file diff --git a/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.expected.qcc b/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.expected.qcc new file mode 100644 index 0000000000..24d904e104 --- /dev/null +++ b/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.expected.qcc @@ -0,0 +1,11 @@ +| test.c:6:3:6:8 | call to printf | Access of closed file& ... which was closed at $@ | test.c:4:3:4:8 | call to fclose | this location. | +| test.c:7:3:7:6 | call to puts | Access of closed file& ... which was closed at $@ | test.c:4:3:4:8 | call to fclose | this location. | +| test.c:16:13:16:18 | _Stderr | Access of closed file& ... which was closed at $@ | test.c:13:3:13:8 | call to fclose | this location. | +| test.c:17:3:17:8 | call to perror | Access of closed file& ... which was closed at $@ | test.c:13:3:13:8 | call to fclose | this location. | +| test.c:24:8:24:12 | _Stdin | Access of closed file& ... which was closed at $@ | test.c:22:3:22:8 | call to fclose | this location. | +| test.c:25:3:25:9 | call to getchar | Access of closed file& ... which was closed at $@ | test.c:22:3:22:8 | call to fclose | this location. | +| test.c:34:18:34:18 | f | Access of closed filef which was closed at $@ | test.c:31:3:31:8 | call to fclose | this location. | +| test.c:35:15:35:15 | f | Access of closed filef which was closed at $@ | test.c:31:3:31:8 | call to fclose | this location. | +| test.c:70:11:70:12 | fp | Access of closed filefp which was closed at $@ | test.c:69:3:69:8 | call to fclose | this location. | +| test.c:71:7:71:8 | fp | Access of closed filefp which was closed at $@ | test.c:69:3:69:8 | call to fclose | this location. | +| test.c:81:17:81:17 | f | Access of closed filef which was closed at $@ | test.c:80:3:80:8 | call to fclose | this location. | diff --git a/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.ql b/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.ql index 430e258ae0..d3b8b9ea3a 100644 --- a/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.ql +++ b/c/common/test/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotaccessaclosedfile.DoNotAccessAClosedFile + +class TestFileQuery extends DoNotAccessAClosedFileSharedQuery, TestQuery { } diff --git a/c/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql b/c/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql index 43bc4286e6..ceae7e6a9e 100644 --- a/c/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql +++ b/c/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotallowamutextogooutofscopewhilelocked.DoNotAllowAMutexToGoOutOfScopeWhileLocked + +class TestFileQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql b/c/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql index 9f0c40ef4c..9955572e73 100644 --- a/c/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql +++ b/c/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql @@ -1,2 +1,5 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotcopyaddressofautostorageobjecttootherobject.DoNotCopyAddressOfAutoStorageObjectToOtherObject + +class TestFileQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery, TestQuery { +} diff --git a/c/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql b/c/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql index f3ba1667a8..96ea58009e 100644 --- a/c/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql +++ b/c/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotdestroyamutexwhileitislocked.DoNotDestroyAMutexWhileItIsLocked + +class TestFileQuery extends DoNotDestroyAMutexWhileItIsLockedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.expected b/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.expected new file mode 100644 index 0000000000..4d4c20a39c --- /dev/null +++ b/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.expected @@ -0,0 +1,6 @@ +| test.c:59:3:59:6 | call to copy | Call to 'copy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:59:13:59:15 | & ... | aliased pointer | test.c:59:8:59:10 | & ... | restrict-qualified parameter | test.c:59:8:59:10 | & ... | addressof1 | test.c:59:13:59:15 | & ... | addressof2 | +| test.c:65:3:65:6 | call to copy | Call to 'copy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:65:15:65:19 | & ... | aliased pointer | test.c:65:8:65:12 | & ... | restrict-qualified parameter | test.c:65:8:65:12 | & ... | addressof1 | test.c:65:15:65:19 | & ... | addressof2 | +| test.c:67:3:67:6 | call to copy | Call to 'copy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:67:15:67:16 | px | aliased pointer | test.c:67:8:67:12 | & ... | restrict-qualified parameter | test.c:67:8:67:12 | & ... | addressof1 | test.c:63:13:63:17 | & ... | addressof2 | +| test.c:73:3:73:8 | call to strcpy | Call to 'strcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:73:15:73:21 | ... + ... | aliased pointer | test.c:73:10:73:12 | & ... | restrict-qualified parameter | test.c:73:10:73:12 | & ... | addressof1 | test.c:73:15:73:17 | & ... | addressof2 | +| test.c:80:3:80:8 | call to memcpy | Call to 'memcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:80:15:80:21 | ... + ... | aliased pointer | test.c:80:10:80:12 | & ... | restrict-qualified parameter | test.c:80:10:80:12 | & ... | addressof1 | test.c:80:15:80:17 | & ... | addressof2 | +| test.c:93:3:93:7 | call to scanf | Call to 'scanf' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:93:14:93:20 | ... + ... | aliased pointer | test.c:93:9:93:11 | & ... | restrict-qualified parameter | test.c:93:9:93:11 | & ... | addressof1 | test.c:93:14:93:16 | & ... | addressof2 | diff --git a/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql b/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql new file mode 100644 index 0000000000..dc3a521edf --- /dev/null +++ b/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql @@ -0,0 +1,6 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared + +class TestFileQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery, + TestQuery +{ } diff --git a/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/test.c b/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/test.c new file mode 100644 index 0000000000..3bf7cfa490 --- /dev/null +++ b/c/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/test.c @@ -0,0 +1,100 @@ +#include +#include +#include + +int *restrict g1; +int *restrict g2; +int *restrict g1_1; +int *g2_1; + +struct s1 { + int x, y, z; +}; +struct s1 v1; + +void test_global_local() { + int *restrict i1 = g1; // COMPLIANT + int *restrict i2 = g2; // COMPLIANT + int *restrict i3 = i2; // NON_COMPLIANT + g1 = g2; // NON_COMPLIANT + i1 = i2; // NON_COMPLIANT + { + int *restrict i4; + int *restrict i5; + int *restrict i6; + i4 = g1; // COMPLIANT + i4 = (void *)0; // COMPLIANT + i5 = g1; // NON_COMPLIANT - block rather than statement scope matters + i4 = g1; // NON_COMPLIANT + i6 = g2; // COMPLIANT + } +} + +void test_global_local_1() { + g1_1 = g2_1; // COMPLIANT +} + +void test_structs() { + struct s1 *restrict p1 = &v1; + int *restrict px = &v1.x; // NON_COMPLIANT + { + int *restrict py; + int *restrict pz; + py = &v1.y; // COMPLIANT + py = (int *)0; + pz = &v1.z; // NON_COMPLIANT - block rather than statement scope matters + py = &v1.y; // NON_COMPLIANT + } +} + +void copy(int *restrict p1, int *restrict p2, size_t s) { + for (size_t i = 0; i < s; ++i) { + p2[i] = p1[i]; + } +} + +void test_restrict_params() { + int i1 = 1; + int i2 = 2; + copy(&i1, &i1, 1); // NON_COMPLIANT + copy(&i1, &i2, 1); // COMPLIANT + + int x[10]; + int *px = &x[0]; + copy(&x[0], &x[1], 1); // COMPLIANT - non overlapping + copy(&x[0], &x[1], 2); // NON_COMPLIANT - overlapping + copy(&x[0], (int *)x[0], 1); // COMPLIANT - non overlapping + copy(&x[0], px, 1); // NON_COMPLIANT - overlapping +} + +void test_strcpy() { + char s1[] = "my test string"; + char s2[] = "my other string"; + strcpy(&s1, &s1 + 3); // NON_COMPLIANT + strcpy(&s2, &s1); // COMPLIANT +} + +void test_memcpy() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memcpy(&s1, &s1 + 3, 5); // NON_COMPLIANT + memcpy(&s2, &s1 + 3, 5); // COMPLIANT +} + +void test_memmove() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memmove(&s1, &s1 + 3, 5); // COMPLIANT - memmove is allowed to overlap + memmove(&s2, &s1 + 3, 5); // COMPLIANT +} + +void test_scanf() { + char s1[200] = "%10s"; + scanf(&s1, &s1 + 4); // NON_COMPLIANT +} + +// TODO also consider the following: +// strncpy(), strncpy_s() +// strcat(), strcat_s() +// strncat(), strncat_s() +// strtok_s() \ No newline at end of file diff --git a/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected b/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected index 0011556fd0..75866b8503 100644 --- a/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected +++ b/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected @@ -4,15 +4,19 @@ problems | test.c:13:10:13:11 | p4 | test.c:5:14:5:15 | l2 | test.c:13:10:13:11 | p4 | Subtraction between left operand pointing to array $@ and other operand pointing to array $@. | test.c:3:7:3:8 | l2 | l2 | test.c:2:7:2:8 | l1 | l1 | | test.c:13:15:13:16 | l1 | test.c:13:15:13:16 | l1 | test.c:13:15:13:16 | l1 | Subtraction between right operand pointing to array $@ and other operand pointing to array $@. | test.c:2:7:2:8 | l1 | l1 | test.c:3:7:3:8 | l2 | l2 | edges -| test.c:4:14:4:15 | l1 | test.c:10:10:10:11 | p1 | -| test.c:4:14:4:15 | l1 | test.c:12:10:12:11 | p1 | -| test.c:5:14:5:15 | l2 | test.c:11:10:11:11 | p2 | -| test.c:5:14:5:15 | l2 | test.c:12:15:12:16 | p2 | -| test.c:5:14:5:15 | l2 | test.c:13:10:13:11 | p4 | -| test.c:5:14:5:15 | l2 | test.c:14:10:14:11 | p4 | +| test.c:4:14:4:15 | l1 | test.c:4:14:4:18 | access to array | provenance | Config | +| test.c:4:14:4:18 | access to array | test.c:10:10:10:11 | p1 | provenance | | +| test.c:4:14:4:18 | access to array | test.c:12:10:12:11 | p1 | provenance | | +| test.c:5:14:5:15 | l2 | test.c:5:14:5:19 | access to array | provenance | Config | +| test.c:5:14:5:19 | access to array | test.c:11:10:11:11 | p2 | provenance | | +| test.c:5:14:5:19 | access to array | test.c:12:15:12:16 | p2 | provenance | | +| test.c:5:14:5:19 | access to array | test.c:13:10:13:11 | p4 | provenance | | +| test.c:5:14:5:19 | access to array | test.c:14:10:14:11 | p4 | provenance | | nodes | test.c:4:14:4:15 | l1 | semmle.label | l1 | +| test.c:4:14:4:18 | access to array | semmle.label | access to array | | test.c:5:14:5:15 | l2 | semmle.label | l2 | +| test.c:5:14:5:19 | access to array | semmle.label | access to array | | test.c:10:10:10:11 | p1 | semmle.label | p1 | | test.c:10:15:10:16 | l1 | semmle.label | l1 | | test.c:11:10:11:11 | p2 | semmle.label | p2 | diff --git a/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql b/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql index bf47bf28f1..374a6fc52b 100644 --- a/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql +++ b/c/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotsubtractpointersaddressingdifferentarrays.DoNotSubtractPointersAddressingDifferentArrays + +class TestFileQuery extends DoNotSubtractPointersAddressingDifferentArraysSharedQuery, TestQuery { } diff --git a/c/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql b/c/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql index 6fdfb9c928..edef2c1127 100644 --- a/c/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql +++ b/c/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotusemorethantwolevelsofpointerindirection.DoNotUseMoreThanTwoLevelsOfPointerIndirection + +class TestFileQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery, TestQuery { } diff --git a/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected b/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected index 1d487765df..bc471c0dc4 100644 --- a/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected +++ b/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected @@ -1,5 +1,5 @@ -| test.c:4:13:4:18 | ... + ... | Array pointer p2 points 1 element passed the end of $@. | test.c:2:7:2:8 | l1 | l1 | -| test.c:5:13:5:18 | ... + ... | Array pointer p3 points 1 element passed the end of $@. | test.c:2:7:2:8 | l1 | l1 | -| test.c:6:13:6:18 | & ... | Array pointer p4 points 1 element passed the end of $@. | test.c:2:7:2:8 | l1 | l1 | -| test.c:11:8:11:11 | ... -- | Array pointer p7 points 1 element passed the end of $@. | test.c:2:7:2:8 | l1 | l1 | -| test.c:12:8:12:9 | p3 | Array pointer p8 points 1 element passed the end of $@. | test.c:2:7:2:8 | l1 | l1 | +| test.c:4:13:4:18 | ... + ... | Array pointer p2 points 1 element past the end of $@. | test.c:2:7:2:8 | l1 | l1 | +| test.c:5:13:5:18 | ... + ... | Array pointer p3 points 1 element past the end of $@. | test.c:2:7:2:8 | l1 | l1 | +| test.c:6:13:6:18 | & ... | Array pointer p4 points 1 element past the end of $@. | test.c:2:7:2:8 | l1 | l1 | +| test.c:11:8:11:11 | ... -- | Array pointer p7 points 1 element past the end of $@. | test.c:2:7:2:8 | l1 | l1 | +| test.c:12:8:12:9 | p3 | Array pointer p8 points 1 element past the end of $@. | test.c:2:7:2:8 | l1 | l1 | diff --git a/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql b/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql index b06daa52b7..c0a3435e05 100644 --- a/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql +++ b/c/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql @@ -1,2 +1,5 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotusepointerarithmetictoaddressdifferentarrays.DoNotUsePointerArithmeticToAddressDifferentArrays + +class TestFileQuery extends DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery, TestQuery { +} diff --git a/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected b/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected new file mode 100644 index 0000000000..3bbffedcca --- /dev/null +++ b/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected @@ -0,0 +1 @@ +| test.c:5:47:5:50 | call to rand | Use of banned function rand. | diff --git a/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql b/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql new file mode 100644 index 0000000000..3ad5626256 --- /dev/null +++ b/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.donotuserandforgeneratingpseudorandomnumbers.DoNotUseRandForGeneratingPseudorandomNumbers + +class TestFileQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery, TestQuery { } diff --git a/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.c b/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.c new file mode 100644 index 0000000000..a9ce133b6b --- /dev/null +++ b/c/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.c @@ -0,0 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +void test_use_of_rand() { int random_number = rand() % 10; } \ No newline at end of file diff --git a/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected b/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected index 5431867345..bda6c7ad05 100644 --- a/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected +++ b/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected @@ -10,21 +10,27 @@ problems | test.c:25:7:25:14 | ... >= ... | test.c:7:14:7:15 | l1 | test.c:25:7:25:8 | p1 | Compare operation >= comparing left operand pointing to array $@ and other operand pointing to array $@. | test.c:2:7:2:8 | l1 | l1 | test.c:4:7:4:8 | l3 | l3 | | test.c:25:7:25:14 | ... >= ... | test.c:25:13:25:14 | l3 | test.c:25:13:25:14 | l3 | Compare operation >= comparing right operand pointing to array $@ and other operand pointing to array $@. | test.c:4:7:4:8 | l3 | l3 | test.c:2:7:2:8 | l1 | l1 | edges -| test.c:6:13:6:14 | l1 | test.c:13:12:13:13 | p0 | -| test.c:7:14:7:15 | l1 | test.c:11:7:11:8 | p1 | -| test.c:7:14:7:15 | l1 | test.c:13:7:13:8 | p1 | -| test.c:7:14:7:15 | l1 | test.c:15:13:15:14 | p1 | -| test.c:7:14:7:15 | l1 | test.c:17:7:17:8 | p1 | -| test.c:7:14:7:15 | l1 | test.c:23:13:23:14 | p1 | -| test.c:7:14:7:15 | l1 | test.c:25:7:25:8 | p1 | -| test.c:8:14:8:15 | l1 | test.c:11:12:11:13 | p2 | -| test.c:8:14:8:15 | l1 | test.c:21:7:21:8 | p2 | -| test.c:9:14:9:15 | l2 | test.c:21:12:21:13 | p3 | +| test.c:6:13:6:14 | l1 | test.c:13:12:13:13 | p0 | provenance | | +| test.c:7:14:7:15 | l1 | test.c:7:14:7:18 | access to array | provenance | Config | +| test.c:7:14:7:18 | access to array | test.c:11:7:11:8 | p1 | provenance | | +| test.c:7:14:7:18 | access to array | test.c:13:7:13:8 | p1 | provenance | | +| test.c:7:14:7:18 | access to array | test.c:15:13:15:14 | p1 | provenance | | +| test.c:7:14:7:18 | access to array | test.c:17:7:17:8 | p1 | provenance | | +| test.c:7:14:7:18 | access to array | test.c:23:13:23:14 | p1 | provenance | | +| test.c:7:14:7:18 | access to array | test.c:25:7:25:8 | p1 | provenance | | +| test.c:8:14:8:15 | l1 | test.c:8:14:8:18 | access to array | provenance | Config | +| test.c:8:14:8:18 | access to array | test.c:11:12:11:13 | p2 | provenance | | +| test.c:8:14:8:18 | access to array | test.c:21:7:21:8 | p2 | provenance | | +| test.c:9:14:9:15 | l2 | test.c:9:14:9:18 | access to array | provenance | Config | +| test.c:9:14:9:18 | access to array | test.c:21:12:21:13 | p3 | provenance | | nodes | test.c:6:13:6:14 | l1 | semmle.label | l1 | | test.c:7:14:7:15 | l1 | semmle.label | l1 | +| test.c:7:14:7:18 | access to array | semmle.label | access to array | | test.c:8:14:8:15 | l1 | semmle.label | l1 | +| test.c:8:14:8:18 | access to array | semmle.label | access to array | | test.c:9:14:9:15 | l2 | semmle.label | l2 | +| test.c:9:14:9:18 | access to array | semmle.label | access to array | | test.c:11:7:11:8 | p1 | semmle.label | p1 | | test.c:11:12:11:13 | p2 | semmle.label | p2 | | test.c:13:7:13:8 | p1 | semmle.label | p1 | diff --git a/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql b/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql index c6cca37aa2..bceb46bf63 100644 --- a/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql +++ b/c/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotuserelationaloperatorswithdifferingarrays.DoNotUseRelationalOperatorsWithDifferingArrays + +class TestFileQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery, TestQuery { } diff --git a/c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.expected b/c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.expected new file mode 100644 index 0000000000..0844b14417 --- /dev/null +++ b/c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.expected @@ -0,0 +1,4 @@ +| test.c:5:13:5:18 | call to malloc | The memory allocated here may not be freed at $@. | test.c:4:15:11:1 | { ... } | this location | +| test.c:14:13:14:19 | call to realloc | The memory allocated here may not be freed at $@. | test.c:13:25:20:1 | { ... } | this location | +| test.c:23:13:23:19 | call to realloc | The memory allocated here may not be freed at $@. | test.c:22:26:29:1 | { ... } | this location | +| test.c:42:13:42:18 | call to malloc | The memory allocated here may not be freed at $@. | test.c:51:3:51:11 | return ... | this location | diff --git a/c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.ql b/c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.ql new file mode 100644 index 0000000000..27683eddfb --- /dev/null +++ b/c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.freememorywhennolongerneededshared.FreeMemoryWhenNoLongerNeededShared + +class TestFileQuery extends FreeMemoryWhenNoLongerNeededSharedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/freememorywhennolongerneededshared/test.c b/c/common/test/rules/freememorywhennolongerneededshared/test.c new file mode 100644 index 0000000000..85a86594ba --- /dev/null +++ b/c/common/test/rules/freememorywhennolongerneededshared/test.c @@ -0,0 +1,82 @@ +#include +#include + +int f1a(void) { + void *p = malloc(10); // NON_COMPLIANT + if (NULL == p) { + return -1; + } + /* ... */ + return 0; +} + +int f1b(const void *in) { + void *p = realloc(in, 10); // NON_COMPLIANT + if (NULL == p) { + return -1; + } + /* ... */ + return 0; +} + +void f1c(const void *in) { + void *p = realloc(in, 10); // NON_COMPLIANT + if (NULL == p) { + return; + } + /* ... */ + // pointer out of scope not freed +} + +int f2a(void) { + void *p = malloc(10); // COMPLIANT + if (NULL == p) { + return -1; + } + /* ... */ + free(p); + return 0; +} + +int f2b(int test) { + void *p = malloc(10); // NON_COMPLIANT + if (NULL == p) { + return -1; + } + if (test == 1) { + free(p); + return -1; + } + // memory not freed on this path + return 0; +} + +// scope prolonged +int f2c(void) { + void *q; + { + void *p = malloc(10); // COMPLIANT + if (NULL == p) { + return -1; + } + /* ... */ + q = p; + // p out of scope + } + free(q); + return 0; +} + +// interprocedural +int free_helper(void *g) { + free(g); + return 0; +} + +int f2inter(void) { + void *p = malloc(10); // COMPLIANT + if (NULL == p) { + return -1; + } + return free_helper(p); +} \ No newline at end of file diff --git a/c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.expected b/c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.expected new file mode 100644 index 0000000000..dc72201a8a --- /dev/null +++ b/c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.expected @@ -0,0 +1 @@ +| test.c:16:3:16:8 | call to remove | Return value from remove is not tested for errors. | diff --git a/c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql b/c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql new file mode 100644 index 0000000000..12c2196efd --- /dev/null +++ b/c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functionerroneousreturnvaluenottested.FunctionErroneousReturnValueNotTested + +class TestFileQuery extends FunctionErroneousReturnValueNotTestedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M0-3-2/test.cpp b/c/common/test/rules/functionerroneousreturnvaluenottested/test.c similarity index 100% rename from cpp/autosar/test/rules/M0-3-2/test.cpp rename to c/common/test/rules/functionerroneousreturnvaluenottested/test.c diff --git a/c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.expected b/c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.expected new file mode 100644 index 0000000000..c9a9eb0d48 --- /dev/null +++ b/c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.expected @@ -0,0 +1,2 @@ +| test.c:8:1:8:25 | #define MACRO4(x) (x + 1) | Macro used instead of a function. | +| test.c:13:1:13:48 | #define MACRO9() printf_custom("output = %d", 7) | Macro used instead of a function. | diff --git a/c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql b/c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql new file mode 100644 index 0000000000..29088c4458 --- /dev/null +++ b/c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functionlikemacrosdefined.FunctionLikeMacrosDefined + +class TestFileQuery extends FunctionLikeMacrosDefinedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/functionlikemacrosdefined/test.c b/c/common/test/rules/functionlikemacrosdefined/test.c new file mode 100644 index 0000000000..ee36549b8d --- /dev/null +++ b/c/common/test/rules/functionlikemacrosdefined/test.c @@ -0,0 +1,42 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +#define MACRO(OP, L, R) ((L)OP(R)) // COMPLIANT +#define MACRO2(L, R) (L + R) // COMPLIANT +#define MACRO3(L, R) (L " " R " " L) // COMPLIANT +#define MACRO4(x) (x + 1) // NON_COMPLIANT +#define MACRO5(L, LR) (LR + 1) // COMPLIANT +#define MACRO6(x) printf_custom("output = %d", test##x) // COMPLIANT +#define MACRO7(x) #x // COMPLIANT +#define MACRO8(x) "NOP" // COMPLIANT +#define MACRO9() printf_custom("output = %d", 7) // NON_COMPLIANT +#define MACRO10(x) // COMPLIANT +#define MY_ASSERT(X) assert(X) // NON_COMPLIANT[FALSE_NEGATIVE] + +char a1[MACRO2(1, 1) + 6]; +extern int printf_custom(char *, int); +int test1; + +void f() { + int i = MACRO(+, 1, 1); + int i2 = MACRO2(7, 10); + + static int i3 = MACRO2(1, 1); + + char *i4 = MACRO3("prefix", "suffix"); + + int i5 = MACRO4(1); + + int i6 = MACRO4(MACRO2(1, 1)); + + int i7 = MACRO5(1, 1); + + MACRO6(1); + + char *i10 = MACRO7("prefix"); + + asm(MACRO8(1)); + + MY_ASSERT(1); +} \ No newline at end of file diff --git a/c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.expected b/c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.expected new file mode 100644 index 0000000000..5aede0a5ba --- /dev/null +++ b/c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.expected @@ -0,0 +1,3 @@ +| test.c:9:16:9:31 | test_noreturn_f2 | The function test_noreturn_f2 declared with attribute _Noreturn returns a value. | +| test.c:34:16:34:31 | test_noreturn_f5 | The function test_noreturn_f5 declared with attribute _Noreturn returns a value. | +| test.c:49:32:49:47 | test_noreturn_f7 | The function test_noreturn_f7 declared with attribute _Noreturn returns a value. | diff --git a/c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql b/c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql new file mode 100644 index 0000000000..4af4aeceaf --- /dev/null +++ b/c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functionnoreturnattributecondition.FunctionNoReturnAttributeCondition + +class TestFileQuery extends FunctionNoReturnAttributeConditionSharedQuery, TestQuery { } diff --git a/c/common/test/rules/functionnoreturnattributecondition/test.c b/c/common/test/rules/functionnoreturnattributecondition/test.c new file mode 100644 index 0000000000..c13654a8e0 --- /dev/null +++ b/c/common/test/rules/functionnoreturnattributecondition/test.c @@ -0,0 +1,88 @@ +#include "setjmp.h" +#include "stdlib.h" +#include "threads.h" + +_Noreturn void test_noreturn_f1(int i) { // COMPLIANT + abort(); +} + +_Noreturn void test_noreturn_f2(int i) { // NON_COMPLIANT + if (i > 0) { + abort(); + } + if (i < 0) { + abort(); + } +} + +_Noreturn void test_noreturn_f3(int i) { // COMPLIANT + if (i > 0) { + abort(); + } + exit(1); +} + +void test_noreturn_f4(int i) { // COMPLIANT + if (i > 0) { + abort(); + } + if (i < 0) { + abort(); + } +} + +_Noreturn void test_noreturn_f5(int i) { // NON_COMPLIANT + if (i > 0) { + abort(); + } +} + +_Noreturn void test_noreturn_f6(int i) { // COMPLIANT + if (i > 0) { + abort(); + } + while (1) { + i = 5; + } +} + +__attribute__((noreturn)) void test_noreturn_f7(int i) { // NON_COMPLIANT + if (i > 0) { + abort(); + } +} + +__attribute__((noreturn)) void test_noreturn_f8(int i) { // COMPLIANT + abort(); +} + +_Noreturn void test_noreturn_f9(int i) { // COMPLIANT + test_noreturn_f1(i); +} + +_Noreturn void test_noreturn_f10(int i) { // COMPLIANT + switch (i) { + case 0: + abort(); + break; + case 1: + exit(0); + break; + case 2: + _Exit(0); + break; + case 3: + quick_exit(0); + break; + case 4: + thrd_exit(0); + break; + default:; + jmp_buf jb; + longjmp(jb, 0); + } +} + +_Noreturn void test_noreturn_f11(int i) { // COMPLIANT + return test_noreturn_f11(i); +} \ No newline at end of file diff --git a/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.expected b/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.expected new file mode 100644 index 0000000000..f2c08897b8 --- /dev/null +++ b/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.expected @@ -0,0 +1,4 @@ +| test.c:3:6:3:7 | f1 | Function f1 declares parameter that is unnamed. | +| test.c:4:6:4:7 | f2 | Function f2 does not specify void for no parameters present. | +| test.c:5:6:5:7 | f3 | Function f3 does not specify void for no parameters present. | +| test.c:7:5:7:6 | f5 | Function f5 declares parameter in unsupported declaration list. | diff --git a/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.expected.clang b/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.expected.clang new file mode 100644 index 0000000000..1d4a30ce8c --- /dev/null +++ b/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.expected.clang @@ -0,0 +1,3 @@ +| test.c:3:6:3:7 | f1 | Function f1 declares parameter that is unnamed. | +| test.c:4:6:4:7 | f2 | Function f2 does not specify void for no parameters present. | +| test.c:7:5:7:6 | f5 | Function f5 declares parameter in unsupported declaration list. | diff --git a/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.ql b/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.ql new file mode 100644 index 0000000000..25d273354d --- /dev/null +++ b/c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functiontypesnotinprototypeformshared.FunctionTypesNotInPrototypeFormShared + +class TestFileQuery extends FunctionTypesNotInPrototypeFormSharedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/functiontypesnotinprototypeformshared/test.c b/c/common/test/rules/functiontypesnotinprototypeformshared/test.c new file mode 100644 index 0000000000..c254a221d9 --- /dev/null +++ b/c/common/test/rules/functiontypesnotinprototypeformshared/test.c @@ -0,0 +1,9 @@ +void f(int x); // COMPLIANT +void f0(void); // COMPLIANT +void f1(int); // NON_COMPLIANT +void f2(); // NON_COMPLIANT +void f3(x); // NON_COMPLIANT +void f4(const x); // NON_COMPLIANT[FALSE_NEGATIVE] +int f5(x) // NON_COMPLIANT +int x; +{ return 1; } \ No newline at end of file diff --git a/c/common/test/rules/functiontypesnotinprototypeformshared/test.c.clang b/c/common/test/rules/functiontypesnotinprototypeformshared/test.c.clang new file mode 100644 index 0000000000..f4cfb7da14 --- /dev/null +++ b/c/common/test/rules/functiontypesnotinprototypeformshared/test.c.clang @@ -0,0 +1,9 @@ +void f(int x); // COMPLIANT +void f0(void); // COMPLIANT +void f1(int); // NON_COMPLIANT +void f2(); // NON_COMPLIANT +// void f3(x); // NON_COMPILABLE +void f4(const x); // NON_COMPLIANT[FALSE_NEGATIVE] +int f5(x) // NON_COMPLIANT + int x; +{ return 1; } \ No newline at end of file diff --git a/c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.expected b/c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.expected new file mode 100644 index 0000000000..7fd94b47f3 --- /dev/null +++ b/c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.expected @@ -0,0 +1,3 @@ +| test.c:4:3:4:10 | goto ... | The goto statement and its $@ are not declared or enclosed in the same block. | test.c:6:3:6:5 | label ...: | label | +| test.c:42:3:42:10 | goto ... | The goto statement and its $@ are not declared or enclosed in the same block. | test.c:46:3:46:5 | label ...: | label | +| test.c:57:5:57:12 | goto ... | The goto statement and its $@ are not declared or enclosed in the same block. | test.c:60:3:60:5 | label ...: | label | diff --git a/c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql b/c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql new file mode 100644 index 0000000000..f553135683 --- /dev/null +++ b/c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotoreferencealabelinsurroundingblock.GotoReferenceALabelInSurroundingBlock + +class TestFileQuery extends GotoReferenceALabelInSurroundingBlockSharedQuery, TestQuery { } diff --git a/c/common/test/rules/gotoreferencealabelinsurroundingblock/test.c b/c/common/test/rules/gotoreferencealabelinsurroundingblock/test.c new file mode 100644 index 0000000000..083e0fe57b --- /dev/null +++ b/c/common/test/rules/gotoreferencealabelinsurroundingblock/test.c @@ -0,0 +1,87 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void f1() { + goto L1; + for (int i = 0; i < 100; i++) { + L1: // NON_COMPLIANT + break; + } +} + +void f2() { + int i = 0; + if (i >= 0) { + for (int j = 0; j < 10; j++) { + goto L2; + } + } +L2: // COMPLIANT + return; +} + +void f3() { + int i = 0; + if (i >= 0) { + for (int j = 0; j < 10; j++) { + goto L3; + L3: // COMPLIANT + break; + } + } +} + +void f4() { + int i = 0; +L4: // COMPLIANT + if (i >= 0) { + goto L4; + } +} + +void f5(int p) { + goto L1; + + switch (p) { + case 0: + L1:; // NON_COMPLIANT + break; + default: + break; + } +} + +void f6(int p) { + + switch (p) { + case 0: + goto L1; + break; + default: + L1: // NON_COMPLIANT + break; + } +} + +void f7(int p) { +L1: // COMPLIANT + switch (p) { + case 0: + goto L1; + break; + default: + break; + } +} + +void f8(int p) { + + switch (p) { + case 0: + goto L1; + ; + L1:; // COMPLIANT + break; + default: + break; + } +} diff --git a/c/common/test/rules/gotostatementcondition/GotoStatementCondition.expected b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.expected new file mode 100644 index 0000000000..c42642c343 --- /dev/null +++ b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.expected @@ -0,0 +1,4 @@ +| test.c:9:3:9:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:9:3:9:10 | goto ... | l1 | test.c:5:1:5:3 | label ...: | label ...: | +| test.c:21:3:21:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:21:3:21:10 | goto ... | l2 | test.c:17:1:17:3 | label ...: | label ...: | +| test.c:23:3:23:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:23:3:23:10 | goto ... | l1 | test.c:16:1:16:3 | label ...: | label ...: | +| test.c:28:3:28:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:28:3:28:10 | goto ... | l1 | test.c:27:1:27:3 | label ...: | label ...: | diff --git a/c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql new file mode 100644 index 0000000000..89768a3022 --- /dev/null +++ b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition + +class TestFileQuery extends GotoStatementConditionSharedQuery, TestQuery { } diff --git a/c/common/test/rules/gotostatementcondition/test.c b/c/common/test/rules/gotostatementcondition/test.c new file mode 100644 index 0000000000..48426261fe --- /dev/null +++ b/c/common/test/rules/gotostatementcondition/test.c @@ -0,0 +1,29 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void f1(int p1) { + +l1: + if (p1) { + goto l2; // COMPLIANT + } + goto l1; // NON_COMPLIANT + +l2:; +} + +void f2(int p1) { + +l1:; +l2: + if (p1) { + goto l3; // COMPLIANT + } + goto l2; // NON_COMPLIANT +l3: + goto l1; // NON_COMPLIANT +} + +void f3() { +l1: + goto l1; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.expected b/c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.expected new file mode 100644 index 0000000000..15dc49ee37 --- /dev/null +++ b/c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.expected @@ -0,0 +1 @@ +| test.c:6:3:6:14 | goto ... | Use of goto. | diff --git a/c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql b/c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql new file mode 100644 index 0000000000..1a117d5ddd --- /dev/null +++ b/c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotostatementshouldnotbeused.GotoStatementShouldNotBeUsed + +class TestFileQuery extends GotoStatementShouldNotBeUsedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/gotostatementshouldnotbeused/test.c b/c/common/test/rules/gotostatementshouldnotbeused/test.c new file mode 100644 index 0000000000..4ecc1789c7 --- /dev/null +++ b/c/common/test/rules/gotostatementshouldnotbeused/test.c @@ -0,0 +1,11 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void test_goto() { + int x = 1; + + goto label1; // NON_COMPLIANT + +label1: + + x = 2; +} \ No newline at end of file diff --git a/c/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql b/c/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql index 693dae8f57..a0d83a59a6 100644 --- a/c/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql +++ b/c/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.guardaccesstobitfields.GuardAccessToBitFields + +class TestFileQuery extends GuardAccessToBitFieldsSharedQuery, TestQuery { } diff --git a/c/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected b/c/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected new file mode 100644 index 0000000000..9bb8ff5249 --- /dev/null +++ b/c/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected @@ -0,0 +1,4 @@ +| test.c:11:1:11:22 | #define MACROFIVE(X) #X | Macro definition uses the # or ## operator. | +| test.c:13:1:13:26 | #define MACROSIX(X,Y) X ## Y | Macro definition uses the # or ## operator. | +| test.c:15:1:15:29 | #define MACROSEVEN "##'" #"#" | Macro definition uses the # or ## operator. | +| test.c:17:1:17:28 | #define MACROEIGHT '##' #"#" | Macro definition uses the # or ## operator. | diff --git a/c/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql b/c/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql new file mode 100644 index 0000000000..a61dc7860a --- /dev/null +++ b/c/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.hashoperatorsused.HashOperatorsUsed + +class TestFileQuery extends HashOperatorsUsedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/hashoperatorsused/test.c b/c/common/test/rules/hashoperatorsused/test.c new file mode 100644 index 0000000000..f5dee3d536 --- /dev/null +++ b/c/common/test/rules/hashoperatorsused/test.c @@ -0,0 +1,21 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#define MACROONE 1 // COMPLIANT + +#define MACROTWO '#' // COMPLIANT + +#define MACROTHREE "##" // COMPLIANT + +#define MACROFOUR "##" + "#" // COMPLIANT + +#define MACROFIVE(X) #X // NON_COMPLIANT + +#define MACROSIX(X, Y) X##Y // NON_COMPLIANT + +#define MACROSEVEN "##'" #"#" // NON_COMPLIANT + +#define MACROEIGHT '##' #"#" // NON_COMPLIANT + +#define MACRONINE "##\"\"" + "#" // COMPLIANT + +#define MACROTEN "##\"\"'" + "#" // COMPLIANT \ No newline at end of file diff --git a/c/common/test/rules/identifierhidden/IdentifierHidden.ql b/c/common/test/rules/identifierhidden/IdentifierHidden.ql index 62abdd2163..ba13b28bd4 100644 --- a/c/common/test/rules/identifierhidden/IdentifierHidden.ql +++ b/c/common/test/rules/identifierhidden/IdentifierHidden.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.identifierhidden.IdentifierHidden + +class TestFileQuery extends IdentifierHiddenSharedQuery, TestQuery { } diff --git a/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.expected b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.expected new file mode 100644 index 0000000000..8ea6eca50e --- /dev/null +++ b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.expected @@ -0,0 +1,2 @@ +| test.c:1:5:1:6 | definition of g1 | The identifier g1 has external linkage and is redefined $@. | test1.c:1:5:1:6 | definition of g1 | here | +| test.c:6:6:6:7 | definition of f2 | The identifier f2 has external linkage and is redefined $@. | test1.c:6:6:6:7 | definition of f2 | here | diff --git a/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql new file mode 100644 index 0000000000..d50eb53652 --- /dev/null +++ b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.identifierwithexternallinkageonedefinitionshared.IdentifierWithExternalLinkageOneDefinitionShared + +class TestFileQuery extends IdentifierWithExternalLinkageOneDefinitionSharedSharedQuery, TestQuery { +} diff --git a/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/test.c b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/test.c new file mode 100644 index 0000000000..a2e9eeff8d --- /dev/null +++ b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/test.c @@ -0,0 +1,6 @@ +int g1 = 1; // NON_COMPLIANT +static int g2 = 1; // COMPLIANT; internal linkage + +inline void f1() {} // COMPLIANT; inline functions are an exception + +void f2() {} // NON_COMPLIANT \ No newline at end of file diff --git a/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/test1.c b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/test1.c new file mode 100644 index 0000000000..6882a55800 --- /dev/null +++ b/c/common/test/rules/identifierwithexternallinkageonedefinitionshared/test1.c @@ -0,0 +1,6 @@ +int g1 = 0; // NON_COMPLIANT +static int g2 = 1; // COMPLIANT; internal linkage + +inline void f1() {} // COMPLIANT; inline functions are an exception + +void f2() {} // NON_COMPLIANT \ No newline at end of file diff --git a/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected new file mode 100644 index 0000000000..910ea55bab --- /dev/null +++ b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected @@ -0,0 +1,3 @@ +| test.c:16:3:20:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:16:3:20:3 | if (...) ... | `if...else` | +| test.c:33:5:37:5 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:33:5:37:5 | if (...) ... | `if...else` | +| test.c:45:3:55:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:45:3:55:3 | if (...) ... | `if...else` | diff --git a/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql new file mode 100644 index 0000000000..acdd497be7 --- /dev/null +++ b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct + +class TestFileQuery extends IfElseTerminationConstructSharedQuery, TestQuery { } diff --git a/c/common/test/rules/ifelseterminationconstruct/test.c b/c/common/test/rules/ifelseterminationconstruct/test.c new file mode 100644 index 0000000000..c3fb95df15 --- /dev/null +++ b/c/common/test/rules/ifelseterminationconstruct/test.c @@ -0,0 +1,56 @@ +void f1(int p1) { + + if (p1) { // COMPLIANT + ; + } else if (p1) { + ; + } else { + ; + } +} + +void f2(int p1) { + if (p1) { // COMPLIANT + ; + } + if (p1) { // NON_COMPLIANT + ; + } else if (p1) { + ; + } +} + +void f3(int p1) { + + if (p1) { // COMPLIANT + ; + } else { + ; + } + if (p1) { // COMPLIANT + ; + } else if (p1) { + if (p1) { // NON_COMPLIANT + ; + } else if (p1) { + ; + } + } else { + ; + } +} + +void f4(int p1) { + + if (p1) { // NON_COMPLIANT + ; + } else if (p1) { + if (p1) { // COMPLIANT + ; + } else if (p1) { + ; + } else { + ; + } + } +} \ No newline at end of file diff --git a/c/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql b/c/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql index 2fcfddeca9..13b07b4e90 100644 --- a/c/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql +++ b/c/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.includeguardsnotused.IncludeGuardsNotUsed + +class TestFileQuery extends IncludeGuardsNotUsedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected b/c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected new file mode 100644 index 0000000000..e8cda5becb --- /dev/null +++ b/c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected @@ -0,0 +1,18 @@ +| arrays.c:11:20:11:21 | wa | 'wa' may leak information from {elements of a[...] (arrays.c:7)}. Path: wa (arrays.c:11) --> & ... (arrays.c:12) | +| arrays.c:33:22:33:23 | wa | 'wa' may leak information from {elements of elements of a[...][...] (arrays.c:29)}. Path: wa (arrays.c:33) --> & ... (arrays.c:34) | +| arrays.c:57:22:57:23 | wa | 'wa' may leak information from {WithPointer (arrays.c:52)}. Path: wa (arrays.c:57) --> & ... (arrays.c:59) | +| interprocedural.c:37:9:37:9 | p | 'p' may leak information from {y (interprocedural.c:8)}. Path: p (interprocedural.c:37) --> past assign_x (interprocedural.c:32) --> & ... (interprocedural.c:39) | +| interprocedural.c:104:9:104:9 | p | 'p' may leak information from {x (interprocedural.c:7), y (interprocedural.c:8)}. Path: p (interprocedural.c:104) --> overwrite_after_leak(...) (interprocedural.c:96) --> p (interprocedural.c:97) | +| multilayer.c:16:10:16:10 | s | 's' may leak information from {b (multilayer.c:12)}. Path: s (multilayer.c:16) --> & ... (multilayer.c:18) | +| multilayer.c:29:10:29:10 | s | 's' may leak information from {b (multilayer.c:12), x (multilayer.c:7)}. Path: s (multilayer.c:29) --> & ... (multilayer.c:30) | +| multilayer.c:34:8:34:8 | s | 's' may leak information from {(unnamed class/struct/union) (multilayer.c:6)}. Path: s (multilayer.c:34) --> & ... (multilayer.c:35) | +| test.c:12:12:12:12 | s | 's' may leak information from {y (test.c:8)}. Path: s (test.c:12) --> & ... (test.c:14) | +| test.c:18:12:18:12 | s | 's' may leak information from {x (test.c:7)}. Path: s (test.c:18) --> & ... (test.c:20) | +| test.c:24:12:24:12 | s | 's' may leak information from {x (test.c:7), y (test.c:8)}. Path: s (test.c:24) --> & ... (test.c:25) | +| test.c:36:12:36:12 | s | 's' may leak information from {y (test.c:8)}. Path: s (test.c:36) --> & ... (test.c:38) | +| test.c:43:12:43:12 | s | 's' may leak information from {x (test.c:7)}. Path: s (test.c:43) --> & ... (test.c:47) | +| test.c:58:12:58:12 | s | 's' may leak information from {x (test.c:7), y (test.c:8)}. Path: s (test.c:58) --> & ... (test.c:59) | +| test.c:64:12:64:12 | s | 's' may leak information from {y (test.c:8)}. Path: s (test.c:64) --> & ... (test.c:66) | +| test.c:112:16:112:16 | s | 's' may leak information from {buf (test.c:92)}. Path: s (test.c:112) --> & ... (test.c:115) | +| test.c:128:12:128:12 | s | 's' may leak information from {x (test.c:7), y (test.c:8)}. Path: s (test.c:128) --> & ... (test.c:132) | +| test.c:157:22:157:22 | s | 's' may leak information from {2 to 2 bytes of padding in has_padding (test.c:151)}. Path: s (test.c:157) --> & ... (test.c:160) | diff --git a/c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql b/c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql new file mode 100644 index 0000000000..3393d015c3 --- /dev/null +++ b/c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.informationleakageacrossboundaries.InformationLeakageAcrossBoundaries + +class TestFileQuery extends InformationLeakageAcrossBoundariesSharedQuery, TestQuery { } diff --git a/c/common/test/rules/informationleakageacrossboundaries/arrays.c b/c/common/test/rules/informationleakageacrossboundaries/arrays.c new file mode 100644 index 0000000000..875af58934 --- /dev/null +++ b/c/common/test/rules/informationleakageacrossboundaries/arrays.c @@ -0,0 +1,62 @@ +#include +#include + +unsigned long copy_to_user(void *to, const void *from, unsigned long n); + +struct WithArray { + int a[2]; +}; + +void forget_array() { + struct WithArray wa; + copy_to_user(0, &wa, sizeof wa); // NON_COMPLIANT +} + +void write_partial_array() { + struct WithArray wa; + wa.a[0] = 1; + copy_to_user(0, &wa, sizeof wa); // NON_COMPLIANT[FALSE NEGATIVE] +} + +void write_full_array() { + struct WithArray wa; + wa.a[0] = 1; + wa.a[1] = 1; + copy_to_user(0, &wa, sizeof wa); // COMPLIANT +} + +struct WithArray2D { + int a[2][1]; +}; + +void forget_array2d() { + struct WithArray2D wa; + copy_to_user(0, &wa, sizeof wa); // NON_COMPLIANT +} + +void write_partial_array2d() { + struct WithArray2D wa; + wa.a[0][0] = 1; + copy_to_user(0, &wa, sizeof wa); // NON_COMPLIANT[FALSE NEGATIVE] +} + +void write_full_array2d() { + struct WithArray2D wa; + wa.a[0][0] = 1; + wa.a[1][0] = 1; + copy_to_user(0, &wa, sizeof wa); // COMPLIANT +} + +// A pointer field allows mostly the same syntactic operations as an array +// field, but the semantics are completely different. +struct WithPointer { + int *a; +}; + +void pointer_array_expression() { + struct WithPointer wa; + wa.a[0] = 1; + copy_to_user(0, &wa, sizeof wa); // NON_COMPLIANT +} + +// TODO: test a struct in an array \ No newline at end of file diff --git a/c/common/test/rules/informationleakageacrossboundaries/interprocedural.c b/c/common/test/rules/informationleakageacrossboundaries/interprocedural.c new file mode 100644 index 0000000000..e03d5fcc6e --- /dev/null +++ b/c/common/test/rules/informationleakageacrossboundaries/interprocedural.c @@ -0,0 +1,107 @@ +#include +#include + +unsigned long copy_to_user(void *to, const void *from, unsigned long n); + +typedef struct _point { + int x; + int y; +} point; + +void callee1(point *p) { + p->y = 1; + copy_to_user(0, p, sizeof(point)); // COMPLIANT +} + +void caller1() { + point p; + p.x = 1; + callee1(&p); +} + +void callee2(point *p) { + memset(p, 0, sizeof(point)); + copy_to_user(0, p, sizeof(point)); // COMPLIANT +} + +void caller2() { + point p; + callee2(&p); +} + +void assign_x(point *p, int value) { p->x = value; } + +void zero_y(point *p) { memset(&p->y, 0, sizeof(p->y)); } + +void call_to_overwrite_x() { + point p; + assign_x(&p, 1); + copy_to_user(0, &p, sizeof p); // NON_COMPLIANT +} + +void call_to_overwrite_both() { + point p; + assign_x(&p, 1); + zero_y(&p); + copy_to_user(0, &p, sizeof p); // COMPLIANT +} + +void zero_y_and_loop(point *p) { + int i; + memset(&p->y, 0, sizeof(p->y)); + for (i = 0; i < 10; i++) { + p->y++; + } +} + +void call_zero_y_and_loop() { + point p; + zero_y_and_loop(&p); + assign_x(&p, 1); + copy_to_user(0, &p, sizeof p); // COMPLIANT +} + +int zero_y_or_fail(point *p) { + if (p->x < 0) { + return 0; + } + p->y = 0; + return 1; +} + +void call_zero_y_or_fail(int i) { + point p; + p.x = i; + if (!zero_y_or_fail(&p)) { + return; + } + copy_to_user(0, &p, sizeof p); // COMPLIANT +} + +int zero_y_proxy(point *p) { + if (p->x) { + zero_y(p); + } else { + zero_y(p); + } +} + +void call_zero_y_proxy() { + point p; + zero_y_proxy(&p); + assign_x(&p, 1); + copy_to_user(0, &p, sizeof p); // COMPLIANT +} + +void overwrite_after_leak(point *p) { + copy_to_user(0, p, sizeof(*p)); // NON_COMPLIANT + + p->x = 0; + p->y = 0; +} + +void call_overwrite_after_leak(void) { + point p; + overwrite_after_leak(&p); + copy_to_user(0, &p, sizeof p); // COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/informationleakageacrossboundaries/multilayer.c b/c/common/test/rules/informationleakageacrossboundaries/multilayer.c new file mode 100644 index 0000000000..7fad75429f --- /dev/null +++ b/c/common/test/rules/informationleakageacrossboundaries/multilayer.c @@ -0,0 +1,36 @@ +#include +#include + +unsigned long copy_to_user(void *to, const void *from, unsigned long n); + +typedef struct { + int x; +} intx; + +typedef struct { + intx a; + intx b; +} intxab; + +void forget_y() { + intxab s; + s.a.x = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) +} + +void set_both() { + intxab s; + s.a.x = 1; + memset(&s.b, 0, sizeof s.b); + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void set_none() { + intxab s; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (both) +} + +void set_none_intx() { + intx s; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) +} \ No newline at end of file diff --git a/c/common/test/rules/informationleakageacrossboundaries/test.c b/c/common/test/rules/informationleakageacrossboundaries/test.c new file mode 100644 index 0000000000..f17ca8fb87 --- /dev/null +++ b/c/common/test/rules/informationleakageacrossboundaries/test.c @@ -0,0 +1,167 @@ +#include +#include + +unsigned long copy_to_user(void *to, const void *from, unsigned long n); + +typedef struct { + int x; + int y; +} MyStruct; + +void forget_y() { + MyStruct s; + s.x = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) +} + +void forget_x() { + MyStruct s; + s.y = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) +} + +void forget_both() { + MyStruct s; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x, y) +} + +void init_both() { + MyStruct s; + s.x = 1; + s.y = 1; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void init_after() { + MyStruct s; + s.x = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT + s.y = 1; +} + +void init_other() { + MyStruct s, t; + s.y = 1; + t.x = 1; + t.y = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) + copy_to_user(0, &t, sizeof t); // COMPLIANT +} + +void zero_memory() { + MyStruct s; + memset(&s, 0, sizeof s); + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void zero_memory_after() { + MyStruct s; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT + memset(&s, 0, sizeof s); +} + +void zero_field() { + MyStruct s; + memset(&s.x, 0, sizeof s.x); + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) +} + +void overwrite_with_zeroed() { + MyStruct s, t; + memset(&t, 0, sizeof t); + s = t; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void overwrite_struct_with_uninit() { + MyStruct s, t; + s = t; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT[FALSE NEGATIVE] +} + +void overwrite_field_with_uninit() { + MyStruct s; + int x; + s.x = x; + s.y = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT[FALSE NEGATIVE] +} + +typedef struct { + size_t length; + char buf[128]; +} PascalString; + +void zero_array() { + PascalString s; + memset(s.buf, 0, sizeof s.buf); + s.length = 0; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void zero_array_by_ref() { + PascalString s; + memset(&s.buf, 0, sizeof s.buf); + s.length = 0; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +char *strcpy(char *dst, const char *src); + +void use_strcpy() { + PascalString s; + strcpy(s.buf, "Hello, World"); // does not zero rest of s.buf + s.length = strlen(s.buf); + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (buf) +} + +void *malloc(size_t size); + +void heap_memory() { + MyStruct *s; + s = (MyStruct *)malloc(sizeof(*s)); + s->x = 1; + copy_to_user(0, s, sizeof(*s)); // NON_COMPLIANT[FALSE NEGATIVE] +} + +void conditional_memset(int b) { + MyStruct s; + if (b) { + memset(&s, 0, sizeof s); + } + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT +} + +void memset_field() { + MyStruct s; + memset(&s.x, 0, sizeof(s.x)); + s.y = 1; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +const static int one = 1; +void zero_if_true() { + MyStruct s; + if (one) { + memset(&s, 0, sizeof s); + } + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +struct has_padding { + short s; + int i; +}; + +void forget_padding() { + struct has_padding s; + s.s = 1; + s.i = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT +} + +void remember_padding() { + struct has_padding s; + memset(&s, 0, sizeof s); + copy_to_user(0, &s, sizeof s); // COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected b/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected index c52544450f..26a84ecd8a 100644 --- a/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected +++ b/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected @@ -1,6 +1,6 @@ -| test.c:19:14:19:19 | tmpvar | This pointer was returned by a $@ and may have been overwritten by the susequent $@. | test.c:11:12:11:17 | call to getenv | call to getenv | test.c:15:13:15:18 | call to getenv | call to getenv | -| test.c:132:14:132:17 | temp | This pointer was returned by a $@ and may have been overwritten by the susequent $@. | test.c:128:12:128:17 | call to getenv | call to getenv | test.c:129:11:129:16 | call to getenv | call to getenv | -| test.c:132:20:132:22 | tmp | This pointer was returned by a $@ and may have been overwritten by the susequent $@. | test.c:129:11:129:16 | call to getenv | call to getenv | test.c:128:12:128:17 | call to getenv | call to getenv | -| test.c:163:14:163:26 | tmpvar_global | This pointer was returned by a $@ and may have been overwritten by the susequent $@. | test.c:155:19:155:24 | call to getenv | call to getenv | test.c:159:20:159:25 | call to getenv | call to getenv | -| test.c:186:18:186:18 | r | This pointer was returned by a $@ and may have been overwritten by the susequent $@. | test.c:183:7:183:15 | call to setlocale | call to setlocale | test.c:185:8:185:17 | call to localeconv | call to localeconv | -| test.c:206:10:206:15 | tmpvar | This pointer was returned by a $@ and may have been overwritten by the susequent $@. | test.c:200:12:200:17 | call to getenv | call to getenv | test.c:204:3:204:8 | call to f11fun | call to f11fun | +| test.c:21:14:21:19 | tmpvar | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.c:13:12:13:17 | call to getenv | call to getenv | test.c:17:13:17:18 | call to getenv | call to getenv | +| test.c:134:14:134:17 | temp | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.c:130:12:130:17 | call to getenv | call to getenv | test.c:131:11:131:16 | call to getenv | call to getenv | +| test.c:134:20:134:22 | tmp | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.c:131:11:131:16 | call to getenv | call to getenv | test.c:130:12:130:17 | call to getenv | call to getenv | +| test.c:165:14:165:26 | tmpvar_global | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.c:157:19:157:24 | call to getenv | call to getenv | test.c:161:20:161:25 | call to getenv | call to getenv | +| test.c:188:18:188:18 | r | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.c:185:7:185:15 | call to setlocale | call to setlocale | test.c:187:8:187:17 | call to localeconv | call to localeconv | +| test.c:208:10:208:15 | tmpvar | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.c:202:12:202:17 | call to getenv | call to getenv | test.c:206:3:206:8 | call to f11fun | call to f11fun | diff --git a/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql b/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql index a623ce5d7f..b82c43333a 100644 --- a/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql +++ b/c/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.invalidatedenvstringpointers.InvalidatedEnvStringPointers + +class TestFileQuery extends InvalidatedEnvStringPointersSharedQuery, TestQuery { } diff --git a/c/common/test/rules/invalidatedenvstringpointers/test.c b/c/common/test/rules/invalidatedenvstringpointers/test.c index 59c9593d21..183a4891c1 100644 --- a/c/common/test/rules/invalidatedenvstringpointers/test.c +++ b/c/common/test/rules/invalidatedenvstringpointers/test.c @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. #include #include #include diff --git a/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected b/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected index 556a3fe4a8..628a4f99d6 100644 --- a/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected +++ b/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected @@ -1,2 +1,2 @@ -| test.c:13:19:13:24 | call to getenv | The value of variable $@ might become invalid after a subsequent call to function `getenv`. | test.c:10:7:10:19 | tmpvar_global | tmpvar_global | -| test.c:16:20:16:25 | call to getenv | The value of variable $@ might become invalid after a subsequent call to function `getenv`. | test.c:7:9:7:20 | tmpvar_field | tmpvar_field | +| test.c:15:19:15:24 | call to getenv | The value of variable $@ might become invalid after a subsequent call to function `getenv`. | test.c:12:7:12:19 | tmpvar_global | tmpvar_global | +| test.c:18:20:18:25 | call to getenv | The value of variable $@ might become invalid after a subsequent call to function `getenv`. | test.c:9:9:9:20 | tmpvar_field | tmpvar_field | diff --git a/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql b/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql index e7cb5c5445..7a56af210d 100644 --- a/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql +++ b/c/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.invalidatedenvstringpointerswarn.InvalidatedEnvStringPointersWarn + +class TestFileQuery extends InvalidatedEnvStringPointersWarnSharedQuery, TestQuery { } diff --git a/c/common/test/rules/invalidatedenvstringpointerswarn/test.c b/c/common/test/rules/invalidatedenvstringpointerswarn/test.c index 2b678df6ac..6d4cec1d8d 100644 --- a/c/common/test/rules/invalidatedenvstringpointerswarn/test.c +++ b/c/common/test/rules/invalidatedenvstringpointerswarn/test.c @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. #include #include #include diff --git a/c/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql b/c/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql index 164aa6ed96..c1f22c408a 100644 --- a/c/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql +++ b/c/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.iofstreammissingpositioning.IOFstreamMissingPositioning + +class TestFileQuery extends IOFstreamMissingPositioningSharedQuery, TestQuery { } diff --git a/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.expected b/c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.expected similarity index 100% rename from c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.expected rename to c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.expected diff --git a/c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.ql b/c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.ql new file mode 100644 index 0000000000..87188403af --- /dev/null +++ b/c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.joinordetachthreadonlyonce.JoinOrDetachThreadOnlyOnce + +class TestFileQuery extends JoinOrDetachThreadOnlyOnceSharedQuery, TestQuery { } diff --git a/c/cert/test/rules/CON39-C/test.c b/c/common/test/rules/joinordetachthreadonlyonce/test.c similarity index 100% rename from c/cert/test/rules/CON39-C/test.c rename to c/common/test/rules/joinordetachthreadonlyonce/test.c diff --git a/c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.expected b/c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.expected new file mode 100644 index 0000000000..a381fdb7e8 --- /dev/null +++ b/c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.expected @@ -0,0 +1,16 @@ +| test.c:5:10:5:11 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:6:10:6:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:9:10:9:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:10:10:10:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:15:11:15:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:16:11:16:13 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:19:11:19:13 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:20:11:20:13 | 0 | Lowercase 'l' used as a literal suffix. | +| test.c:25:10:25:14 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:26:10:26:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:29:10:29:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:30:10:30:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:35:11:35:14 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:36:11:36:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:39:11:39:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.c:40:11:40:15 | 1 | Lowercase 'l' used as a literal suffix. | diff --git a/c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql b/c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql new file mode 100644 index 0000000000..ab353ca8a9 --- /dev/null +++ b/c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.lowercaselstartsinliteralsuffix.LowercaseLStartsInLiteralSuffix + +class TestFileQuery extends LowercaseLStartsInLiteralSuffixSharedQuery, TestQuery { } diff --git a/c/common/test/rules/lowercaselstartsinliteralsuffix/test.c b/c/common/test/rules/lowercaselstartsinliteralsuffix/test.c new file mode 100644 index 0000000000..549e90bd7d --- /dev/null +++ b/c/common/test/rules/lowercaselstartsinliteralsuffix/test.c @@ -0,0 +1,46 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +// int x = false; // COMPLIANT - reported as C++ FP in #319 +int a1 = 0L; // COMPLIANT +int a2 = 0l; // NON_COMPLIANT +int a3 = 0ll; // NON_COMPLIANT +int a4 = 0LL; // COMPLIANT +int a5 = 0uL; // COMPLIANT +int a6 = 0ul; // NON_COMPLIANT +int a7 = 0lu; // NON_COMPLIANT +int a8 = 0Lu; // COMPLIANT +int a9 = 0LU; // COMPLIANT + +long b1 = 0L; // COMPLIANT +long b2 = 0l; // NON_COMPLIANT +long b3 = 0ll; // NON_COMPLIANT +long b4 = 0LL; // COMPLIANT +long b5 = 0uL; // COMPLIANT +long b6 = 0ul; // NON_COMPLIANT +long b7 = 0lu; // NON_COMPLIANT +long b8 = 0Lu; // COMPLIANT +long b9 = 0LU; // COMPLIANT + +int c1 = 0x01L; // COMPLIANT +int c2 = 0x01l; // NON_COMPLIANT +int c3 = 0x01ll; // NON_COMPLIANT +int c4 = 0x01LL; // COMPLIANT +int c5 = 0x01uL; // COMPLIANT +int c6 = 0x01ul; // NON_COMPLIANT +int c7 = 0x01lu; // NON_COMPLIANT +int c8 = 0x01Lu; // COMPLIANT +int c9 = 0x01LU; // COMPLIANT + +long d1 = 001L; // COMPLIANT +long d2 = 001l; // NON_COMPLIANT +long d3 = 001ll; // NON_COMPLIANT +long d4 = 001LL; // COMPLIANT +long d5 = 001uL; // COMPLIANT +long d6 = 001ul; // NON_COMPLIANT +long d7 = 001lu; // NON_COMPLIANT +long d8 = 001Lu; // COMPLIANT +long d9 = 001LU; // COMPLIANT + +char *e1 = ""; +char *e2 = "ul"; +char *e3 = "UL"; \ No newline at end of file diff --git a/c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.expected b/c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.expected new file mode 100644 index 0000000000..715bbe781d --- /dev/null +++ b/c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.expected @@ -0,0 +1 @@ +| test.c:27:1:27:29 | #define MACROTHIRTEEN(X) #X ## X | Macro definition uses an # operator followed by a ## operator. | diff --git a/c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql b/c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql new file mode 100644 index 0000000000..f753b75463 --- /dev/null +++ b/c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.macroparameterfollowinghash.MacroParameterFollowingHash + +class TestFileQuery extends MacroParameterFollowingHashSharedQuery, TestQuery { } diff --git a/c/common/test/rules/macroparameterfollowinghash/test.c b/c/common/test/rules/macroparameterfollowinghash/test.c new file mode 100644 index 0000000000..d998ce8106 --- /dev/null +++ b/c/common/test/rules/macroparameterfollowinghash/test.c @@ -0,0 +1,29 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#define MACROONE 1 // COMPLIANT + +#define MACROTWO '#\'-#' + '#' // COMPLIANT + +#define MACROTHREE "##" // COMPLIANT + +#define MACROFOUR "##" + "#" // COMPLIANT + +#define MACROFIVE(X) #X // COMPLIANT + +#define MACROSIX(X, Y) X##Y // COMPLIANT + +#define MACROSEVEN "##'" #"#" // COMPLIANT + +#define MACROEIGHT '##' #"#" // COMPLIANT + +#define MACRONINE "##\"\"" + "#" // COMPLIANT + +#define MACROTEN "##\"\"'" + "#" // COMPLIANT + +#define MACROELEVEN(X) X #X #X // COMPLIANT + +#define MACROTWELVE(X) X##X##X // COMPLIANT + +#define MACROTHIRTEEN(X) #X##X // NON_COMPLIANT + +#define MACROFOURTEEN '#\'-#' + 1 #1 #1 + '#' // COMPLIANT \ No newline at end of file diff --git a/c/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql b/c/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql index 0ce5ceef5c..2ff9477919 100644 --- a/c/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql +++ b/c/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.macroparameternotenclosedinparentheses.MacroParameterNotEnclosedInParentheses + +class TestFileQuery extends MacroParameterNotEnclosedInParenthesesSharedQuery, TestQuery { } diff --git a/c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.expected b/c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.expected new file mode 100644 index 0000000000..01b4649fa8 --- /dev/null +++ b/c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.expected @@ -0,0 +1 @@ +| test.c:13:8:13:13 | call to memcmp | memcmp accesses bits which are not part of the object's value representation. | diff --git a/c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql b/c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql new file mode 100644 index 0000000000..55290047a1 --- /dev/null +++ b/c/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.memcmpusedtocomparepaddingdata.MemcmpUsedToComparePaddingData + +class TestFileQuery extends MemcmpUsedToComparePaddingDataSharedQuery, TestQuery { } diff --git a/c/common/test/rules/memcmpusedtocomparepaddingdata/test.c b/c/common/test/rules/memcmpusedtocomparepaddingdata/test.c new file mode 100644 index 0000000000..00cf4c3230 --- /dev/null +++ b/c/common/test/rules/memcmpusedtocomparepaddingdata/test.c @@ -0,0 +1,20 @@ +#include + +struct S1 { + unsigned char buffType; + int size; +}; + +struct S2 { + unsigned char buff[8]; +}; + +void f1(const struct S1 *s1, const struct S1 *s2) { + if (!memcmp(s1, s2, sizeof(struct S1))) { // NON_COMPLIANT + } +} + +void f2(const struct S2 *s1, const struct S2 *s2) { + if (!memcmp(&s1, &s2, sizeof(struct S2))) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.expected b/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.expected new file mode 100644 index 0000000000..f6cde5d73b --- /dev/null +++ b/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.expected @@ -0,0 +1 @@ +| test.c:2:6:2:7 | definition of f1 | The redeclaration of $@ with internal linkage misses the static specifier. | test.c:1:13:1:14 | declaration of f1 | function | diff --git a/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql b/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql new file mode 100644 index 0000000000..f84cbf8698 --- /dev/null +++ b/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.missingstaticspecifierfunctionredeclarationshared.MissingStaticSpecifierFunctionRedeclarationShared + +class TestFileQuery extends MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery, TestQuery { +} diff --git a/cpp/autosar/test/rules/M3-3-2/test.cpp b/c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/test.c similarity index 100% rename from cpp/autosar/test/rules/M3-3-2/test.cpp rename to c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/test.c diff --git a/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.expected b/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.expected new file mode 100644 index 0000000000..34a7723bcd --- /dev/null +++ b/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.expected @@ -0,0 +1 @@ +| test.c:2:12:2:12 | declaration of g | The redeclaration of $@ with internal linkage misses the static specifier. | test.c:1:12:1:12 | definition of g | g | diff --git a/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.ql b/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.ql new file mode 100644 index 0000000000..3d6d2019fb --- /dev/null +++ b/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.missingstaticspecifierobjectredeclarationshared.MissingStaticSpecifierObjectRedeclarationShared + +class TestFileQuery extends MissingStaticSpecifierObjectRedeclarationSharedSharedQuery, TestQuery { +} diff --git a/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/test.c b/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/test.c new file mode 100644 index 0000000000..d98d71c6f0 --- /dev/null +++ b/c/common/test/rules/missingstaticspecifierobjectredeclarationshared/test.c @@ -0,0 +1,8 @@ +static int g = 0; +extern int g; // NON_COMPLIANT + +static int g1; +static int g1 = 0; // COMPLIANT + +int g2; +int g2 = 0; // COMPLIANT diff --git a/c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.expected b/c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.expected new file mode 100644 index 0000000000..f3b94b6095 --- /dev/null +++ b/c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.expected @@ -0,0 +1,113 @@ +problems +| test.c:12:8:12:9 | l2 | test.c:8:14:8:20 | ... / ... | test.c:12:8:12:9 | l2 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.c:8:14:8:20 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:13:8:13:9 | l3 | test.c:8:14:8:20 | ... / ... | test.c:13:8:13:9 | l3 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.c:8:14:8:20 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:18:8:18:9 | l2 | test.c:8:14:8:20 | ... / ... | test.c:18:3:18:9 | l2 | Possibly infinite float value $@ flows to cast to integer. | test.c:8:14:8:20 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:19:8:19:9 | l3 | test.c:8:14:8:20 | ... / ... | test.c:19:3:19:9 | l3 | Possibly infinite float value $@ flows to cast to integer. | test.c:8:14:8:20 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:27:19:27:20 | l2 | test.c:8:14:8:20 | ... / ... | test.c:27:19:27:20 | l2 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.c:8:14:8:20 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:28:19:28:20 | l3 | test.c:8:14:8:20 | ... / ... | test.c:28:19:28:20 | l3 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.c:8:14:8:20 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:38:8:38:9 | l7 | test.c:31:14:32:15 | ... / ... | test.c:38:3:38:9 | l7 | Possibly infinite float value $@ flows to cast to integer. | test.c:31:14:32:15 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:61:11:61:17 | ... / ... | test.c:61:5:61:18 | ... / ... | test.c:61:5:61:18 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.c:61:11:61:17 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:66:11:66:19 | ... / ... | test.c:66:5:66:20 | ... / ... | test.c:66:5:66:20 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.c:66:11:66:19 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:72:20:72:28 | ... / ... | test.c:72:14:72:29 | ... / ... | test.c:72:14:72:29 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.c:72:20:72:28 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:75:24:75:32 | ... / ... | test.c:75:18:75:33 | ... / ... | test.c:75:18:75:33 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.c:75:24:75:32 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:79:10:79:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:79:5:79:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:87:10:87:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:87:5:87:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:91:10:91:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:91:5:91:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:93:10:93:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:93:5:93:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:99:10:99:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:99:5:99:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:105:10:105:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:105:5:105:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:111:10:111:12 | l12 | test.c:77:15:77:21 | ... / ... | test.c:111:5:111:12 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:114:21:114:23 | l12 | test.c:77:15:77:21 | ... / ... | test.c:114:16:114:23 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:117:28:117:30 | l12 | test.c:77:15:77:21 | ... / ... | test.c:117:23:117:30 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:120:25:120:27 | l12 | test.c:77:15:77:21 | ... / ... | test.c:120:20:120:27 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.c:77:15:77:21 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:163:9:164:15 | ... / ... | test.c:163:3:164:16 | ... / ... | test.c:163:3:164:16 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.c:163:9:164:15 | ... / ... | from division by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:175:32:175:32 | p | test.c:189:51:189:59 | ... / ... | test.c:175:27:175:32 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.c:189:51:189:59 | ... / ... | from division by zero | test.c:189:6:189:24 | addInfThenCastToInt | addInfThenCastToInt | +| test.c:175:32:175:32 | p | test.c:193:13:194:15 | ... / ... | test.c:175:27:175:32 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.c:193:13:194:15 | ... / ... | from division by zero | test.c:192:6:192:7 | f2 | f2 | +| test.c:175:32:175:32 | p | test.c:204:19:204:27 | ... / ... | test.c:175:27:175:32 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.c:204:19:204:27 | ... / ... | from division by zero | test.c:192:6:192:7 | f2 | f2 | +| test.c:185:18:185:18 | p | test.c:200:25:200:33 | ... / ... | test.c:185:13:185:18 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.c:200:25:200:33 | ... / ... | from division by zero | test.c:192:6:192:7 | f2 | f2 | +edges +| test.c:8:14:8:20 | ... / ... | test.c:8:14:8:20 | ... / ... | provenance | | +| test.c:8:14:8:20 | ... / ... | test.c:9:14:9:16 | - ... | provenance | Config | +| test.c:8:14:8:20 | ... / ... | test.c:12:8:12:9 | l2 | provenance | | +| test.c:8:14:8:20 | ... / ... | test.c:18:3:18:9 | l2 | provenance | | +| test.c:8:14:8:20 | ... / ... | test.c:27:19:27:20 | l2 | provenance | | +| test.c:9:14:9:16 | - ... | test.c:9:14:9:16 | - ... | provenance | | +| test.c:9:14:9:16 | - ... | test.c:13:8:13:9 | l3 | provenance | | +| test.c:9:14:9:16 | - ... | test.c:19:3:19:9 | l3 | provenance | | +| test.c:9:14:9:16 | - ... | test.c:28:19:28:20 | l3 | provenance | | +| test.c:31:14:32:15 | ... / ... | test.c:31:14:32:15 | ... / ... | provenance | | +| test.c:31:14:32:15 | ... / ... | test.c:38:3:38:9 | l7 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:77:15:77:21 | ... / ... | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:79:5:79:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:87:5:87:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:91:5:91:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:93:5:93:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:99:5:99:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:105:5:105:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:111:5:111:12 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:114:16:114:23 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:117:23:117:30 | l12 | provenance | | +| test.c:77:15:77:21 | ... / ... | test.c:120:20:120:27 | l12 | provenance | | +| test.c:175:22:175:22 | p | test.c:175:27:175:32 | p | provenance | | +| test.c:183:34:183:34 | p | test.c:185:13:185:18 | p | provenance | | +| test.c:189:32:189:32 | p | test.c:189:47:189:59 | ... + ... | provenance | Config | +| test.c:189:47:189:59 | ... + ... | test.c:175:22:175:22 | p | provenance | | +| test.c:189:47:189:59 | ... + ... | test.c:175:22:175:22 | p | provenance | | +| test.c:189:51:189:59 | ... / ... | test.c:189:47:189:59 | ... + ... | provenance | Config | +| test.c:193:13:194:15 | ... / ... | test.c:175:22:175:22 | p | provenance | | +| test.c:200:25:200:33 | ... / ... | test.c:183:34:183:34 | p | provenance | | +| test.c:204:19:204:27 | ... / ... | test.c:204:19:204:27 | ... / ... | provenance | | +| test.c:204:19:204:27 | ... / ... | test.c:206:21:206:31 | ... + ... | provenance | Config | +| test.c:206:21:206:31 | ... + ... | test.c:206:21:206:31 | ... + ... | provenance | | +| test.c:206:21:206:31 | ... + ... | test.c:208:13:208:21 | middleInf | provenance | | +| test.c:206:21:206:31 | ... + ... | test.c:210:23:210:31 | middleInf | provenance | | +| test.c:208:13:208:21 | middleInf | test.c:175:22:175:22 | p | provenance | | +| test.c:210:23:210:31 | middleInf | test.c:189:32:189:32 | p | provenance | | +nodes +| test.c:8:14:8:20 | ... / ... | semmle.label | ... / ... | +| test.c:8:14:8:20 | ... / ... | semmle.label | ... / ... | +| test.c:9:14:9:16 | - ... | semmle.label | - ... | +| test.c:9:14:9:16 | - ... | semmle.label | - ... | +| test.c:12:8:12:9 | l2 | semmle.label | l2 | +| test.c:13:8:13:9 | l3 | semmle.label | l3 | +| test.c:18:3:18:9 | l2 | semmle.label | l2 | +| test.c:19:3:19:9 | l3 | semmle.label | l3 | +| test.c:27:19:27:20 | l2 | semmle.label | l2 | +| test.c:28:19:28:20 | l3 | semmle.label | l3 | +| test.c:31:14:32:15 | ... / ... | semmle.label | ... / ... | +| test.c:31:14:32:15 | ... / ... | semmle.label | ... / ... | +| test.c:38:3:38:9 | l7 | semmle.label | l7 | +| test.c:61:5:61:18 | ... / ... | semmle.label | ... / ... | +| test.c:66:5:66:20 | ... / ... | semmle.label | ... / ... | +| test.c:72:14:72:29 | ... / ... | semmle.label | ... / ... | +| test.c:75:18:75:33 | ... / ... | semmle.label | ... / ... | +| test.c:77:15:77:21 | ... / ... | semmle.label | ... / ... | +| test.c:77:15:77:21 | ... / ... | semmle.label | ... / ... | +| test.c:79:5:79:12 | l12 | semmle.label | l12 | +| test.c:87:5:87:12 | l12 | semmle.label | l12 | +| test.c:91:5:91:12 | l12 | semmle.label | l12 | +| test.c:93:5:93:12 | l12 | semmle.label | l12 | +| test.c:99:5:99:12 | l12 | semmle.label | l12 | +| test.c:105:5:105:12 | l12 | semmle.label | l12 | +| test.c:111:5:111:12 | l12 | semmle.label | l12 | +| test.c:114:16:114:23 | l12 | semmle.label | l12 | +| test.c:117:23:117:30 | l12 | semmle.label | l12 | +| test.c:120:20:120:27 | l12 | semmle.label | l12 | +| test.c:163:3:164:16 | ... / ... | semmle.label | ... / ... | +| test.c:175:22:175:22 | p | semmle.label | p | +| test.c:175:27:175:32 | p | semmle.label | p | +| test.c:183:34:183:34 | p | semmle.label | p | +| test.c:185:13:185:18 | p | semmle.label | p | +| test.c:189:32:189:32 | p | semmle.label | p | +| test.c:189:47:189:59 | ... + ... | semmle.label | ... + ... | +| test.c:189:47:189:59 | ... + ... | semmle.label | ... + ... | +| test.c:189:51:189:59 | ... / ... | semmle.label | ... / ... | +| test.c:193:13:194:15 | ... / ... | semmle.label | ... / ... | +| test.c:200:25:200:33 | ... / ... | semmle.label | ... / ... | +| test.c:204:19:204:27 | ... / ... | semmle.label | ... / ... | +| test.c:204:19:204:27 | ... / ... | semmle.label | ... / ... | +| test.c:206:21:206:31 | ... + ... | semmle.label | ... + ... | +| test.c:206:21:206:31 | ... + ... | semmle.label | ... + ... | +| test.c:208:13:208:21 | middleInf | semmle.label | middleInf | +| test.c:210:23:210:31 | middleInf | semmle.label | middleInf | +subpaths \ No newline at end of file diff --git a/c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql b/c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql new file mode 100644 index 0000000000..f0d160a650 --- /dev/null +++ b/c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.misuseofinfinitefloatingpointvalue.MisuseOfInfiniteFloatingPointValue + +class TestFileQuery extends MisuseOfInfiniteFloatingPointValueSharedQuery, TestQuery { } diff --git a/c/common/test/rules/misuseofinfinitefloatingpointvalue/test.c b/c/common/test/rules/misuseofinfinitefloatingpointvalue/test.c new file mode 100644 index 0000000000..6a4ebd94b9 --- /dev/null +++ b/c/common/test/rules/misuseofinfinitefloatingpointvalue/test.c @@ -0,0 +1,212 @@ +#include "math.h" + +float getFloat() { return 1.0; } + +// Parameter could be infinity +void f1(float p1) { + float l1 = 1; + float l2 = 1.0 / 0; + float l3 = -l2; + + 10 / l1; // COMPLIANT + 10 / l2; // NON_COMPLIANT: Underflows to zero + 10 / l3; // NON_COMPLIANT: Underflow to negative zero + 10 / p1; // COMPLIANT: Reduce false positives by assuming not infinity + 10 / getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + (int)l1; // COMPLIANT + (int)l2; // NON_COMPLIANT + (int)l3; // NON_COMPLIANT + (int)p1; // COMPLIANT: Reduce false positives by assuming not infinity + (int)getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + // Not NaN: + float l4 = l1 / l1; // COMPLIANT + + // NaN because of infinity divided by itself: + float l5 = l2 / l2; // NON_COMPLIANT: Division by infinity not allowed. + float l6 = l3 / l3; // NON_COMPLIANT: Division by infinity not allowed. + + // NaN because of zero divided by itself: + float l7 = getFloat() / + p1; // COMPLIANT: Reduce false positives by assuming not infinity + float l8 = 0.0 / 0.0; + + (int)l4; // COMPLIANT + (int)l5; // COMPLIANT: Casting NaN to int + (int)l6; // COMPLIANT: Casting NaN to int + (int)l7; // NON_COMPLIANT: Casting Infinity to int + (int)l8; // COMPLIANT: Casting NaN to int + + l4 == 0; // COMPLIANT + l4 != 0; // COMPLIANT + l4 <= 0; // COMPLIANT + l4 < 0; // COMPLIANT + l4 >= 0; // COMPLIANT + l5 == 0; // NON_COMPLIANT: Comparison with NaN always false + l5 != 0; // NON_COMPLIANT: Comparison with NaN always false + l5 < 0; // NON_COMPLIANT: Comparison with NaN always false + l5 <= 0; // NON_COMPLIANT: Comparison with NaN always false + l5 > 0; // NON_COMPLIANT: Comparison with NaN always false + l5 >= 0; // NON_COMPLIANT: Comparison with NaN always false + l6 == 0; // NON_COMPLIANT: Comparison with NaN always false + l7 == 0; // NON_COMPLIANT: Comparison with NaN always false + l8 == 0; // NON_COMPLIANT: Comparison with NaN always false + + // Guards + float l9 = 0; + if (l9 != 0) { + (int)(l9 / l9); // COMPLIANT: l9 is not zero + } else { + (int)(l9 / l9); // NON_COMPLIANT[False positive]: Guarded to not be NaN + } + + float l10 = 0; + if (l10 == 0) { + (int)(l10 / l10); // NON_COMPLIANT[False positive]: Casting NaN to integer + } else { + (int)(l10 / l10); // COMPLIANT: Guarded to not be NaN + } + + float l11 = 0; + l11 == 0 ? (int)(l11 / l11) : 0; // NON_COMPLIANT[False positive] + l11 == 0 ? 0 : (int)(l11 / l11); // COMPLIANT + l11 != 0 ? (int)(l11 / l11) : 0; // COMPLIANT + l11 != 0 ? 0 : (int)(l11 / l11); // NON_COMPLIANT[False positive] + + float l12 = 1.0 / 0; + if (isinf(l12)) { + (int)l12; // NON_COMPLIANT: Casting Infinity to integer + } else { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } + + if (!isinf(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // NON_COMPLIANT: Casting Infinity to integer + } + + if (isinf(l12) == 1) { + (int)l12; // NON_COMPLIANT: Must be +Infinity + } else { + (int)l12; // NON_COMPLIANT: May be -Infinity + } + + if (isfinite(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // NON_COMPLIANT: Casting Infinity to integer + } + + if (isnormal(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // NON_COMPLIANT: Casting Infinity to integer + } + + if (isnan(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // NON_COMPLIANT: Casting Infinity to integer + } + + isinf(l12) ? (int)l12 : 0; // NON_COMPLIANT: Check on wrong branch + isinf(l12) ? 0 : (int)l12; // COMPLIANT: Checked not infinite before use + isfinite(l12) ? (int)l12 : 0; // COMPLIANT: Checked finite before use + isfinite(l12) ? 0 : (int)l12; // NON_COMPLIANT: Checked on wrong branch + isnan(l12) ? (int)l12 + : 0; // COMPLIANT: Checked NaN, therefore not infinite, before use + isnan(l12) ? 0 : (int)l12; // NON_COMPLIANT: Check on wrong branch + + float l13 = 0.0 / 0; + if (isinf(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // COMPLIANT: Casting NaN to integer + } + + if (isinf(l13) == 1) { + (int)l13; // COMPLIANT: Guarded not to be NaN (must be +Infinity) + } else { + (int)l13; // COMPLIANT: Casting NaN to integer + } + + if (isfinite(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // COMPLIANT: Casting NaN to integer + } + + if (isnormal(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // COMPLIANT: Casting NaN to integer + } + + if (isnan(l13)) { + (int)l13; // COMPLIANT: Casting NaN to integer + } else { + (int)l13; // COMPLIANT: Guarded not to be NaN + } + + isinf(l13) ? (int)l13 + : 0; // COMPLIANT: Checked infinite, therefore not NaN, before use + isinf(l13) ? 0 : (int)l13; // COMPLIANT: Check on wrong branch + isfinite(l13) ? (int)l13 : 0; // COMPLIANT: Checked finite before use + isfinite(l13) ? 0 : (int)l13; // COMPLIANT: Checked on wrong branch + isnan(l13) ? (int)l13 : 0; // COMPLIANT: Check on wrong branch + isnan(l13) ? 0 : (int)l13; // COMPLIANT: Checked not NaN before use + + (int)pow(2, p1); // NON_COMPLIANT[False negative]: likely to be Infinity + (int)pow(2, sin(p1)); // COMPLIANT: not likely to be Infinity + (int)(1 / + sin(p1)); // NON_COMPLIANT: possible infinity from zero in denominator + (int)(1 / log(p1)); // COMPLIANT: not possibly zero in denominator + (int)pow(p1, p1); // COMPLIANT: NaN if p1 is zero + if (p1 != 0) { + (int)pow(p1, p1); // COMPLIANT: p1 is not zero + } + + (int)acos(p1); // COMPLIANT: NaN if p1 is not within -1..1 + (int)acos(cos(p1)); // COMPLIANT: cos(p1) is within -1..1 +} + +void castToInt(float p) { (int)p; } + +void checkBeforeCastToInt(float p) { + if (isfinite(p)) { + castToInt(p); + } +} + +void castToIntToFloatToInt(float p) { + // This should be reported as a violation, but not downstream from here. + castToInt((int)p); +} + +void addOneThenCastToInt(float p) { castToInt(p + 1); } +void addInfThenCastToInt(float p) { castToInt(p + 1.0 / 0.0); } +void addNaNThenCastToInt(float p) { castToInt(p + 0.0 / 0.0); } + +void f2() { + castToInt(1.0 / + 0.0); // NON_COMPLIANT: Infinity flows to denominator in division + castToInt(0.0 / 0.0); // COMPLIANT + checkBeforeCastToInt(1.0 / 0.0); // COMPLIANT + checkBeforeCastToInt(0.0 / 0.0); // COMPLIANT + addOneThenCastToInt(1.0 / 0.0); // NON_COMPLIANT[False negative] + addOneThenCastToInt(0.0 / 0.0); // NON_COMPLIANT + castToIntToFloatToInt(1.0 / 0.0); // NON_COMPLIANT + castToIntToFloatToInt(0.0 / 0.0); // COMPLIANT + + // Check that during flow analysis, we only report the true root cause: + float rootInf = 1.0 / 0.0; + float rootNaN = 0.0 / 0.0; + float middleInf = rootInf + 1; + float middleNaN = rootNaN + 1; + castToInt(middleInf); // NON_COMPLIANT + castToInt(middleNaN); // COMPLIANT + addInfThenCastToInt(middleInf); // NON_COMPLIANT + addNaNThenCastToInt(middleNaN); // COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.expected b/c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.expected new file mode 100644 index 0000000000..b567e06bc2 --- /dev/null +++ b/c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.expected @@ -0,0 +1,136 @@ +problems +| test.c:36:8:36:9 | l5 | test.c:27:14:27:20 | ... / ... | test.c:36:3:36:9 | l5 | Possible NaN value $@ flows to a cast to integer. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:37:8:37:9 | l6 | test.c:28:14:28:20 | ... / ... | test.c:37:3:37:9 | l6 | Possible NaN value $@ flows to a cast to integer. | test.c:28:14:28:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:38:8:38:9 | l7 | test.c:31:14:32:15 | ... / ... | test.c:38:3:38:9 | l7 | Possible NaN value $@ flows to a cast to integer. | test.c:31:14:32:15 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:39:8:39:9 | l8 | test.c:33:14:33:22 | ... / ... | test.c:39:3:39:9 | l8 | Possible NaN value $@ flows to a cast to integer. | test.c:33:14:33:22 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:46:3:46:4 | l5 | test.c:27:14:27:20 | ... / ... | test.c:46:3:46:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:47:3:47:4 | l5 | test.c:27:14:27:20 | ... / ... | test.c:47:3:47:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:48:3:48:4 | l5 | test.c:27:14:27:20 | ... / ... | test.c:48:3:48:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:49:3:49:4 | l5 | test.c:27:14:27:20 | ... / ... | test.c:49:3:49:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:50:3:50:4 | l5 | test.c:27:14:27:20 | ... / ... | test.c:50:3:50:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:51:3:51:4 | l5 | test.c:27:14:27:20 | ... / ... | test.c:51:3:51:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:27:14:27:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:52:3:52:4 | l6 | test.c:28:14:28:20 | ... / ... | test.c:52:3:52:4 | l6 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:28:14:28:20 | ... / ... | from division of infinity by infinity | test.c:6:6:6:7 | f1 | f1 | +| test.c:53:3:53:4 | l7 | test.c:31:14:32:15 | ... / ... | test.c:53:3:53:4 | l7 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:31:14:32:15 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:54:3:54:4 | l8 | test.c:33:14:33:22 | ... / ... | test.c:54:3:54:4 | l8 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.c:33:14:33:22 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:61:11:61:17 | ... / ... | test.c:61:5:61:18 | ... / ... | test.c:61:5:61:18 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.c:61:11:61:17 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:66:11:66:19 | ... / ... | test.c:66:5:66:20 | ... / ... | test.c:66:5:66:20 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.c:66:11:66:19 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:72:20:72:28 | ... / ... | test.c:72:14:72:29 | ... / ... | test.c:72:14:72:29 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.c:72:20:72:28 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:75:24:75:32 | ... / ... | test.c:75:18:75:33 | ... / ... | test.c:75:18:75:33 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.c:75:24:75:32 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:126:10:126:12 | l13 | test.c:122:15:122:21 | ... / ... | test.c:126:5:126:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:132:10:132:12 | l13 | test.c:122:15:122:21 | ... / ... | test.c:132:5:132:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:138:10:138:12 | l13 | test.c:122:15:122:21 | ... / ... | test.c:138:5:138:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:144:10:144:12 | l13 | test.c:122:15:122:21 | ... / ... | test.c:144:5:144:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:148:10:148:12 | l13 | test.c:122:15:122:21 | ... / ... | test.c:148:5:148:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:155:25:155:27 | l13 | test.c:122:15:122:21 | ... / ... | test.c:155:20:155:27 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:157:28:157:30 | l13 | test.c:122:15:122:21 | ... / ... | test.c:157:23:157:30 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:158:21:158:23 | l13 | test.c:122:15:122:21 | ... / ... | test.c:158:16:158:23 | l13 | Possible NaN value $@ flows to a cast to integer. | test.c:122:15:122:21 | ... / ... | from division of zero by zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:165:8:165:10 | call to pow | test.c:165:3:165:18 | call to pow | test.c:165:3:165:18 | call to pow | Possible NaN value $@ flows to a cast to integer. | test.c:165:8:165:10 | call to pow | both arguments are equal to zero | test.c:6:6:6:7 | f1 | f1 | +| test.c:170:8:170:11 | call to acos | test.c:170:3:170:15 | call to acos | test.c:170:3:170:15 | call to acos | Possible NaN value $@ flows to a cast to integer. | test.c:170:8:170:11 | call to acos | the argument has a range -1000000000000000...1000000000000000 which is outside the domain of this function (-1.0...1.0) | test.c:6:6:6:7 | f1 | f1 | +| test.c:174:32:174:32 | p | test.c:189:51:189:59 | ... / ... | test.c:174:27:174:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.c:189:51:189:59 | ... / ... | from division of zero by zero | test.c:189:6:189:24 | addNaNThenCastToInt | addNaNThenCastToInt | +| test.c:174:32:174:32 | p | test.c:193:13:193:21 | ... / ... | test.c:174:27:174:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.c:193:13:193:21 | ... / ... | from division of zero by zero | test.c:191:6:191:7 | f2 | f2 | +| test.c:174:32:174:32 | p | test.c:197:23:197:31 | ... / ... | test.c:174:27:174:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.c:197:23:197:31 | ... / ... | from division of zero by zero | test.c:191:6:191:7 | f2 | f2 | +| test.c:174:32:174:32 | p | test.c:203:19:203:27 | ... / ... | test.c:174:27:174:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.c:203:19:203:27 | ... / ... | from division of zero by zero | test.c:191:6:191:7 | f2 | f2 | +| test.c:184:18:184:18 | p | test.c:199:25:199:33 | ... / ... | test.c:184:13:184:18 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.c:199:25:199:33 | ... / ... | from division of zero by zero | test.c:191:6:191:7 | f2 | f2 | +edges +| test.c:27:14:27:20 | ... / ... | test.c:27:14:27:20 | ... / ... | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:36:3:36:9 | l5 | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:46:3:46:4 | l5 | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:47:3:47:4 | l5 | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:48:3:48:4 | l5 | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:49:3:49:4 | l5 | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:50:3:50:4 | l5 | provenance | | +| test.c:27:14:27:20 | ... / ... | test.c:51:3:51:4 | l5 | provenance | | +| test.c:28:14:28:20 | ... / ... | test.c:28:14:28:20 | ... / ... | provenance | | +| test.c:28:14:28:20 | ... / ... | test.c:37:3:37:9 | l6 | provenance | | +| test.c:28:14:28:20 | ... / ... | test.c:52:3:52:4 | l6 | provenance | | +| test.c:31:14:32:15 | ... / ... | test.c:31:14:32:15 | ... / ... | provenance | | +| test.c:31:14:32:15 | ... / ... | test.c:38:3:38:9 | l7 | provenance | | +| test.c:31:14:32:15 | ... / ... | test.c:53:3:53:4 | l7 | provenance | | +| test.c:33:14:33:22 | ... / ... | test.c:33:14:33:22 | ... / ... | provenance | | +| test.c:33:14:33:22 | ... / ... | test.c:39:3:39:9 | l8 | provenance | | +| test.c:33:14:33:22 | ... / ... | test.c:54:3:54:4 | l8 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:122:15:122:21 | ... / ... | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:126:5:126:12 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:132:5:132:12 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:138:5:138:12 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:144:5:144:12 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:148:5:148:12 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:155:20:155:27 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:157:23:157:30 | l13 | provenance | | +| test.c:122:15:122:21 | ... / ... | test.c:158:16:158:23 | l13 | provenance | | +| test.c:174:22:174:22 | p | test.c:174:27:174:32 | p | provenance | | +| test.c:182:34:182:34 | p | test.c:184:13:184:18 | p | provenance | | +| test.c:187:32:187:32 | p | test.c:187:47:187:51 | ... + ... | provenance | Config | +| test.c:187:47:187:51 | ... + ... | test.c:174:22:174:22 | p | provenance | | +| test.c:189:32:189:32 | p | test.c:189:47:189:59 | ... + ... | provenance | Config | +| test.c:189:47:189:59 | ... + ... | test.c:174:22:174:22 | p | provenance | | +| test.c:189:47:189:59 | ... + ... | test.c:174:22:174:22 | p | provenance | | +| test.c:189:51:189:59 | ... / ... | test.c:189:47:189:59 | ... + ... | provenance | Config | +| test.c:193:13:193:21 | ... / ... | test.c:174:22:174:22 | p | provenance | | +| test.c:197:23:197:31 | ... / ... | test.c:187:32:187:32 | p | provenance | | +| test.c:199:25:199:33 | ... / ... | test.c:182:34:182:34 | p | provenance | | +| test.c:203:19:203:27 | ... / ... | test.c:203:19:203:27 | ... / ... | provenance | | +| test.c:203:19:203:27 | ... / ... | test.c:205:21:205:31 | ... + ... | provenance | Config | +| test.c:205:21:205:31 | ... + ... | test.c:205:21:205:31 | ... + ... | provenance | | +| test.c:205:21:205:31 | ... + ... | test.c:207:13:207:21 | middleNaN | provenance | | +| test.c:205:21:205:31 | ... + ... | test.c:209:23:209:31 | middleNaN | provenance | | +| test.c:207:13:207:21 | middleNaN | test.c:174:22:174:22 | p | provenance | | +| test.c:209:23:209:31 | middleNaN | test.c:189:32:189:32 | p | provenance | | +nodes +| test.c:27:14:27:20 | ... / ... | semmle.label | ... / ... | +| test.c:27:14:27:20 | ... / ... | semmle.label | ... / ... | +| test.c:28:14:28:20 | ... / ... | semmle.label | ... / ... | +| test.c:28:14:28:20 | ... / ... | semmle.label | ... / ... | +| test.c:31:14:32:15 | ... / ... | semmle.label | ... / ... | +| test.c:31:14:32:15 | ... / ... | semmle.label | ... / ... | +| test.c:33:14:33:22 | ... / ... | semmle.label | ... / ... | +| test.c:33:14:33:22 | ... / ... | semmle.label | ... / ... | +| test.c:36:3:36:9 | l5 | semmle.label | l5 | +| test.c:37:3:37:9 | l6 | semmle.label | l6 | +| test.c:38:3:38:9 | l7 | semmle.label | l7 | +| test.c:39:3:39:9 | l8 | semmle.label | l8 | +| test.c:46:3:46:4 | l5 | semmle.label | l5 | +| test.c:47:3:47:4 | l5 | semmle.label | l5 | +| test.c:48:3:48:4 | l5 | semmle.label | l5 | +| test.c:49:3:49:4 | l5 | semmle.label | l5 | +| test.c:50:3:50:4 | l5 | semmle.label | l5 | +| test.c:51:3:51:4 | l5 | semmle.label | l5 | +| test.c:52:3:52:4 | l6 | semmle.label | l6 | +| test.c:53:3:53:4 | l7 | semmle.label | l7 | +| test.c:54:3:54:4 | l8 | semmle.label | l8 | +| test.c:61:5:61:18 | ... / ... | semmle.label | ... / ... | +| test.c:66:5:66:20 | ... / ... | semmle.label | ... / ... | +| test.c:72:14:72:29 | ... / ... | semmle.label | ... / ... | +| test.c:75:18:75:33 | ... / ... | semmle.label | ... / ... | +| test.c:122:15:122:21 | ... / ... | semmle.label | ... / ... | +| test.c:122:15:122:21 | ... / ... | semmle.label | ... / ... | +| test.c:126:5:126:12 | l13 | semmle.label | l13 | +| test.c:132:5:132:12 | l13 | semmle.label | l13 | +| test.c:138:5:138:12 | l13 | semmle.label | l13 | +| test.c:144:5:144:12 | l13 | semmle.label | l13 | +| test.c:148:5:148:12 | l13 | semmle.label | l13 | +| test.c:155:20:155:27 | l13 | semmle.label | l13 | +| test.c:157:23:157:30 | l13 | semmle.label | l13 | +| test.c:158:16:158:23 | l13 | semmle.label | l13 | +| test.c:165:3:165:18 | call to pow | semmle.label | call to pow | +| test.c:170:3:170:15 | call to acos | semmle.label | call to acos | +| test.c:174:22:174:22 | p | semmle.label | p | +| test.c:174:27:174:32 | p | semmle.label | p | +| test.c:182:34:182:34 | p | semmle.label | p | +| test.c:184:13:184:18 | p | semmle.label | p | +| test.c:187:32:187:32 | p | semmle.label | p | +| test.c:187:47:187:51 | ... + ... | semmle.label | ... + ... | +| test.c:189:32:189:32 | p | semmle.label | p | +| test.c:189:47:189:59 | ... + ... | semmle.label | ... + ... | +| test.c:189:47:189:59 | ... + ... | semmle.label | ... + ... | +| test.c:189:51:189:59 | ... / ... | semmle.label | ... / ... | +| test.c:193:13:193:21 | ... / ... | semmle.label | ... / ... | +| test.c:197:23:197:31 | ... / ... | semmle.label | ... / ... | +| test.c:199:25:199:33 | ... / ... | semmle.label | ... / ... | +| test.c:203:19:203:27 | ... / ... | semmle.label | ... / ... | +| test.c:203:19:203:27 | ... / ... | semmle.label | ... / ... | +| test.c:205:21:205:31 | ... + ... | semmle.label | ... + ... | +| test.c:205:21:205:31 | ... + ... | semmle.label | ... + ... | +| test.c:207:13:207:21 | middleNaN | semmle.label | middleNaN | +| test.c:209:23:209:31 | middleNaN | semmle.label | middleNaN | +subpaths diff --git a/c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql b/c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql new file mode 100644 index 0000000000..a1f729ed02 --- /dev/null +++ b/c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.misuseofnanfloatingpointvalue.MisuseOfNaNFloatingPointValue + +class TestFileQuery extends MisuseOfNaNFloatingPointValueSharedQuery, TestQuery { } diff --git a/c/common/test/rules/misuseofnanfloatingpointvalue/test.c b/c/common/test/rules/misuseofnanfloatingpointvalue/test.c new file mode 100644 index 0000000000..bd997282f0 --- /dev/null +++ b/c/common/test/rules/misuseofnanfloatingpointvalue/test.c @@ -0,0 +1,210 @@ +#include "math.h" + +float getFloat() { return 1.0; } + +// Parameter could be infinity +void f1(float p1) { + float l1 = 1; + float l2 = 1.0 / 0; + float l3 = -l2; + + 10 / l1; // COMPLIANT + 10 / l2; // COMPLIANT: Underflows to zero + 10 / l3; // COMPLIANT: Underflow to negative zero + 10 / p1; // COMPLIANT: Reduce false positives by assuming not infinity + 10 / getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + (int)l1; // COMPLIANT + (int)l2; // COMPLIANT + (int)l3; // COMPLIANT + (int)p1; // COMPLIANT: Reduce false positives by assuming not infinity + (int)getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + // Not NaN: + float l4 = l1 / l1; // COMPLIANT + + // NaN because of infinity divided by itself: + float l5 = l2 / l2; // COMPLIANT: Division by infinity not allowed. + float l6 = l3 / l3; // COMPLIANT: Division by infinity not allowed. + + // NaN because of zero divided by itself: + float l7 = getFloat() / + p1; // COMPLIANT: Reduce false positives by assuming not infinity + float l8 = 0.0 / 0.0; + + (int)l4; // COMPLIANT + (int)l5; // NON_COMPLIANT: Casting NaN to int + (int)l6; // NON_COMPLIANT: Casting NaN to int + (int)l7; // NON_COMPLIANT: Casting NaN to int + (int)l8; // NON_COMPLIANT: Casting NaN to int + + l4 == 0; // COMPLIANT + l4 != 0; // COMPLIANT + l4 <= 0; // COMPLIANT + l4 < 0; // COMPLIANT + l4 >= 0; // COMPLIANT + l5 == 0; // NON_COMPLIANT: Comparison with NaN always false + l5 != 0; // NON_COMPLIANT: Comparison with NaN always false + l5 < 0; // NON_COMPLIANT: Comparison with NaN always false + l5 <= 0; // NON_COMPLIANT: Comparison with NaN always false + l5 > 0; // NON_COMPLIANT: Comparison with NaN always false + l5 >= 0; // NON_COMPLIANT: Comparison with NaN always false + l6 == 0; // NON_COMPLIANT: Comparison with NaN always false + l7 == 0; // NON_COMPLIANT: Comparison with NaN always false + l8 == 0; // NON_COMPLIANT: Comparison with NaN always false + + // Guards + float l9 = 0; + if (l9 != 0) { + (int)(l9 / l9); // COMPLIANT: l9 is not zero + } else { + (int)(l9 / l9); // NON_COMPLIANT: Casting NaN to integer + } + + float l10 = 0; + if (l10 == 0) { + (int)(l10 / l10); // NON_COMPLIANT: Casting NaN to integer + } else { + (int)(l10 / l10); // COMPLIANT: Guarded to not be NaN + } + + float l11 = 0; + l11 == 0 ? (int)(l11 / l11) : 0; // NON_COMPLIANT + l11 == 0 ? 0 : (int)(l11 / l11); // COMPLIANT + l11 != 0 ? (int)(l11 / l11) : 0; // COMPLIANT + l11 != 0 ? 0 : (int)(l11 / l11); // NON_COMPLIANT + + float l12 = 1.0 / 0; + if (isinf(l12)) { + (int)l12; // COMPLIANT: Casting Infinity to integer + } else { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } + + if (!isinf(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + if (isinf(l12) == 1) { + (int)l12; // COMPLIANT: Must be +Infinity + } else { + (int)l12; // COMPLIANT: May be -Infinity + } + + if (isfinite(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + if (isnormal(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + if (isnan(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + isinf(l12) ? (int)l12 : 0; // COMPLIANT: Check on wrong branch + isinf(l12) ? 0 : (int)l12; // COMPLIANT: Checked not infinite before use + isfinite(l12) ? (int)l12 : 0; // COMPLIANT: Checked finite before use + isfinite(l12) ? 0 : (int)l12; // COMPLIANT: Checked on wrong branch + isnan(l12) ? (int)l12 + : 0; // COMPLIANT: Checked NaN, therefore not infinite, before use + isnan(l12) ? 0 : (int)l12; // COMPLIANT: Check on wrong branch + + float l13 = 0.0 / 0; + if (isinf(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (isinf(l13) == 1) { + (int)l13; // COMPLIANT: Guarded not to be NaN (must be +Infinity) + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (isfinite(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (isnormal(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (isnan(l13)) { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } else { + (int)l13; // COMPLIANT: Guarded not to be NaN + } + + isinf(l13) ? (int)l13 + : 0; // COMPLIANT: Checked infinite, therefore not NaN, before use + isinf(l13) ? 0 : (int)l13; // NON_COMPLIANT: Check on wrong branch + isfinite(l13) ? (int)l13 : 0; // COMPLIANT: Checked finite before use + isfinite(l13) ? 0 : (int)l13; // NON_COMPLIANT: Checked on wrong branch + isnan(l13) ? (int)l13 : 0; // NON_COMPLIANT: Check on wrong branch + isnan(l13) ? 0 : (int)l13; // COMPLIANT: Checked not NaN before use + + (int)pow(2, p1); // COMPLIANT: likely to be Infinity + (int)pow(2, sin(p1)); // COMPLIANT: not likely to be Infinity + (int)(1 / sin(p1)); // COMPLIANT: possible infinity from zero in denominator + (int)(1 / log(p1)); // COMPLIANT: not possibly zero in denominator + (int)pow(p1, p1); // NON_COMPLIANT: NaN if p1 is zero + if (p1 != 0) { + (int)pow(p1, p1); // COMPLIANT: p1 is not zero + } + + (int)acos(p1); // NON_COMPLIANT: NaN if p1 is not within -1..1 + (int)acos(cos(p1)); // COMPLIANT: cos(p1) is within -1..1 +} + +void castToInt(float p) { (int)p; } + +void checkBeforeCastToInt(float p) { + if (isfinite(p)) { + castToInt(p); + } +} + +void castToIntToFloatToInt(float p) { + // This should be reported as a violation, but not downstream from here. + castToInt((int)p); +} + +void addOneThenCastToInt(float p) { castToInt(p + 1); } +void addInfThenCastToInt(float p) { castToInt(p + 1.0 / 0.0); } +void addNaNThenCastToInt(float p) { castToInt(p + 0.0 / 0.0); } + +void f2() { + castToInt(1.0 / 0.0); // COMPLIANT: Infinity flows to denominator in division + castToInt(0.0 / 0.0); // COMPLIANT: NaN flows to denominator in division + checkBeforeCastToInt(1.0 / 0.0); // COMPLIANT + checkBeforeCastToInt(0.0 / 0.0); // COMPLIANT + addOneThenCastToInt(1.0 / 0.0); // COMPLIANT + addOneThenCastToInt(0.0 / 0.0); // NON_COMPLIANT + castToIntToFloatToInt(1.0 / 0.0); // COMPLIANT + castToIntToFloatToInt(0.0 / 0.0); // NON_COMPLIANT + + // Check that during flow analysis, we only report the true root cause: + float rootInf = 1.0 / 0.0; + float rootNaN = 0.0 / 0.0; + float middleInf = rootInf + 1; + float middleNaN = rootNaN + 1; + castToInt(middleInf); // COMPLIANT + castToInt(middleNaN); // NON_COMPLIANT + addInfThenCastToInt(middleInf); // NON_COMPLIANT + addNaNThenCastToInt(middleNaN); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.expected b/c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.expected new file mode 100644 index 0000000000..7a6b7c33a5 --- /dev/null +++ b/c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.expected @@ -0,0 +1,5 @@ +| test.c:6:7:6:8 | x1 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.c:9:14:9:15 | x2 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.c:11:7:11:8 | x3 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.c:13:7:13:8 | x4 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.c:22:14:22:14 | x | A named bit-field with signed integral type should have at least 2 bits of storage. | diff --git a/c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql b/c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql new file mode 100644 index 0000000000..a82fa7905a --- /dev/null +++ b/c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.namedbitfieldswithsignedintegertype.NamedBitFieldsWithSignedIntegerType + +class TestFileQuery extends NamedBitFieldsWithSignedIntegerTypeSharedQuery, TestQuery { } diff --git a/c/common/test/rules/namedbitfieldswithsignedintegertype/test.c b/c/common/test/rules/namedbitfieldswithsignedintegertype/test.c new file mode 100644 index 0000000000..8fae6812fe --- /dev/null +++ b/c/common/test/rules/namedbitfieldswithsignedintegertype/test.c @@ -0,0 +1,28 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +struct SampleStruct { + int x1 : 1; // NON_COMPLIANT: very likely be signed, but if it's not, the + // query will automatically handle it since we use signed(), not + // isExplicitlySigned(). + signed int x2 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed char + x3 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed short + x4 : 1; // NON_COMPLIANT: single-bit named field with a signed type + unsigned int + x5 : 1; // COMPLIANT: single-bit named field but with an unsigned type + signed int x6 : 2; // COMPLIANT: named field with a signed type but declared + // to carry more than 1 bit + signed char : 1; // COMPLIANT: single-bit bit-field but unnamed +} sample_struct; + +struct S { + signed int x : 1; // NON-COMPLIANT + signed int y : 5; // COMPLIANT + signed int z : 7; // COMPLIANT + signed int : 0; // COMPLIANT + signed int : 1; // COMPLIANT + signed int : 2; // COMPLIANT +}; \ No newline at end of file diff --git a/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected new file mode 100644 index 0000000000..3adeecc903 --- /dev/null +++ b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected @@ -0,0 +1,3 @@ +| test.c:9:5:9:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:9:5:9:11 | case ...: | case ...: | test.c:6:3:17:3 | switch (...) ... | switch (...) ... | +| test.c:36:5:36:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:36:5:36:11 | case ...: | case ...: | test.c:23:3:43:3 | switch (...) ... | switch (...) ... | +| test.c:76:5:76:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:76:5:76:11 | case ...: | case ...: | test.c:73:3:79:3 | switch (...) ... | switch (...) ... | diff --git a/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql new file mode 100644 index 0000000000..3e0b1f7e8b --- /dev/null +++ b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch + +class TestFileQuery extends NestedLabelInSwitchSharedQuery, TestQuery { } diff --git a/c/common/test/rules/nestedlabelinswitch/test.c b/c/common/test/rules/nestedlabelinswitch/test.c new file mode 100644 index 0000000000..5c04578f0b --- /dev/null +++ b/c/common/test/rules/nestedlabelinswitch/test.c @@ -0,0 +1,80 @@ +void f(); + +void f1(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + case 2: // NON_COMPLIANT + j; + break; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f2(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + case 3: // COMPLIANT + if (i) { + j; + case 4: // NON_COMPLIANT + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f3(int p1) { + + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + break; + case 3: // COMPLIANT + if (i) { + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f4(int p1) { + switch (p1) { + int i; + if (i) { + case 1: // NON_COMPLIANT + f(); + } + } +} diff --git a/c/common/test/rules/nonconstantformat/NonConstantFormat.ql b/c/common/test/rules/nonconstantformat/NonConstantFormat.ql index 7a92b544e2..25750ae9e5 100644 --- a/c/common/test/rules/nonconstantformat/NonConstantFormat.ql +++ b/c/common/test/rules/nonconstantformat/NonConstantFormat.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.nonconstantformat.NonConstantFormat + +class TestFileQuery extends NonConstantFormatSharedQuery, TestQuery { } diff --git a/c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.expected b/c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.expected new file mode 100644 index 0000000000..26401472ac --- /dev/null +++ b/c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.expected @@ -0,0 +1,21 @@ +| test.c:37:18:37:24 | \u001aG | Invalid hexadecimal escape in string literal at '\\x1AG"'. | +| test.c:40:18:40:23 | \u00029 | Invalid octal escape in string literal at '\\029"'. | +| test.c:43:18:43:24 | \n7 | Invalid octal escape in string literal at '\\0127"'. | +| test.c:44:18:44:24 | \r7 | Invalid octal escape in string literal at '\\0157"'. | +| test.c:46:19:46:29 | \n\n9 | Invalid octal escape in string literal at '\\0129"'. | +| test.c:47:19:47:28 | \n\u00019 | Invalid octal escape in string literal at '\\019"'. | +| test.c:50:19:50:31 | \nAAA\u000f | Invalid octal escape in string literal at '\\012AAA\\017"'. | +| test.c:53:19:53:39 | Some Data \n\u000fA | Invalid octal escape in string literal at '\\017A"'. | +| test.c:54:19:55:21 | Some Data \n\u000fA5 | Invalid octal escape in string literal at '\\017A"\n "5"'. | +| test.c:56:19:58:25 | Some Data \n\u000fA\n1 | Invalid octal escape in string literal at '\\0121"'. | +| test.c:62:19:63:26 | \u0011G\u00012 | Invalid octal escape in string literal at '\\0012"'. | +| test.c:64:19:65:25 | \u0011GG\u0001 | Invalid hexadecimal escape in string literal at '\\x11G"\n "G\\001"'. | +| test.c:66:19:67:26 | \u0011GG\u00013 | Invalid hexadecimal escape in string literal at '\\x11G"\n "G\\0013"'. | +| test.c:66:19:67:26 | \u0011GG\u00013 | Invalid octal escape in string literal at '\\0013"'. | +| test.c:73:18:73:42 | Some Data \n\u000fA5 | Invalid octal escape in string literal at '\\017A" "5"'. | +| test.c:74:18:74:49 | Some Data \n\u000fA\n1 | Invalid octal escape in string literal at '\\0121"'. | +| test.c:76:18:76:32 | \u0011G\u00012 | Invalid octal escape in string literal at '\\0012"'. | +| test.c:77:18:77:32 | \u0011GG\u0001 | Invalid hexadecimal escape in string literal at '\\x11G" "G\\001"'. | +| test.c:78:18:78:33 | \u0011GG\u00013 | Invalid hexadecimal escape in string literal at '\\x11G" "G\\0013"'. | +| test.c:78:18:78:33 | \u0011GG\u00013 | Invalid octal escape in string literal at '\\0013"'. | +| test.c:81:11:81:16 | 10 | Invalid hexadecimal escape in string literal at '\\x0a''. | diff --git a/c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql b/c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql new file mode 100644 index 0000000000..c1aae3c31b --- /dev/null +++ b/c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonterminatedescapesequences.NonTerminatedEscapeSequences + +class TestFileQuery extends NonTerminatedEscapeSequencesSharedQuery, TestQuery { } diff --git a/c/common/test/rules/nonterminatedescapesequences/test.c b/c/common/test/rules/nonterminatedescapesequences/test.c new file mode 100644 index 0000000000..67c6e3d5a3 --- /dev/null +++ b/c/common/test/rules/nonterminatedescapesequences/test.c @@ -0,0 +1,81 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +struct SampleStruct { + int x1 : 1; // NON_COMPLIANT: very likely be signed, but if it's not, the + // query will automatically handle it since we use signed(), not + // isExplicitlySigned(). + signed int x2 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed char + x3 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed short + x4 : 1; // NON_COMPLIANT: single-bit named field with a signed type + unsigned int + x5 : 1; // COMPLIANT: single-bit named field but with an unsigned type + signed int x6 : 2; // COMPLIANT: named field with a signed type but declared + // to carry more than 1 bit + signed char : 1; // COMPLIANT: single-bit bit-field but unnamed +} sample_struct; + +struct S { + signed int x : 1; // NON-COMPLIANT + signed int y : 5; // COMPLIANT + signed int z : 7; // COMPLIANT + signed int : 0; // COMPLIANT + signed int : 1; // COMPLIANT + signed int : 2; // COMPLIANT +}; +const char *a1 = "\x11" + "G"; // COMPLIANT + +const char *a2 = "\x1" + "G"; // COMPLIANT + +const char *a3 = "\x1A"; // COMPLIANT + +const char *a4 = "\x1AG"; // NON_COMPLIANT + +const char *a5 = "\021"; // COMPLIANT +const char *a6 = "\029"; // NON_COMPLIANT +const char *a7 = "\0" + "0"; // COMPLIANT +const char *a8 = "\0127"; // NON_COMPLIANT +const char *a9 = "\0157"; // NON_COMPLIANT + +const char *a10 = "\012\0129"; // NON_COMPLIANT (1x) +const char *a11 = "\012\019"; // NON_COMPLIANT +const char *a12 = "\012\017"; // COMPLIANT + +const char *a13 = "\012AAA\017"; // NON_COMPLIANT (1x) + +const char *a14 = "Some Data \012\017"; // COMPLIANT +const char *a15 = "Some Data \012\017A"; // NON_COMPLIANT (1x) +const char *a16 = "Some Data \012\017A" + "5"; // NON_COMPLIANT (1x) +const char *a17 = "Some Data \012\017" + "A" + "\0121"; // NON_COMPLIANT (1x) + +const char *a18 = "\x11" + "G\001"; // COMPLIANT +const char *a19 = "\x11" + "G\0012"; // NON_COMPLIANT (1x) +const char *a20 = "\x11G" + "G\001"; // NON_COMPLIANT (1x) +const char *a21 = "\x11G" + "G\0013"; // NON_COMPLIANT (2x) + +// clang-format off +const char *b1 = "\x11" "G"; // COMPLIANT +const char *b2 = "\x1" "G"; // COMPLIANT +const char *b3 = "\0" "0"; // COMPLIANT +const char *b4 = "Some Data \012\017A" "5"; // NON_COMPLIANT (1x) +const char *b5 = "Some Data \012\017" "A" "\0121"; // NON_COMPLIANT (1x) +const char *b6 = "\x11" "G\001"; // COMPLIANT +const char *b7 = "\x11" "G\0012"; // NON_COMPLIANT (1x) +const char *b8 = "\x11G" "G\001"; // NON_COMPLIANT (1x) +const char *b9 = "\x11G" "G\0013"; // NON_COMPLIANT (2x) + +char c1 = '\023'; // COMPLIANT +char c2 = '\x0a'; // COMPLIANT diff --git a/c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.expected b/c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.expected new file mode 100644 index 0000000000..65e57e3575 --- /dev/null +++ b/c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.expected @@ -0,0 +1 @@ +| test.c:5:19:5:20 | c4 | Nonunique value of enum constant compared to $@ | test.c:5:23:5:24 | c5 | c5 | diff --git a/c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql b/c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql new file mode 100644 index 0000000000..97ba6f516e --- /dev/null +++ b/c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonuniqueenumerationconstant.NonUniqueEnumerationConstant + +class TestFileQuery extends NonUniqueEnumerationConstantSharedQuery, TestQuery { } diff --git a/c/common/test/rules/nonuniqueenumerationconstant/test.c b/c/common/test/rules/nonuniqueenumerationconstant/test.c new file mode 100644 index 0000000000..0712cb59e4 --- /dev/null +++ b/c/common/test/rules/nonuniqueenumerationconstant/test.c @@ -0,0 +1,6 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +enum e { c = 3 }; // COMPLIANT +enum e1 { c1 = 3, c2 }; // COMPLIANT +enum e3 { c3 = 3, c4, c5 = 4 }; // NON_COMPLIANT +enum e4 { c6 = 3, c7, c8, c9 = 6 }; // COMPLIANT \ No newline at end of file diff --git a/c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected b/c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected new file mode 100644 index 0000000000..4ebb26f6f7 --- /dev/null +++ b/c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected @@ -0,0 +1,3 @@ +| test.c:8:1:8:1 | return ... | Function test_return_f1 should return a value of type int but does not return a value here | +| test.c:18:27:18:28 | { ... } | Function test_return_f3 should return a value of type int but does not return a value here | +| test.c:27:1:27:1 | return ... | Function test_return_f5 should return a value of type int but does not return a value here | diff --git a/c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql b/c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql new file mode 100644 index 0000000000..bcf99b44e7 --- /dev/null +++ b/c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonvoidfunctiondoesnotreturn.NonVoidFunctionDoesNotReturn + +class TestFileQuery extends NonVoidFunctionDoesNotReturnSharedQuery, TestQuery { } diff --git a/c/common/test/rules/nonvoidfunctiondoesnotreturn/test.c b/c/common/test/rules/nonvoidfunctiondoesnotreturn/test.c new file mode 100644 index 0000000000..b90e2187ec --- /dev/null +++ b/c/common/test/rules/nonvoidfunctiondoesnotreturn/test.c @@ -0,0 +1,27 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +int test_return_f1(int i) { // NON_COMPLIANT + if (i > 100) { + return i; + } +} + +int test_return_f2(int i) { // COMPLIANT + if (i > 0) { + return i; + } else { + return -i; + } +} + +int test_return_f3(int i) {} // NON_COMPLIANT + +int test_return_f5(int i) { // NON_COMPLIANT + if (i > 0) { + return i; + } + if (i < 0) { + return -i; + } +} diff --git a/c/common/test/rules/notdistinctidentifier/NotDistinctIdentifier.ql b/c/common/test/rules/notdistinctidentifier/NotDistinctIdentifier.ql index 82ea80b775..3b7a8a5f9a 100644 --- a/c/common/test/rules/notdistinctidentifier/NotDistinctIdentifier.ql +++ b/c/common/test/rules/notdistinctidentifier/NotDistinctIdentifier.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.notdistinctidentifier.NotDistinctIdentifier + +class TestFileQuery extends NotDistinctIdentifierSharedQuery, TestQuery { } diff --git a/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.expected b/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.expected new file mode 100644 index 0000000000..a6c41a6d75 --- /dev/null +++ b/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.expected @@ -0,0 +1,24 @@ +problems +| test.c:8:8:8:10 | g_p | test.c:8:8:8:10 | g_p | test.c:8:8:8:10 | g_p | Free expression frees memory which was not dynamically allocated. | +| test.c:10:8:10:10 | g_p | test.c:10:8:10:10 | g_p | test.c:10:8:10:10 | g_p | Free expression frees memory which was not dynamically allocated. | +| test.c:12:8:12:10 | g_p | test.c:12:8:12:10 | g_p | test.c:12:8:12:10 | g_p | Free expression frees memory which was not dynamically allocated. | +| test.c:16:33:16:35 | g_p | test.c:16:33:16:35 | g_p | test.c:16:33:16:35 | g_p | Free expression frees memory which was not dynamically allocated. | +| test.c:18:36:18:38 | ptr | test.c:27:7:27:8 | & ... | test.c:18:36:18:38 | ptr | Free expression frees memory which was not dynamically allocated. | +| test.c:26:8:26:8 | p | test.c:25:13:25:14 | & ... | test.c:26:8:26:8 | p | Free expression frees memory which was not dynamically allocated. | +edges +| test.c:18:24:18:26 | ptr | test.c:18:36:18:38 | ptr | provenance | | +| test.c:25:13:25:14 | & ... | test.c:26:8:26:8 | p | provenance | | +| test.c:27:7:27:8 | & ... | test.c:28:15:28:15 | p | provenance | | +| test.c:28:15:28:15 | p | test.c:18:24:18:26 | ptr | provenance | | +nodes +| test.c:8:8:8:10 | g_p | semmle.label | g_p | +| test.c:10:8:10:10 | g_p | semmle.label | g_p | +| test.c:12:8:12:10 | g_p | semmle.label | g_p | +| test.c:16:33:16:35 | g_p | semmle.label | g_p | +| test.c:18:24:18:26 | ptr | semmle.label | ptr | +| test.c:18:36:18:38 | ptr | semmle.label | ptr | +| test.c:25:13:25:14 | & ... | semmle.label | & ... | +| test.c:26:8:26:8 | p | semmle.label | p | +| test.c:27:7:27:8 | & ... | semmle.label | & ... | +| test.c:28:15:28:15 | p | semmle.label | p | +subpaths diff --git a/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.ql b/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.ql new file mode 100644 index 0000000000..f7d315554e --- /dev/null +++ b/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.onlyfreememoryallocateddynamicallyshared.OnlyFreeMemoryAllocatedDynamicallyShared + +class TestFileQuery extends OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/test.c b/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/test.c new file mode 100644 index 0000000000..bfb8899f71 --- /dev/null +++ b/c/common/test/rules/onlyfreememoryallocateddynamicallyshared/test.c @@ -0,0 +1,52 @@ +#include +#include + +int g_i = 0; +void *g_p = &g_i; + +void test_global(void) { + free(g_p); // NON_COMPLIANT + g_p = &g_i; + free(g_p); // NON_COMPLIANT + g_p = malloc(10); + free(g_p); // COMPLIANT[FALSE_POSITIVE] - but could be written to in different + // scope +} + +void test_global_b(void) { free(g_p); } // NON_COMPLIANT + +void free_nested(void *ptr) { free(ptr); } // NON_COMPLIANT - some paths + +void get_allocated_memory(void **p) { *p = malloc(10); } + +void test_local(void) { + int i; + int j; + void *p = &i; + free(p); // NON_COMPLIANT + p = &j; + free_nested(p); // NON_COMPLIANT - reported on line 18 + p = malloc(10); + free(p); // COMPLIANT + p = malloc(10); + free_nested(p); // COMPLIANT + get_allocated_memory(&p); + free(p); // COMPLIANT +} + +struct S { + int i; + void *p; +}; + +void test_local_field_nested(struct S *s) { free(s->p); } // COMPLIANT + +void test_local_field(void) { + struct S s; + s.p = &s.i; + free(s.p); // NON_COMPLIANT[FALSE_NEGATIVE] + s.p = malloc(10); + free(s.p); // COMPLIANT + s.p = malloc(10); + test_local_field_nested(&s); +} \ No newline at end of file diff --git a/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql b/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql index 37ff4945f4..d66babdb6d 100644 --- a/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql +++ b/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument + +class TestFileQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery, TestQuery { } diff --git a/c/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql b/c/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql index 1b27d1e0ee..c7652ab4ae 100644 --- a/c/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql +++ b/c/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preprocessorincludesforbiddenheadernames.PreprocessorIncludesForbiddenHeaderNames + +class TestFileQuery extends PreprocessorIncludesForbiddenHeaderNamesSharedQuery, TestQuery { } diff --git a/c/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql b/c/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql index 343b168637..43701dbbf9 100644 --- a/c/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql +++ b/c/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preprocessorincludespreceded.PreprocessorIncludesPreceded + +class TestFileQuery extends PreprocessorIncludesPrecededSharedQuery, TestQuery { } diff --git a/c/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql b/c/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql index cef4c700ab..009c7f9e26 100644 --- a/c/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql +++ b/c/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preservesafetywhenusingconditionvariables.PreserveSafetyWhenUsingConditionVariables + +class TestFileQuery extends PreserveSafetyWhenUsingConditionVariablesSharedQuery, TestQuery { } diff --git a/c/common/test/rules/preservesafetywhenusingconditionvariables/test.c b/c/common/test/rules/preservesafetywhenusingconditionvariables/test.c index 0134a1fd6d..2c6028f0ae 100644 --- a/c/common/test/rules/preservesafetywhenusingconditionvariables/test.c +++ b/c/common/test/rules/preservesafetywhenusingconditionvariables/test.c @@ -29,7 +29,6 @@ void f1() { } mtx_destroy(&mxl); - return 0; } void f2() { @@ -48,5 +47,4 @@ void f2() { } mtx_destroy(&mxl); - return 0; } diff --git a/c/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql b/c/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql index 6412db389a..4ca46f15ea 100644 --- a/c/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql +++ b/c/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preventdeadlockbylockinginpredefinedorder.PreventDeadlockByLockingInPredefinedOrder + +class TestFileQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery, TestQuery { } diff --git a/c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.expected b/c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.expected new file mode 100644 index 0000000000..a5b472ff9d --- /dev/null +++ b/c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.expected @@ -0,0 +1,4 @@ +| test.c:33:11:33:12 | l1 | Local variable $@ is read here and may not be initialized on all paths. | test.c:32:7:32:8 | l1 | l1 | +| test.c:35:15:35:16 | l2 | Local variable $@ is read here and may not be initialized on all paths. | test.c:34:8:34:9 | l2 | l2 | +| test.c:37:20:37:21 | l3 | Local variable $@ is read here and may not be initialized on all paths. | test.c:36:13:36:14 | l3 | l3 | +| test.c:84:17:84:24 | arrayPtr | Local variable $@ is read here and may not be initialized on all paths. | test.c:77:8:77:15 | arrayPtr | arrayPtr | diff --git a/c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql b/c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql new file mode 100644 index 0000000000..9150d4459d --- /dev/null +++ b/c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.readofuninitializedmemory.ReadOfUninitializedMemory + +class TestFileQuery extends ReadOfUninitializedMemorySharedQuery, TestQuery { } diff --git a/c/common/test/rules/readofuninitializedmemory/test.c b/c/common/test/rules/readofuninitializedmemory/test.c new file mode 100644 index 0000000000..e76c5a22b3 --- /dev/null +++ b/c/common/test/rules/readofuninitializedmemory/test.c @@ -0,0 +1,99 @@ +#include + +struct A { + int m1; +}; + +void use_int(int a); +void use_struct_A(struct A a); +void use_int_ptr(int *a); +void use_struct_A_ptr(struct A *a); + +void init_by_pointer(int *pointer_param); + +void test_basic_init() { + int l1 = 0; + use_int(l1); // COMPLIANT + struct A l2 = {}; + use_struct_A(l2); // COMPLIANT + int l3; + init_by_pointer(&l3); + use_int(l3); // COMPLIANT + struct A l4; + l4.m1 = 1; // COMPLIANT + use_struct_A(l4); // COMPLIANT + int l5[10] = {1, 0}; + use_int_ptr(l5); // COMPLIANT + struct A l6; + use_struct_A(l6); // COMPLIANT[FALSE_NEGATIVE] +} + +void test_basic_uninit() { + int l1; + use_int(l1); // NON_COMPLIANT + int *l2; + use_int_ptr(l2); // NON_COMPLIANT + struct A *l3; + use_struct_A_ptr(l3); // NON_COMPLIANT + struct A l4; + use_int(l4.m1); // NON_COMPLIANT[FALSE_NEGATIVE] - field is not initialized + int l5[10]; + use_int( + l5[0]); // NON_COMPLIANT[FALSE_NEGATIVE] - array entry is not initialized +} + +int run1(); + +void test_conditional(int x) { + + int l1; // l1 is defined and used only when x is true + if (x) { + l1 = 0; + } + if (x) { + use_int(l1); // COMPLIANT + } + + int l2; // l2 is defined and used only when x is false + if (!x) { + l2 = 0; + } + if (!x) { + use_int(l2); // COMPLIANT + } + + int l3 = 0; + int l4; + if (x) { + l3 = 1; + l4 = 1; + } + + if (l3) { // l3 true indicates l4 is initialized + use_int(l4); // COMPLIANT + } + + int numElements = 0; + int *arrayPtr; + if (x) { + numElements = 5; + arrayPtr = malloc(sizeof(int) * numElements); + } + + if (numElements > 0) { // numElements > 0 indicates arrayPtr is initialized + use_int_ptr(arrayPtr); // COMPLIANT[FALSE_POSITIVE] + } +} + +void test_non_default_init() { + static int sl; + use_int(sl); // COMPLIANT - static int type variables are zero initialized + static int *slp; + use_int_ptr( + slp); // COMPLIANT - static pointer type variables are zero initialized + static struct A ss; + use_struct_A( + ss); // COMPLIANT - static struct type variables are zero initialized + _Atomic int x; + use_int(x); // COMPLIANT - atomics are special, covered by other rules +} \ No newline at end of file diff --git a/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected b/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected new file mode 100644 index 0000000000..c0a8359320 --- /dev/null +++ b/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected @@ -0,0 +1,3 @@ +| test.c:9:7:9:12 | ... = ... | Use of an assignment operator's result. | +| test.c:13:11:13:16 | ... = ... | Use of an assignment operator's result. | +| test.c:15:8:15:13 | ... = ... | Use of an assignment operator's result. | diff --git a/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql b/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql new file mode 100644 index 0000000000..286e4424a4 --- /dev/null +++ b/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.resultofanassignmentoperatorshouldnotbeused.ResultOfAnAssignmentOperatorShouldNotBeUsed + +class TestFileQuery extends ResultOfAnAssignmentOperatorShouldNotBeUsedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/test.c b/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/test.c new file mode 100644 index 0000000000..db0a45384e --- /dev/null +++ b/c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/test.c @@ -0,0 +1,16 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void test() { + int l1, l2; + int l3[1]; + + l1 = l2; // COMPLIANT + + if (l1 = 1) // NON_COMPLIANT + { + } + + l1 = l3[l2 = 0]; // NON_COMPLIANT + + l1 = l2 = 0; // NON_COMPLIANT +} diff --git a/c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected new file mode 100644 index 0000000000..d3ef31011c --- /dev/null +++ b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected @@ -0,0 +1,20 @@ +| test.c:39:1:39:39 | // int myFunction() { return myValue; } | This comment appears to contain commented-out code. | +| test.c:41:1:41:45 | // int myFunction() const { return myValue; } | This comment appears to contain commented-out code. | +| test.c:43:1:43:54 | // int myFunction() const noexcept { return myValue; } | This comment appears to contain commented-out code. | +| test.c:45:1:45:18 | // #define MYMACRO | This comment appears to contain commented-out code. | +| test.c:47:1:47:23 | // #include "include.h" | This comment appears to contain commented-out code. | +| test.c:49:1:53:2 | /*\n#ifdef\nvoid myFunction();\n#endif\n*/ | This comment appears to contain commented-out code. | +| test.c:61:1:61:24 | // #if(defined(MYMACRO)) | This comment appears to contain commented-out code. | +| test.c:65:1:65:15 | // #pragma once | This comment appears to contain commented-out code. | +| test.c:67:1:67:17 | // # pragma once | This comment appears to contain commented-out code. | +| test.c:69:1:69:19 | /*#error"myerror"*/ | This comment appears to contain commented-out code. | +| test.c:93:1:97:2 | /*\n#ifdef MYMACRO\n // ...\n#endif // #ifdef MYMACRO\n*/ | This comment appears to contain commented-out code. | +| test.c:108:21:108:43 | // #include "config2.h" | This comment appears to contain commented-out code. | +| test.c:114:16:114:35 | /* #ifdef MYMACRO */ | This comment appears to contain commented-out code. | +| test.c:118:1:118:24 | // commented_out_code(); | This comment appears to contain commented-out code. | +| test.c:121:2:121:25 | // commented_out_code(); | This comment appears to contain commented-out code. | +| test.c:124:1:124:22 | // commented out code; | This comment appears to contain commented-out code. | +| test.c:126:1:129:8 | // some; | This comment appears to contain commented-out code. | +| test.c:131:1:135:8 | // also; | This comment appears to contain commented-out code. | +| test.c:143:1:148:2 | /*\n some;\n commented;\n out;\n code;\n*/ | This comment appears to contain commented-out code. | +| test.c:150:1:156:2 | /*\n also;\n this\n is;\n commented-out\n code;\n*/ | This comment appears to contain commented-out code. | diff --git a/c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql new file mode 100644 index 0000000000..aacadf0253 --- /dev/null +++ b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.sectionsofcodeshallnotbecommentedout.SectionsOfCodeShallNotBeCommentedOut + +class TestFileQuery extends SectionsOfCodeShallNotBeCommentedOutSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A1-1-1/test.cpp b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/config.h similarity index 100% rename from cpp/autosar/test/rules/A1-1-1/test.cpp rename to c/common/test/rules/sectionsofcodeshallnotbecommentedout/config.h diff --git a/c/common/test/rules/sectionsofcodeshallnotbecommentedout/test.c b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/test.c new file mode 100644 index 0000000000..cb4d47aa79 --- /dev/null +++ b/c/common/test/rules/sectionsofcodeshallnotbecommentedout/test.c @@ -0,0 +1,172 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +/* + * This sentence contains a semicolon; + * however, this doesn't make it code. + */ + +// This sentence contains a semicolon; +// however, this doesn't make it code. + +/* Mention a ';' */ + +/* Mention a '{' */ + +/* JSON example: {"foo":"bar"} */ + +/* JSON example in backticks: `{"foo":"bar"}` */ + +/* JSON example in quotes: '{"foo":"bar"}' */ + +/* + * Code example: `return 0;`. + */ + +// Code example: +// +// return 0; + +// Code example: +// +// ``` +// return 0; +// ``` + +// { 1, 2, 3, 4 } + +// Example: { 1, 2, 3, 4 } + +// int myFunction() { return myValue; } + +// int myFunction() const { return myValue; } + +// int myFunction() const noexcept { return myValue; } + +// #define MYMACRO + +// #include "include.h" + +/* +#ifdef +void myFunction(); +#endif +*/ + +// define some constants + +// don't #include anything here + +// #hashtag + +// #if(defined(MYMACRO)) + +// #iffy + +// #pragma once + +// # pragma once + +/*#error"myerror"*/ + +#ifdef MYMACRO + +// ... + +#endif // #ifdef MYMACRO + +#if !defined(MYMACRO) + +// ... + +#else // #if !defined(MYMACRO) + +// ... + +#endif // #else #if !defined(MYMACRO) + +#ifdef MYMACRO + +// ... + +#endif // #ifdef MYMACRO (comment) + +/* +#ifdef MYMACRO + // ... +#endif // #ifdef MYMACRO +*/ + +#ifdef MYMACRO1 +#ifdef MYMACRO2 + +// ... + +// comment at end of block +#endif // #ifdef MYMACRO2 +#endif // #ifdef MYMACRO1 + +#include "config.h" // #include "config2.h" + +#ifdef MYMACRO + +// ... + +#error "error" /* #ifdef MYMACRO */ + +#endif /* #ifdef MYMACRO */ + +// commented_out_code(); + +#if 0 + // commented_out_code(); +#endif + +// commented out code; + +// some; +// commented; +// out; +// code; + +// also; +// this +// is; +// commented-out +// code; + +// this +// is; +// not +// commented-out; +// code + +/* + some; + commented; + out; + code; +*/ + +/* + also; + this + is; + commented-out + code; +*/ + +/* + this + is; + not + commented-out; + code +*/ + +/* + * documentation; + * not; + * commented; + * out; + * code; + */ diff --git a/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected new file mode 100644 index 0000000000..14cc8431da --- /dev/null +++ b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected @@ -0,0 +1 @@ +| test.c:5:3:24:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.c:5:3:24:3 | switch (...) ... | Switch | diff --git a/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql new file mode 100644 index 0000000000..1b323a652d --- /dev/null +++ b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition + +class TestFileQuery extends SwitchCasePositionConditionSharedQuery, TestQuery { } diff --git a/c/common/test/rules/switchcasepositioncondition/test.c b/c/common/test/rules/switchcasepositioncondition/test.c new file mode 100644 index 0000000000..bbdbddfb80 --- /dev/null +++ b/c/common/test/rules/switchcasepositioncondition/test.c @@ -0,0 +1,42 @@ +void f1(int p1); + +void f2(int p1) { + + switch (p1) { + start:; // NON_COMPLIANT + case 1: + if (p1) { + ; + }; + break; + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} +void f3(int p1) { + + switch (p1) { // COMPLIANT + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} diff --git a/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected new file mode 100644 index 0000000000..6843b78cd9 --- /dev/null +++ b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected @@ -0,0 +1,3 @@ +| test.c:4:3:11:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.c:4:3:11:3 | switch (...) ... | Switch | test.c:5:3:5:9 | case ...: | case | +| test.c:14:3:21:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.c:14:3:21:3 | switch (...) ... | Switch | test.c:15:3:15:10 | case ...: | case | +| test.c:26:3:31:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.c:26:3:31:3 | switch (...) ... | Switch | test.c:27:3:27:9 | case ...: | case | diff --git a/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql new file mode 100644 index 0000000000..75ce3cb1ec --- /dev/null +++ b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed + +class TestFileQuery extends SwitchNotWellFormedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/switchnotwellformed/test.c b/c/common/test/rules/switchnotwellformed/test.c new file mode 100644 index 0000000000..1082ee405c --- /dev/null +++ b/c/common/test/rules/switchnotwellformed/test.c @@ -0,0 +1,41 @@ + +void f1(); +void f2(int p1) { + switch (p1) { + case 1: + f1(); + int y = p1; // NON_COMPLIANT - `DeclStmt` whose parent + // statement is the switch body + f1(); + break; + } +} +void f3(int p1) { + switch (p1) { + case 10: + f1(); + goto L1; // NON_COMPLIANT - `JumpStmt` whose parent statement is the// + // switch// body + case 2: + break; + } +L1:; +} + +void f4(int p1) { + switch (p1) { + case 1: + L1:; // NON_COMPLIANT - `LabelStmt` whose parent statement is the + // switch body + break; + } +} + +void f5(int p1) { + switch (p1) { + case 1: // COMPLIANT + default: + p1 = 0; + break; + } +} diff --git a/c/common/test/rules/typeomitted/TypeOmitted.ql b/c/common/test/rules/typeomitted/TypeOmitted.ql index d0853e90a4..a9cd81118c 100644 --- a/c/common/test/rules/typeomitted/TypeOmitted.ql +++ b/c/common/test/rules/typeomitted/TypeOmitted.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.typeomitted.TypeOmitted + +class TestFileQuery extends TypeOmittedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.expected b/c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.expected new file mode 100644 index 0000000000..9059b0d815 --- /dev/null +++ b/c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.expected @@ -0,0 +1,30 @@ +| test.c:6:3:6:6 | call to acos | Domain error in call to 'acos': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | +| test.c:10:3:10:6 | call to acos | Domain error in call to 'acos': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | +| test.c:11:3:11:6 | call to asin | Domain error in call to 'asin': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | +| test.c:15:3:15:6 | call to asin | Domain error in call to 'asin': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | +| test.c:16:3:16:7 | call to atanh | Domain error in call to 'atanh': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | +| test.c:18:3:18:7 | call to atanh | Domain error in call to 'atanh': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | +| test.c:19:3:19:7 | call to atan2 | Domain error in call to 'atan2': both arguments are equal to zero. | +| test.c:23:3:23:5 | call to pow | Domain error in call to 'pow': both arguments are equal to zero. | +| test.c:27:3:27:5 | call to pow | Domain error in call to 'pow': both arguments are less than zero. | +| test.c:32:3:32:7 | call to acosh | Domain error in call to 'acosh': argument is less than 1. | +| test.c:33:3:33:7 | call to ilogb | Domain error in call to 'ilogb': argument is equal to zero. | +| test.c:36:3:36:5 | call to log | Domain error in call to 'log': argument is negative. | +| test.c:38:3:38:7 | call to log10 | Domain error in call to 'log10': argument is negative. | +| test.c:40:3:40:6 | call to log2 | Domain error in call to 'log2': argument is negative. | +| test.c:42:3:42:6 | call to sqrt | Domain error in call to 'sqrt': argument is negative. | +| test.c:45:3:45:7 | call to log1p | Domain error in call to 'log1p': argument is less than 1. | +| test.c:47:3:47:6 | call to logb | Domain error in call to 'logb': argument is equal to zero. | +| test.c:50:3:50:8 | call to tgamma | Domain error in call to 'tgamma': argument is equal to zero. | +| test.c:56:3:56:5 | call to abs | Range error in call to 'abs': argument is most negative number. | +| test.c:57:3:57:6 | call to fmod | Domain error in call to 'fmod': y is 0. | +| test.c:59:3:59:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. | +| test.c:60:3:60:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. | +| test.c:64:3:64:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. | +| test.c:65:3:65:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. | +| test.c:66:3:66:5 | call to log | Pole error in call to 'log': argument is equal to zero. | +| test.c:67:3:67:7 | call to log10 | Pole error in call to 'log10': argument is equal to zero. | +| test.c:68:3:68:6 | call to log2 | Pole error in call to 'log2': argument is equal to zero. | +| test.c:69:3:69:7 | call to log1p | Pole error in call to 'log1p': argument is equal to negative one. | +| test.c:71:3:71:5 | call to pow | Pole error in call to 'pow': base is zero and exp is negative. | +| test.c:72:3:72:8 | call to lgamma | Pole error in call to 'lgamma': argument is equal to zero. | diff --git a/c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql b/c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql new file mode 100644 index 0000000000..11720fb8da --- /dev/null +++ b/c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.uncheckedrangedomainpoleerrors.UncheckedRangeDomainPoleErrors + +class TestFileQuery extends UncheckedRangeDomainPoleErrorsSharedQuery, TestQuery { } diff --git a/c/common/test/rules/uncheckedrangedomainpoleerrors/test.c b/c/common/test/rules/uncheckedrangedomainpoleerrors/test.c new file mode 100644 index 0000000000..ab34302f21 --- /dev/null +++ b/c/common/test/rules/uncheckedrangedomainpoleerrors/test.c @@ -0,0 +1,74 @@ +#include +#include +#include + +void test_domain_errors() { + acos(-1.1f); // NON_COMPLIANT + acos(-1.0f); // COMPLIANT + acos(0.0f); // COMPLIANT + acos(1.0f); // COMPLIANT + acos(1.1f); // NON_COMPLIANT + asin(-1.1f); // NON_COMPLIANT + asin(-1.0f); // COMPLIANT + asin(0.0f); // COMPLIANT + asin(1.0f); // COMPLIANT + asin(1.1f); // NON_COMPLIANT + atanh(-1.1f); // NON_COMPLIANT + atanh(0.0f); // COMPLIANT + atanh(1.1f); // NON_COMPLIANT + atan2(0.0f, 0.0f); // NON_COMPLIANT + atan2(1.0f, 0.0f); // COMPLIANT + atan2(0.0f, 1.0f); // COMPLIANT + atan2(1.0f, 1.0f); // COMPLIANT + pow(0.0f, 0.0f); // NON_COMPLIANT + pow(1.0f, 0.0f); // COMPLIANT + pow(0.0f, 1.0f); // COMPLIANT + pow(1.0f, 1.0f); // COMPLIANT + pow(-1.0f, -1.0f); // NON_COMPLIANT + pow(-1.0f, 0.0f); // COMPLIANT + pow(1.0f, -1.0f); // COMPLIANT + pow(-1.0f, 1.0f); // COMPLIANT + acosh(1.0f); // COMPLIANT + acosh(0.9f); // NON_COMPLIANT + ilogb(0.0f); // NON_COMPLIANT + ilogb(1.0f); // COMPLIANT + ilogb(-1.0f); // COMPLIANT + log(-1.0f); // NON_COMPLIANT + log(1.0f); // COMPLIANT + log10(-1.0f); // NON_COMPLIANT + log10(1.0f); // COMPLIANT + log2(-1.0f); // NON_COMPLIANT + log2(1.0f); // COMPLIANT + sqrt(-1.0f); // NON_COMPLIANT + sqrt(0.0f); // COMPLIANT + sqrt(1.0f); // COMPLIANT + log1p(-2.0f); // NON_COMPLIANT + log1p(0.0f); // COMPLIANT + logb(0.0f); // NON_COMPLIANT + logb(1.0f); // COMPLIANT + logb(-1.0f); // COMPLIANT + tgamma(0.0f); // NON_COMPLIANT + tgamma(1.0f); // COMPLIANT + tgamma(-1.1f); // COMPLIANT +} + +void fn_in_193_missing_domain_or_range_cases() { + abs(INT_MIN); // NON_COMPLIANT + fmod(1.0f, 0.0f); // NON_COMPLIANT + int *exp; + frexp(NAN, exp); // NON_COMPLIANT + frexp(INFINITY, exp); // NON_COMPLIANT +} + +void test_pole_errors() { + atanh(-1.0f); // NON_COMPLIANT + atanh(1.0f); // NON_COMPLIANT + log(0.0f); // NON_COMPLIANT + log10(0.0f); // NON_COMPLIANT + log2(0.0f); // NON_COMPLIANT + log1p(-1.0f); // NON_COMPLIANT + // logb(x) already covered in domain cases + pow(0.0f, -1.0f); // NON_COMPLIANT + lgamma(0.0f); // NON_COMPLIANT + lgamma(-1); // NON_COMPLIANT[FALSE_NEGATIVE] +} \ No newline at end of file diff --git a/c/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql b/c/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql index afede1b8ef..316565cab7 100644 --- a/c/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql +++ b/c/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.undefinedmacroidentifiers.UndefinedMacroIdentifiers + +class TestFileQuery extends UndefinedMacroIdentifiersSharedQuery, TestQuery { } diff --git a/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.expected b/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.expected new file mode 100644 index 0000000000..e9d863a111 --- /dev/null +++ b/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.expected @@ -0,0 +1,4 @@ +| test.c:4:12:4:13 | g2 | The declaration g2 should be moved from the global namespace scope$@ into the $@ too minimize its visibility. | file://:0:0:0:0 | (global namespace) | scope | test.c:59:11:59:25 | { ... } | scope | +| test.c:7:7:7:7 | j | The declaration j should be moved from $@ into the $@ too minimize its visibility. | test.c:6:11:13:1 | { ... } | scope | test.c:8:13:12:3 | { ... } | scope | +| test.c:62:7:62:7 | i | The declaration i should be moved from $@ into the $@ too minimize its visibility. | test.c:61:11:71:1 | { ... } | scope | test.c:64:13:70:3 | { ... } | scope | +| test.c:73:8:73:9 | S1 | The declaration S1 should be moved from the global namespace scope$@ into the $@ too minimize its visibility. | file://:0:0:0:0 | (global namespace) | scope | test.c:77:12:77:28 | { ... } | scope | diff --git a/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql b/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql new file mode 100644 index 0000000000..3baad901da --- /dev/null +++ b/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unnecessaryexposedidentifierdeclarationshared.UnnecessaryExposedIdentifierDeclarationShared + +class TestFileQuery extends UnnecessaryExposedIdentifierDeclarationSharedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/test.c b/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/test.c new file mode 100644 index 0000000000..0ef89369fc --- /dev/null +++ b/c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/test.c @@ -0,0 +1,110 @@ +#include +extern void f1(int i); +extern int g1; // COMPLIANT +extern int g2; // NON_COMPLIANT; single use of a global variable +bool f2() { return g1 == 1; } +void f3() { + int j = g1; // NON_COMPLIANT + if (f2()) { + int k; // COMPLIANT + f1(j); + f1(k); + } +} + +void f4() { + int j = g1; // COMPLIANT; value of g1 changed between + // definition and use + g1 = 1; + if (f2()) { + f1(j); + } +} + +void f5() { + int j = g1; // COMPLIANT; shouldn't be moved inside loop + while (true) { + int i = g1++; + while (f2()) { + i += j; + } + + if (i % 2) + break; + } +} + +void f6() { + int j = g1; // COMPLIANT; can't moved into smaller scope +#ifdef FOO + if (g1) { + g1 = j + 1; + } +#else + if (g1) { + g1 = j + 2; + } +#endif +} + +void f7() { + int j = g1; // COMPLIANT; potentially stores previous value of + // g1 so moving this would be incorrect. + f1(1); // f1 may change the value of g1 + if (f2()) { + f1(j); + } +} + +void f8() { int i = g2; } + +void f9() { + int i; // NON_COMPLIANT + + if (f2()) { + if (f2()) { + i++; + } else { + i--; + } + } +} + +struct S1 { // NON_COMPLIANT + int i; +}; + +void f10() { struct S1 l1; } + +void f11() { + struct S2 { // COMPLIANT + int i; + } l1; +} + +struct S3 { + int i; +}; + +struct S4 { // NON_COMPLIANT; single use in function f13 + int i; +}; + +void f15() { + int i; // COMPLIANT + + if (i == 0) { + i++; + } +} + +void f17() { + int i; // COMPLIANT + int *ptr; + { + // Moving the declaration of i into the reduced scope will result in a + // dangling pointer + ptr = &i; + } + *ptr = 1; +} \ No newline at end of file diff --git a/c/common/test/rules/unreachablecode/UnreachableCode.expected b/c/common/test/rules/unreachablecode/UnreachableCode.expected new file mode 100644 index 0000000000..cf693dcf5c --- /dev/null +++ b/c/common/test/rules/unreachablecode/UnreachableCode.expected @@ -0,0 +1,2 @@ +| test.c:17:3:17:12 | declaration | This statement in function $@ is unreachable. | test.c:15:5:15:21 | test_after_return | test_after_return | +| test.c:21:10:22:12 | { ... } | This statement in function $@ is unreachable. | test.c:20:5:20:27 | test_constant_condition | test_constant_condition | diff --git a/c/common/test/rules/unreachablecode/UnreachableCode.ql b/c/common/test/rules/unreachablecode/UnreachableCode.ql new file mode 100644 index 0000000000..c394bfba3e --- /dev/null +++ b/c/common/test/rules/unreachablecode/UnreachableCode.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unreachablecode.UnreachableCode + +class TestFileQuery extends UnreachableCodeSharedQuery, TestQuery { } diff --git a/c/common/test/rules/unreachablecode/test.c b/c/common/test/rules/unreachablecode/test.c new file mode 100644 index 0000000000..7bb37844cb --- /dev/null +++ b/c/common/test/rules/unreachablecode/test.c @@ -0,0 +1,26 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +void test_switch(int p1) { + int l1 = 0; + switch (p1) { + l1 = p1; // NON_COMPLIANT[FALSE_NEGATIVE] + case 1: + break; + default: + break; + } +} + +int test_after_return() { + return 0; + int l1 = 0; // NON_COMPLIANT - function has returned by this point +} + +int test_constant_condition() { + if (0) { // NON_COMPLIANT + return 1; + } else { // COMPLIANT + return 2; + } +} \ No newline at end of file diff --git a/c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.expected b/c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.expected new file mode 100644 index 0000000000..33ec8d6995 --- /dev/null +++ b/c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.expected @@ -0,0 +1,4 @@ +| test.c:7:3:7:9 | ... + ... | Operation + of type unsigned int may wrap. | +| test.c:8:3:8:10 | ... += ... | Operation += of type unsigned int may wrap. | +| test.c:61:3:61:9 | ... - ... | Operation - of type unsigned int may wrap. | +| test.c:62:3:62:10 | ... -= ... | Operation -= of type unsigned int may wrap. | diff --git a/c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql b/c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql new file mode 100644 index 0000000000..b88e7637c1 --- /dev/null +++ b/c/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unsignedoperationwithconstantoperandswraps.UnsignedOperationWithConstantOperandsWraps + +class TestFileQuery extends UnsignedOperationWithConstantOperandsWrapsSharedQuery, TestQuery { } diff --git a/c/common/test/rules/unsignedoperationwithconstantoperandswraps/test.c b/c/common/test/rules/unsignedoperationwithconstantoperandswraps/test.c new file mode 100644 index 0000000000..214b18a44f --- /dev/null +++ b/c/common/test/rules/unsignedoperationwithconstantoperandswraps/test.c @@ -0,0 +1,83 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +#include + +void test_add_simple(unsigned int i1, unsigned int i2) { + i1 + i2; // NON_COMPLIANT - not bounds checked + i1 += i2; // NON_COMPLIANT - not bounds checked +} + +void test_add_precheck(unsigned int i1, unsigned int i2) { + if (UINT_MAX - i1 < i2) { + // handle error + } else { + i1 + i2; // COMPLIANT - bounds checked + i1 += i2; // COMPLIANT - bounds checked + } +} + +void test_add_precheck_2(unsigned int i1, unsigned int i2) { + if (i1 + i2 < i1) { + // handle error + } else { + i1 + i2; // COMPLIANT - bounds checked + i1 += i2; // COMPLIANT - bounds checked + } +} + +void test_add_postcheck(unsigned int i1, unsigned int i2) { + unsigned int i3 = i1 + i2; // COMPLIANT - checked for overflow afterwards + if (i3 < i1) { + // handle error + } + i1 += i2; // COMPLIANT - checked for overflow afterwards + if (i1 < i2) { + // handle error + } +} + +void test_ex2(unsigned int i1, unsigned int i2) { + unsigned int ci1 = 2; + unsigned int ci2 = 3; + ci1 + ci2; // COMPLIANT, compile time constants + i1 + 0; // COMPLIANT + i1 += 0; // COMPLIANT + i1 - 0; // COMPLIANT + i1 -= 0; // COMPLIANT + UINT_MAX - i1; // COMPLIANT - cannot be smaller than 0 + i1 * 1; // COMPLIANT + i1 *= 1; // COMPLIANT + if (0 <= i1 && i1 < 32) { + UINT_MAX >> i1; // COMPLIANT + } +} + +void test_ex3(unsigned int i1, unsigned int i2) { + i1 << i2; // COMPLIANT - by EX3 +} + +void test_sub_simple(unsigned int i1, unsigned int i2) { + i1 - i2; // NON_COMPLIANT - not bounds checked + i1 -= i2; // NON_COMPLIANT - not bounds checked +} + +void test_sub_precheck(unsigned int i1, unsigned int i2) { + if (i1 < i2) { + // handle error + } else { + i1 - i2; // COMPLIANT - bounds checked + i1 -= i2; // COMPLIANT - bounds checked + } +} + +void test_sub_postcheck(unsigned int i1, unsigned int i2) { + unsigned int i3 = i1 - i2; // COMPLIANT - checked for wrap afterwards + if (i3 > i1) { + // handle error + } + i1 -= i2; // COMPLIANT - checked for wrap afterwards + if (i1 > i2) { + // handle error + } +} \ No newline at end of file diff --git a/c/common/test/rules/unusedparameter/UnusedParameter.expected b/c/common/test/rules/unusedparameter/UnusedParameter.expected new file mode 100644 index 0000000000..09519575c9 --- /dev/null +++ b/c/common/test/rules/unusedparameter/UnusedParameter.expected @@ -0,0 +1 @@ +| test.c:6:22:6:22 | x | Unused parameter 'x' for function $@. | test.c:6:6:6:16 | test_unused | test_unused | diff --git a/c/common/test/rules/unusedparameter/UnusedParameter.ql b/c/common/test/rules/unusedparameter/UnusedParameter.ql new file mode 100644 index 0000000000..e990a7dcf3 --- /dev/null +++ b/c/common/test/rules/unusedparameter/UnusedParameter.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unusedparameter.UnusedParameter + +class TestFileQuery extends UnusedParameterSharedQuery, TestQuery { } diff --git a/c/common/test/rules/unusedparameter/test.c b/c/common/test/rules/unusedparameter/test.c new file mode 100644 index 0000000000..87fb1b5fe7 --- /dev/null +++ b/c/common/test/rules/unusedparameter/test.c @@ -0,0 +1,8 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +int test_used(int x) { return x; } // COMPLIANT + +void test_unused(int x) {} // NON_COMPLIANT + +void test_no_def(int x); // COMPLIANT - no definition, so cannot be "unused" \ No newline at end of file diff --git a/c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected b/c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected new file mode 100644 index 0000000000..cbcad4dc1c --- /dev/null +++ b/c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected @@ -0,0 +1,4 @@ +| test.c:4:8:4:8 | A | Type declaration A is not used. | +| test.c:7:18:7:18 | D | Type declaration D is not used. | +| test.c:28:11:28:11 | R | Type declaration R is not used. | +| test.c:41:12:41:12 | (unnamed class/struct/union) | Type declaration (unnamed class/struct/union) is not used. | diff --git a/c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql b/c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql new file mode 100644 index 0000000000..f1c09524d5 --- /dev/null +++ b/c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unusedtypedeclarations.UnusedTypeDeclarations + +class TestFileQuery extends UnusedTypeDeclarationsSharedQuery, TestQuery { } diff --git a/c/common/test/rules/unusedtypedeclarations/test.c b/c/common/test/rules/unusedtypedeclarations/test.c new file mode 100644 index 0000000000..aedd16338e --- /dev/null +++ b/c/common/test/rules/unusedtypedeclarations/test.c @@ -0,0 +1,52 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +struct A {}; // NON_COMPLIANT - unused + +struct C {}; // COMPLIANT - used in the type def +typedef struct C D; // NON_COMPLIANT - typedef itself not used + +struct F {}; // COMPLIANT - used as a global function return type + +struct F test_return_value() { + struct F f; + return f; +} + +struct G {}; // COMPLIANT - used as a global function parameter type + +void test_global_function(struct G g) {} + +enum M { C1, C2, C3 }; // COMPLIANT - used in an enum type access below + +void test_enum_access() { int i = C1; } + +struct O {}; // COMPLIANT - used in typedef below + +typedef struct O P; // COMPLIANT - used in typedef below +typedef P Q; // COMPLIANT - used in function below +typedef Q R; // NON_COMPLIANT - never used + +Q test_type_def() {} + +struct { // COMPLIANT - used in type definition + union { // COMPLIANT - f1 and f3 is accessed + struct { // COMPLIANT - f1 is accessed + int f1; + }; + struct { // COMPLIANT - f3 is accessed + float f2; + float f3; + }; + struct { // NON_COMPLIANT - f4 is never accessed + long f4; + }; + }; + int f5; +} s; + +void test_nested_struct() { + s.f1; + s.f3; + s.f5; +} \ No newline at end of file diff --git a/c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.expected b/c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.expected new file mode 100644 index 0000000000..1be3ed8ad2 --- /dev/null +++ b/c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.expected @@ -0,0 +1 @@ +| test.c:10:42:10:58 | asm statement | Use of assembler is not documented. | diff --git a/c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql b/c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql new file mode 100644 index 0000000000..f9997627b4 --- /dev/null +++ b/c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.usageofassemblernotdocumented.UsageOfAssemblerNotDocumented + +class TestFileQuery extends UsageOfAssemblerNotDocumentedSharedQuery, TestQuery { } diff --git a/c/common/test/rules/usageofassemblernotdocumented/test.c b/c/common/test/rules/usageofassemblernotdocumented/test.c new file mode 100644 index 0000000000..00e0fee68b --- /dev/null +++ b/c/common/test/rules/usageofassemblernotdocumented/test.c @@ -0,0 +1,14 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +// COMPLIANT +void test_assembly_is_documented() { + // This comment serves as documentation + __asm__("ret\n"); +} + +// NON_COMPLIANT +void test_assembly_is_not_documented() { __asm__("ret\n"); } + +// COMPLIANT +#define RETURN __asm__("ret\n") +void test_undocumented_assembly_from_macro() { RETURN; } \ No newline at end of file diff --git a/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.expected b/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.expected new file mode 100644 index 0000000000..450491ffe7 --- /dev/null +++ b/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.expected @@ -0,0 +1,7 @@ +| test.c:35:20:35:23 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 0 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.c:35:18:35:42 | {...} | array of type int[4][2] | +| test.c:35:26:35:29 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 1 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.c:35:18:35:42 | {...} | array of type int[4][2] | +| test.c:35:32:35:35 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 2 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.c:35:18:35:42 | {...} | array of type int[4][2] | +| test.c:35:38:35:41 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 3 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.c:35:18:35:42 | {...} | array of type int[4][2] | +| test.c:41:34:41:34 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 1 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.c:41:18:41:35 | {...} | array of type int[2][2] | +| test.c:47:26:47:29 | {...} | Missing braces on aggregate literal of type $@ which is assigned to field $@. | test.c:4:10:4:10 | (unnamed class/struct/union) | (unnamed class/struct/union) | test.c:7:5:7:8 | m_s1 | m_s1 | +| test.c:47:26:47:29 | {...} | Missing braces on aggregate literal of type $@ which is assigned to field $@. | test.c:11:10:11:10 | (unnamed class/struct/union) | (unnamed class/struct/union) | test.c:7:5:7:8 | m_s1 | m_s1 | diff --git a/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql b/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql new file mode 100644 index 0000000000..b2bbb0ff1c --- /dev/null +++ b/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.useinitializerbracestomatchaggregatetypestructure.UseInitializerBracesToMatchAggregateTypeStructure + +class TestFileQuery extends UseInitializerBracesToMatchAggregateTypeStructureSharedQuery, TestQuery { +} diff --git a/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/test.c b/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/test.c new file mode 100644 index 0000000000..017889e9fa --- /dev/null +++ b/c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/test.c @@ -0,0 +1,55 @@ +struct Foo { + int m_i1; + int m_i2; + struct { + int m_s1_i1; + int m_s1_i2; + } m_s1; +}; + +struct Bar { + struct { + int m_s1_i1; + int m_s1_i2; + } m_s1; + int m_i1; + int m_i2; +}; + +struct Baz { + int m_baz_i1; + int m_baz_i2; + struct Foo f; +}; + +struct StructNested { + int m_nested_i1; + int *m_nested_i2; + struct Baz m_baz; + int m_array[10]; +}; + +void test() { + int l01[4] = {1, 2, 3, 4}; // COMPLIANT + int l02[4][2] = {{1, 2}, {3, 4}, {3, 4}, {3, 4}}; // COMPLIANT + int l03[4][2] = {1, 2, 3, 4, 3, 4, 3, 4}; // NON_COMPLIANT - implied braces + int l04[4][2] = {0}; // COMPLIANT + int l06[4][2] = {{0}, {0}, {0}, {0}}; // COMPLIANT, nested zero initializer + int l08[4] = {1, 2}; // COMPLIANT, but missing explicit init + int l09[2][2] = {{1, 2}}; // COMPLIANT, but missing explicit init + int l10[2][2] = {{1, 2}, [1] = {0}}; // COMPLIANT + int l11[2][2] = {{1, 2}, [1] = 0}; // NON_COMPLIANT - implied braces + int l12[2][2] = {{1, 2}, [1][0] = 0, [1][1] = 0}; // COMPLIANT + int l13[2][2] = {{0}, [1][0] = 0}; // COMPLIANT + int l14[2][2] = { + {0}, [1][0] = 0, 0}; // NON_COMPLIANT[FALSE_NEGATIVE] - not all elements + // initialized with designated initializer + struct Foo f1 = {1, 2, 3, 4}; // NON_COMPLIANT - implied braces + struct Foo f2 = {1, 2, {3, 4}}; // COMPLIANT + struct Foo f3 = {0}; // COMPLIANT + struct Foo f4 = {0, 2}; // COMPLIANT, but missing explicit init + struct Foo f5 = {0, 2, {0}}; // COMPLIANT + struct Bar b1 = {0}; // COMPLIANT + struct Bar b2 = {{0}}; // COMPLIANT, but missing explicit init + struct StructNested n = {0}; // COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.expected b/c/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.expected new file mode 100644 index 0000000000..bbd4264069 --- /dev/null +++ b/c/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.expected @@ -0,0 +1,2 @@ +| test.c:5:3:5:5 | 10 | Non zero octal literal 012. | +| test.c:6:3:6:5 | 44 | Non zero octal literal 054. | diff --git a/c/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql b/c/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql new file mode 100644 index 0000000000..0404a7bc0c --- /dev/null +++ b/c/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.useofnonzerooctalliteral.UseOfNonZeroOctalLiteral + +class TestFileQuery extends UseOfNonZeroOctalLiteralSharedQuery, TestQuery { } diff --git a/c/common/test/rules/useofnonzerooctalliteral/test.c b/c/common/test/rules/useofnonzerooctalliteral/test.c new file mode 100644 index 0000000000..11b439b02e --- /dev/null +++ b/c/common/test/rules/useofnonzerooctalliteral/test.c @@ -0,0 +1,7 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void test_non_zero_octal() { + 0; // COMPLIANT - octal literal zero permitted + 012; // NON_COMPLIANT + 054; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql b/c/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql index 819d12c4e8..55554bee07 100644 --- a/c/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql +++ b/c/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.useonlyarrayindexingforpointerarithmetic.UseOnlyArrayIndexingForPointerArithmetic + +class TestFileQuery extends UseOnlyArrayIndexingForPointerArithmeticSharedQuery, TestQuery { } diff --git a/c/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql b/c/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql index 6f4ad4c40e..44947bf85a 100644 --- a/c/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql +++ b/c/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.wrapspuriousfunctioninloop.WrapSpuriousFunctionInLoop + +class TestFileQuery extends WrapSpuriousFunctionInLoopSharedQuery, TestQuery { } diff --git a/c/misra/src/codeql-pack.lock.yml b/c/misra/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/c/misra/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/misra/src/codeql-suites/misra-c-2012-third-edition-with-amendment-2.qls b/c/misra/src/codeql-suites/misra-c-2012-third-edition-with-amendment-2.qls new file mode 100644 index 0000000000..8d06e7c2c8 --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-2012-third-edition-with-amendment-2.qls @@ -0,0 +1,13 @@ +- description: MISRA C 2012 - Third Edition, First Revision including Amendment 2 +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/c/2012/third-edition-first-revision + - external/misra/c/2012/amendment2 +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/c/misra/src/codeql-suites/misra-c-advisory.qls b/c/misra/src/codeql-suites/misra-c-advisory.qls new file mode 100644 index 0000000000..39df0d6583 --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-advisory.qls @@ -0,0 +1,12 @@ +- description: MISRA C 2023 (Advisory) +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/obligation/advisory +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/c/misra/src/codeql-suites/misra-c-audit.qls b/c/misra/src/codeql-suites/misra-c-audit.qls new file mode 100644 index 0000000000..cac9df04ae --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-audit.qls @@ -0,0 +1,8 @@ +- description: MISRA C 2023 (Audit) +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/c/audit diff --git a/c/misra/src/codeql-suites/misra-c-default.qls b/c/misra/src/codeql-suites/misra-c-default.qls new file mode 100644 index 0000000000..cdc6eb65ad --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-default.qls @@ -0,0 +1,11 @@ +- description: MISRA C 2023 (Default) +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem +- exclude: + tags contain: + - external/misra/audit + - external/misra/strict + - external/misra/default-disabled diff --git a/c/misra/src/codeql-suites/misra-c-mandatory.qls b/c/misra/src/codeql-suites/misra-c-mandatory.qls new file mode 100644 index 0000000000..09eccdc50c --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-mandatory.qls @@ -0,0 +1,12 @@ +- description: MISRA C 2023 (Advisory) +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/obligation/mandatory +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/c/misra/src/codeql-suites/misra-c-required.qls b/c/misra/src/codeql-suites/misra-c-required.qls new file mode 100644 index 0000000000..f7c77e937a --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-required.qls @@ -0,0 +1,12 @@ +- description: MISRA C 2023 (Required) +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/obligation/required +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/c/misra/src/codeql-suites/misra-c-strict.qls b/c/misra/src/codeql-suites/misra-c-strict.qls new file mode 100644 index 0000000000..b8f4885189 --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-strict.qls @@ -0,0 +1,8 @@ +- description: MISRA C 2023 (Strict) +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/strict diff --git a/c/misra/src/codeql-suites/misra-default.qls b/c/misra/src/codeql-suites/misra-default.qls index d32637556d..e645bb1545 100644 --- a/c/misra/src/codeql-suites/misra-default.qls +++ b/c/misra/src/codeql-suites/misra-default.qls @@ -1,10 +1,2 @@ -- description: MISRA C 2012 (Default) -- qlpack: misra-c-coding-standards -- include: - kind: - - problem - - path-problem -- exclude: - tags contain: - - external/misra/audit - - external/misra/default-disabled \ No newline at end of file +- description: "DEPRECATED - MISRA C 2012 - use misra-c-default.qls instead" +- import: codeql-suites/misra-c-default.qls diff --git a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll new file mode 100644 index 0000000000..44193322ea --- /dev/null +++ b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll @@ -0,0 +1,510 @@ +/** + * A module for identifying essential types as defined by MISRA C 2012. + */ + +import codingstandards.c.misra +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils +import MisraExpressions + +newtype TEssentialFloatCategory = + Real() or + Complex() + +newtype TEssentialTypeCategory = + EssentiallyBooleanType() or + EssentiallyCharacterType() or + EssentiallyEnumType() or + EssentiallySignedType() or + EssentiallyUnsignedType() or + EssentiallyFloatingType(TEssentialFloatCategory c) + +/** An essential type category, as specified by Appendix D.1. */ +class EssentialTypeCategory extends TEssentialTypeCategory { + string toString() { + this = EssentiallyBooleanType() and result = "essentially Boolean type" + or + this = EssentiallyCharacterType() and result = "essentially Character type" + or + this = EssentiallyEnumType() and result = "essentially Enum Type" + or + this = EssentiallySignedType() and result = "essentially Signed type" + or + this = EssentiallyUnsignedType() and result = "essentially Unsigned type" + or + this = EssentiallyFloatingType(Real()) and result = "essentially Floating type" + or + this = EssentiallyFloatingType(Complex()) and result = "essentially Complex Floating type" + } +} + +class EssentiallySignedOrUnsignedType extends EssentialTypeCategory { + EssentiallySignedOrUnsignedType() { + this = EssentiallySignedType() or this = EssentiallyUnsignedType() + } +} + +/** + * An expression in the program that evaluates to a compile time constant signed or unsigned integer. + */ +private class ConstantIntegerExpr extends Expr { + pragma[noinline] + ConstantIntegerExpr() { + getEssentialTypeCategory(this.getType()) instanceof EssentiallySignedOrUnsignedType and + exists(this.getValue().toFloat()) and + not this instanceof Conversion + } +} + +/** A `float` which represents an integer constant in the program. */ +private class IntegerConstantAsFloat extends float { + IntegerConstantAsFloat() { exists(ConstantIntegerExpr ce | this = ce.getValue().toFloat()) } +} + +/** + * Identifies which integral types from which type categories can represent a given integer constant + * in the program. + */ +pragma[nomagic] +private predicate isCandidateIntegralType( + EssentialTypeCategory cat, IntegralType it, IntegerConstantAsFloat c +) { + getEssentialTypeCategory(it) = cat and + c = any(ConstantIntegerExpr ce).getValue().toFloat() and + // As with range analysis, we assume two's complement representation + typeLowerBound(it) <= c and + typeUpperBound(it) >= c +} + +/** + * Gets the unsigned type of lowest rank that can represent the value of the given expression, + * assuming that the expression is essentially unsigned. + */ +pragma[nomagic] +private IntegralType utlr(ConstantIntegerExpr const) { + getEssentialTypeCategory(const.getType()) = EssentiallyUnsignedType() and + result = utlr_c(const.getValue().toFloat()) +} + +/** + * Given an integer constant that appears in the program, gets the unsigned type of lowest rank + * that can hold it. + */ +pragma[nomagic] +private IntegralType utlr_c(IntegerConstantAsFloat c) { + isCandidateIntegralType(EssentiallyUnsignedType(), result, c) and + forall(IntegralType it | isCandidateIntegralType(EssentiallyUnsignedType(), it, c) | + result.getSize() <= it.getSize() + ) +} + +/** + * Gets the signed type of lowest rank that can represent the value of the given expression, + * assuming that the expression is essentially signed. + */ +pragma[nomagic] +private IntegralType stlr(ConstantIntegerExpr const) { + getEssentialTypeCategory(const.getType()) = EssentiallySignedType() and + result = stlr_c(const.getValue().toFloat()) +} + +/** + * Given an integer constant that appears in the program, gets the signed type of lowest rank + * that can hold it. + */ +pragma[nomagic] +private IntegralType stlr_c(IntegerConstantAsFloat c) { + isCandidateIntegralType(EssentiallySignedType(), result, c) and + forall(IntegralType it | isCandidateIntegralType(EssentiallySignedType(), it, c) | + result.getSize() <= it.getSize() + ) +} + +/** + * Define the essential type category for an essentialType or a typedef of an essentialType. + */ +EssentialTypeCategory getEssentialTypeCategory(Type type) { + exists(Type essentialType | + if type instanceof MisraBoolType + then essentialType = type + else + // If not a bool type, resolve the typedefs to determine the actual type + essentialType = type.getUnspecifiedType() + | + result = EssentiallyBooleanType() and essentialType instanceof MisraBoolType + or + result = EssentiallyCharacterType() and essentialType instanceof PlainCharType + or + result = EssentiallySignedType() and + essentialType.(IntegralType).isSigned() and + not essentialType instanceof PlainCharType + or + // Anonymous enums are considered to be signed + result = EssentiallySignedType() and + essentialType instanceof AnonymousEnumType and + not essentialType instanceof MisraBoolType + or + result = EssentiallyUnsignedType() and + essentialType.(IntegralType).isUnsigned() and + not essentialType instanceof PlainCharType + or + result = EssentiallyEnumType() and + essentialType instanceof NamedEnumType and + not essentialType instanceof MisraBoolType + or + result = EssentiallyFloatingType(Real()) and + essentialType instanceof RealNumberType + or + result = EssentiallyFloatingType(Complex()) and + essentialType instanceof ComplexNumberType + ) +} + +/** + * Gets the essential type of the given expression `e`, considering any explicit conversions. + */ +pragma[nomagic] +Type getEssentialType(Expr e) { + if e.hasConversion() + then result = getEssentialTypeOfConversion(e.getFullyConverted()) + else result = e.(EssentialExpr).getEssentialType() +} + +Type getEssentialTypeOfConversion(Expr e) { + if e.(Conversion).isImplicit() or e instanceof ParenthesisExpr or e instanceof C11GenericExpr + then result = getEssentialTypeOfConversion(e.(Conversion).getExpr()) + else result = e.(EssentialExpr).getEssentialType() +} + +Type getEssentialTypeBeforeConversions(Expr e) { result = e.(EssentialExpr).getEssentialType() } + +/** + * For most essential types, `Type.getSize()` is correct, except for complex floating types. + * + * For complex floating types, the size is the size of the real part, so we divide by 2. + */ +int getEssentialSize(Type essentialType) { + if getEssentialTypeCategory(essentialType) = EssentiallyFloatingType(Complex()) + then result = essentialType.getSize() / 2 + else result = essentialType.getSize() +} + +class EssentialExpr extends Expr { + Type getEssentialType() { result = this.getType() } + + Type getStandardType() { result = this.getType() } +} + +class EssentialCommaExpr extends EssentialExpr, CommaExpr { + override Type getEssentialType() { result = getEssentialType(getRightOperand()) } +} + +class EssentialRelationalOperationExpr extends EssentialExpr, RelationalOperation { + override Type getEssentialType() { result instanceof BoolType } +} + +class EssentialBinaryLogicalOperationExpr extends EssentialExpr, BinaryLogicalOperation { + override Type getEssentialType() { result instanceof BoolType } +} + +class EssentialUnaryLogicalOperationExpr extends EssentialExpr, UnaryLogicalOperation { + override Type getEssentialType() { result instanceof BoolType } +} + +class EssentialEqualityOperationExpr extends EssentialExpr, EqualityOperation { + override Type getEssentialType() { result instanceof BoolType } +} + +class EssentialShiftOperationExpr extends EssentialExpr, BinaryBitwiseOperation { + EssentialShiftOperationExpr() { + this instanceof LShiftExpr or + this instanceof RShiftExpr + } + + override Type getEssentialType() { + exists(Type operandEssentialType, EssentialTypeCategory operandEssentialTypeCategory | + operandEssentialType = getEssentialType(getLeftOperand()) and + operandEssentialTypeCategory = getEssentialTypeCategory(operandEssentialType) + | + if operandEssentialTypeCategory instanceof EssentiallyUnsignedType + then + if exists(this.getValue()) + then result = utlr(this) // If constant and essentially unsigned us the utlr + else result = operandEssentialType + else result = this.getStandardType() + ) + } +} + +class EssentialBitwiseComplementExpr extends EssentialExpr, ComplementExpr { + override Type getEssentialType() { + exists(Type operandEssentialType, EssentialTypeCategory operandEssentialTypeCategory | + operandEssentialType = getEssentialType(getOperand()) and + operandEssentialTypeCategory = getEssentialTypeCategory(operandEssentialType) + | + if operandEssentialTypeCategory instanceof EssentiallyUnsignedType + then + if exists(this.getValue()) + then result = utlr(this) // If constant and essentially unsigned us the utlr + else result = operandEssentialType + else result = this.getStandardType() + ) + } +} + +class EssentialUnaryPlusExpr extends EssentialExpr, UnaryPlusExpr { + override Type getEssentialType() { + exists(Type operandEssentialType, EssentialTypeCategory operandEssentialTypeCategory | + operandEssentialType = getEssentialType(getOperand()) and + operandEssentialTypeCategory = getEssentialTypeCategory(operandEssentialType) + | + if operandEssentialTypeCategory instanceof EssentiallySignedOrUnsignedType + then result = operandEssentialType + else result = getStandardType() + ) + } +} + +class EssentialUnaryMinusExpr extends EssentialExpr, UnaryMinusExpr { + override Type getEssentialType() { + exists(Type operandEssentialType, EssentialTypeCategory operandEssentialTypeCategory | + operandEssentialType = getEssentialType(getOperand()) and + operandEssentialTypeCategory = getEssentialTypeCategory(operandEssentialType) + | + if operandEssentialTypeCategory = EssentiallySignedType() + then if exists(this.getValue()) then result = stlr(this) else result = operandEssentialType + else result = getStandardType() + ) + } +} + +bindingset[essentialTypeA, essentialTypeB] +private Type maxRankType(Type essentialTypeA, Type essentialTypeB) { + if essentialTypeA.getSize() > essentialTypeB.getSize() + then result = essentialTypeA + else result = essentialTypeB +} + +class EssentialConditionalExpr extends EssentialExpr, ConditionalExpr { + override Type getEssentialType() { + exists(Type thenEssentialType, Type elseEssentialType | + thenEssentialType = getEssentialType(getThen()) and + elseEssentialType = getEssentialType(getElse()) + | + if thenEssentialType = elseEssentialType + then result = thenEssentialType + else + if + getEssentialTypeCategory(thenEssentialType) = getEssentialTypeCategory(elseEssentialType) and + getEssentialTypeCategory(thenEssentialType) instanceof EssentiallySignedOrUnsignedType + then result = maxRankType(thenEssentialType, elseEssentialType) + else result = this.getStandardType() + ) + } +} + +/** + * A binary operation subject to usual conversions, with essential type behaviour as specified by D.7.9. + */ +class EssentialBinaryOperationSubjectToUsualConversions extends EssentialExpr, BinaryOperation { + EssentialBinaryOperationSubjectToUsualConversions() { + this instanceof MulExpr + or + this instanceof DivExpr + or + this instanceof RemExpr + or + this instanceof AddExpr + or + this instanceof SubExpr + or + this instanceof BitwiseAndExpr + or + this instanceof BitwiseOrExpr + or + this instanceof BitwiseXorExpr + } + + override Type getEssentialType() { + exists( + Type leftEssentialType, Type rightEssentialType, + EssentialTypeCategory leftEssentialTypeCategory, + EssentialTypeCategory rightEssentialTypeCategory, int intTypeSize + | + leftEssentialType = getEssentialType(getLeftOperand()) and + rightEssentialType = getEssentialType(getRightOperand()) and + leftEssentialTypeCategory = getEssentialTypeCategory(leftEssentialType) and + rightEssentialTypeCategory = getEssentialTypeCategory(rightEssentialType) and + // For rules around addition/subtraction with char types: + intTypeSize = any(IntType i | i.isSigned()).getSize() + | + if + leftEssentialTypeCategory = rightEssentialTypeCategory and + leftEssentialTypeCategory instanceof EssentiallySignedOrUnsignedType + then + if exists(getValue()) + then + leftEssentialTypeCategory = EssentiallySignedType() and result = stlr(this) + or + leftEssentialTypeCategory = EssentiallyUnsignedType() and result = utlr(this) + else result = maxRankType(leftEssentialType, rightEssentialType) + else result = this.getStandardType() + ) + } +} + +/** + * An add expression, with essential type behaviour as specified by D.7.9. + */ +class EssentialAddExpr extends EssentialBinaryOperationSubjectToUsualConversions, AddExpr { + override Type getEssentialType() { + exists( + Type otherOperandType, EssentialTypeCategory operandTypeCategory, + EssentialTypeCategory otherOperandTypeCategory, int intTypeSize + | + operandTypeCategory = getEssentialTypeCategory(getEssentialType(getAnOperand())) and + otherOperandType = getEssentialType(getAnOperand()) and + otherOperandTypeCategory = getEssentialTypeCategory(otherOperandType) and + intTypeSize = any(IntType i).getSize() + | + if + operandTypeCategory = EssentiallyCharacterType() and + otherOperandTypeCategory instanceof EssentiallySignedOrUnsignedType and + otherOperandType.getSize() <= intTypeSize + then result instanceof PlainCharType + else result = super.getEssentialType() + ) + } +} + +/** + * A sub expression, with essential type behaviour as specified by D.7.9. + */ +class EssentialSubExpr extends EssentialBinaryOperationSubjectToUsualConversions, SubExpr { + override Type getEssentialType() { + exists( + EssentialTypeCategory leftEssentialTypeCategory, Type rightEssentialType, + EssentialTypeCategory rightEssentialTypeCategory, int intTypeSize + | + leftEssentialTypeCategory = getEssentialTypeCategory(getEssentialType(getLeftOperand())) and + rightEssentialType = getEssentialType(getRightOperand()) and + rightEssentialTypeCategory = getEssentialTypeCategory(rightEssentialType) and + intTypeSize = any(IntType i).getSize() + | + if + leftEssentialTypeCategory = EssentiallyCharacterType() and + rightEssentialTypeCategory instanceof EssentiallySignedOrUnsignedType and + rightEssentialType.getSize() <= intTypeSize + then result instanceof PlainCharType + else result = super.getEssentialType() + ) + } +} + +/** + * A named Enum type, as per D.5. + */ +class NamedEnumType extends Enum { + NamedEnumType() { + not isAnonymous() + or + exists(Type useOfEnum | this = useOfEnum.stripType() | + exists(TypedefType t | t.getBaseType() = useOfEnum) + or + exists(Function f | f.getType() = useOfEnum or f.getAParameter().getType() = useOfEnum) + or + exists(Struct s | s.getAField().getType() = useOfEnum) + or + exists(Variable v | v.getType() = useOfEnum) + ) + } +} + +/** + * An anonymous Enum type, as per D.5. + */ +class AnonymousEnumType extends Enum { + AnonymousEnumType() { not this instanceof NamedEnumType } +} + +/** + * The EssentialType of an EnumConstantAccess, which may be essentially enum or essentially signed. + */ +class EssentialEnumConstantAccess extends EssentialExpr, EnumConstantAccess { + override Type getEssentialType() { + exists(Enum e | e = getTarget().getDeclaringEnum() | + if e instanceof NamedEnumType then result = e else result = stlr(this) + ) + } +} + +class EssentialLiteral extends EssentialExpr, Literal { + override Type getEssentialType() { + if this instanceof BooleanLiteral + then + // This returns a multitude of types - not sure if we really want that + result instanceof MisraBoolType + else ( + if this instanceof CharLiteral + then result instanceof PlainCharType + else + exists(Type underlyingStandardType | + underlyingStandardType = getStandardType().getUnderlyingType() + | + if underlyingStandardType instanceof IntType + then + if underlyingStandardType.(IntType).isSigned() + then result = stlr(this) + else result = utlr(this) + else result = getStandardType() + ) + ) + } +} + +/** + * Holds if `rValue` is assigned to an object of type `lValueEssentialType`. + * + * Assignment is according to "Assignment" in Appendix J of MISRA C 2012, with the inclusion of a + * special case for switch statements as specified for Rule 10.3 and Rule 10.6. + */ +predicate isAssignmentToEssentialType(Type lValueEssentialType, Expr rValue) { + // Special case for Rule 10.3/ Rule 10.6. + exists(SwitchCase sc | + lValueEssentialType = sc.getSwitchStmt().getControllingExpr().getType() and + rValue = sc.getExpr() + ) + or + exists(Assignment a | + lValueEssentialType = a.getLValue().getType() and + rValue = a.getRValue() + ) + or + exists(FunctionCall fc, int i | + lValueEssentialType = fc.getTarget().getParameter(i).getType() and + rValue = fc.getArgument(i) + ) + or + exists(Function f, ReturnStmt rs | + lValueEssentialType = f.getType() and + rs.getEnclosingFunction() = f and + rValue = rs.getExpr() + ) + or + // Initializing a non-aggregate type + exists(Initializer i | + lValueEssentialType = i.getDeclaration().(Variable).getType() and + rValue = i.getExpr() + ) + or + // Initializing an array + exists(ArrayAggregateLiteral aal | + lValueEssentialType = aal.getElementType() and + rValue = aal.getAnElementExpr(_) + ) + or + // Initializing a struct or union + exists(ClassAggregateLiteral cal, Field field | + lValueEssentialType = field.getType() and + rValue = cal.getAFieldExpr(field) + ) +} diff --git a/c/misra/src/codingstandards/c/misra/MisraExpressions.qll b/c/misra/src/codingstandards/c/misra/MisraExpressions.qll new file mode 100644 index 0000000000..b5f03838cc --- /dev/null +++ b/c/misra/src/codingstandards/c/misra/MisraExpressions.qll @@ -0,0 +1,106 @@ +/** + * A module for representing expressions and related types defined in the MISRA C 2012 standard. + */ + +import codingstandards.c.misra + +/** + * A `bool` type, either `stdbool.h` or a hand-coded bool type acceptable to MISRA C 2012. + */ +class MisraBoolType extends Type { + MisraBoolType() { + this instanceof BoolType + or + exists(Enum e | this = e | + count(e.getAnEnumConstant()) = 2 and + e.getEnumConstant(0).getName().toLowerCase() = ["false", "f"] and + e.getEnumConstant(1).getName().toLowerCase() = ["true", "t"] + ) + or + exists(TypedefType t | this = t | t.getName().toLowerCase() = ["bool", "boolean"]) + } +} + +/** + * A boolean literal as defined by the C standard and acceptable to MISRA C 2012. + */ +class BooleanLiteral extends Literal { + BooleanLiteral() { + exists(MacroInvocation mi, int value, string macroName | + macroName = mi.getMacroName() and mi.getExpr() = this and value = this.getValue().toInt() + | + macroName = "false" and value = 0 + or + macroName = "true" and value = 1 + ) + } +} + +/** + * A composite operator as defined in MISRA C:2012 8.10.3. + */ +class CompositeOperator extends Expr { + CompositeOperator() { + // + - * / % + - + this instanceof BinaryArithmeticOperation and + not this instanceof MaxExpr and + not this instanceof MinExpr + or + // << >> & ^ | + this instanceof BinaryBitwiseOperation + or + // ~ + this instanceof ComplementExpr + or + exists(ConditionalExpr ce | ce = this | + ce.getElse() instanceof CompositeExpression or ce.getThen() instanceof CompositeExpression + ) + } +} + +/** + * A composite expression as defined in MISRA C:2012 8.10.3. + */ +class CompositeExpression extends Expr { + CompositeExpression() { + this instanceof CompositeOperator and + // A non-constant expression that is the result of a composite operator + not exists(this.getValue()) + } +} + +/** + * An operator on which the usual arithmetic conversions apply to the operands, as defined in MISRA + * C:2012 6.3.1.8. + */ +class OperationWithUsualArithmeticConversions extends Expr { + OperationWithUsualArithmeticConversions() { + this instanceof BinaryOperation and + not this instanceof LShiftExpr and + not this instanceof RShiftExpr and + not this instanceof LogicalAndExpr and + not this instanceof LogicalOrExpr + or + this instanceof AssignArithmeticOperation + } + + Expr getLeftOperand() { + result = this.(BinaryOperation).getLeftOperand() + or + result = this.(AssignArithmeticOperation).getLValue() + } + + Expr getRightOperand() { + result = this.(BinaryOperation).getRightOperand() + or + result = this.(AssignArithmeticOperation).getRValue() + } + + Expr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() } + + string getOperator() { + result = this.(BinaryOperation).getOperator() + or + result = this.(AssignArithmeticOperation).getOperator() + } +} diff --git a/c/misra/src/qlpack.yml b/c/misra/src/qlpack.yml index 12583f5e2a..8b98f26fb0 100644 --- a/c/misra/src/qlpack.yml +++ b/c/misra/src/qlpack.yml @@ -1,4 +1,9 @@ -name: misra-c-coding-standards -version: 2.9.0 +name: codeql/misra-c-coding-standards +version: 2.49.0-dev +description: MISRA C 2012 suites: codeql-suites -libraryPathDependencies: common-c-coding-standards +license: MIT +default-suite-file: codeql-suites/misra-c-default.qls +dependencies: + codeql/common-c-coding-standards: '*' + codeql/cpp-all: 4.0.3 diff --git a/c/misra/src/rules/RULE-4-10/PrecautionIncludeGuardsNotProvided.ql b/c/misra/src/rules/DIR-4-10/PrecautionIncludeGuardsNotProvided.ql similarity index 76% rename from c/misra/src/rules/RULE-4-10/PrecautionIncludeGuardsNotProvided.ql rename to c/misra/src/rules/DIR-4-10/PrecautionIncludeGuardsNotProvided.ql index deea2afa83..338437b5b2 100644 --- a/c/misra/src/rules/RULE-4-10/PrecautionIncludeGuardsNotProvided.ql +++ b/c/misra/src/rules/DIR-4-10/PrecautionIncludeGuardsNotProvided.ql @@ -1,15 +1,16 @@ /** * @id c/misra/precaution-include-guards-not-provided - * @name RULE-4-10: Precautions shall be taken in order to prevent the contents of a header file being included more than once + * @name DIR-4-10: Precautions shall be taken in order to prevent the contents of a header file being included more than once * @description Using anything other than a standard include guard form can make code confusing and * can lead to multiple or conflicting definitions. * @kind problem * @precision very-high * @problem.severity warning - * @tags external/misra/id/rule-4-10 + * @tags external/misra/id/dir-4-10 * correctness * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/DIR-4-11/CheckMathLibraryFunctionParameters.ql b/c/misra/src/rules/DIR-4-11/CheckMathLibraryFunctionParameters.ql new file mode 100644 index 0000000000..4011b210f8 --- /dev/null +++ b/c/misra/src/rules/DIR-4-11/CheckMathLibraryFunctionParameters.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/check-math-library-function-parameters + * @name DIR-4-11: The validity of values passed to `math.h` library functions shall be checked + * @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/misra/id/dir-4-11 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.uncheckedrangedomainpoleerrors.UncheckedRangeDomainPoleErrors + +class CheckMathLibraryFunctionParametersQuery extends UncheckedRangeDomainPoleErrorsSharedQuery { + CheckMathLibraryFunctionParametersQuery() { + this = ContractsPackage::checkMathLibraryFunctionParametersQuery() + } +} diff --git a/c/misra/src/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.ql b/c/misra/src/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.ql new file mode 100644 index 0000000000..6a910a1a71 --- /dev/null +++ b/c/misra/src/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.ql @@ -0,0 +1,43 @@ +/** + * @id c/misra/low-precision-periodic-trigonometric-function-call + * @name DIR-4-11: The validity of values passed to trigonometric functions shall be checked + * @description Trigonometric periodic functions have significantly less precision when called with + * large floating-point values. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/dir-4-11 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +float getMaxAllowedAbsoluteValue(FloatingPointType t, string description) { + if t.getSize() <= 4 + then ( + // Per MISRA, assume k=1 for float types. + result = 3.15 and description = "pi" + ) else ( + // Allow k=10 for doubles, as the standard allows for a larger range depending on the + // implementation, application, and precision goals. + result = 10 * 3.15 and description = "10 * pi" + ) +} + +from FunctionCall fc, Expr argument, float maxValue, float maxAllowedValue, string maxAllowedStr +where + not isExcluded(fc, ContractsPackage::lowPrecisionPeriodicTrigonometricFunctionCallQuery()) and + fc.getTarget().getName() = ["sin", "cos", "tan"] and + argument = fc.getArgument(0) and + maxValue = rank[1](float bound | bound = [lowerBound(argument), upperBound(argument)].abs()) and + maxAllowedValue = getMaxAllowedAbsoluteValue(argument.getType(), maxAllowedStr) and + maxValue > maxAllowedValue +select fc, + "Call to periodic trigonometric function " + fc.getTarget().getName() + + " with maximum argument absolute value of " + maxValue.toString() + + ", which exceeds the recommended " + "maximum of " + maxAllowedStr + "." diff --git a/c/misra/src/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.ql b/c/misra/src/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.ql similarity index 81% rename from c/misra/src/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.ql rename to c/misra/src/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.ql index 84055145e3..5c70bec761 100644 --- a/c/misra/src/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.ql +++ b/c/misra/src/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.ql @@ -1,16 +1,17 @@ /** * @id c/misra/std-lib-dynamic-memory-allocation-used - * @name RULE-4-12: Dynamic memory allocation shall not be used + * @name DIR-4-12: Dynamic memory allocation shall not be used * @description Using dynamic memory allocation and deallocation can result to undefined behavior. * This query is for the Standard Library Implementation. Any implementation outside it * will require a separate query under the same directive. * @kind problem * @precision very-high * @problem.severity error - * @tags external/misra/id/rule-4-12 + * @tags external/misra/id/dir-4-12 * security * correctness * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -23,7 +24,7 @@ import semmle.code.cpp.models.interfaces.Deallocation from Expr e, string type where - not isExcluded(e, BannedPackage::memoryAllocDeallocFunctionsOfStdlibhUsedQuery()) and + not isExcluded(e, BannedPackage::stdLibDynamicMemoryAllocationUsedQuery()) and ( e.(FunctionCall).getTarget().(AllocationFunction).requiresDealloc() and type = "allocation" diff --git a/c/misra/src/rules/DIR-4-15/PossibleMisuseOfUndetectedInfinity.ql b/c/misra/src/rules/DIR-4-15/PossibleMisuseOfUndetectedInfinity.ql new file mode 100644 index 0000000000..0294fc6919 --- /dev/null +++ b/c/misra/src/rules/DIR-4-15/PossibleMisuseOfUndetectedInfinity.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/possible-misuse-of-undetected-infinity + * @name DIR-4-15: Evaluation of floating-point expressions shall not lead to the undetected generation of infinities + * @description Evaluation of floating-point expressions shall not lead to the undetected generation + * of infinities. + * @kind path-problem + * @precision medium + * @problem.severity warning + * @tags external/misra/id/dir-4-15 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.misuseofinfinitefloatingpointvalue.MisuseOfInfiniteFloatingPointValue + +class PossibleMisuseOfUndetectedInfinityQuery extends MisuseOfInfiniteFloatingPointValueSharedQuery { + PossibleMisuseOfUndetectedInfinityQuery() { + this = FloatingTypes2Package::possibleMisuseOfUndetectedInfinityQuery() + } +} diff --git a/c/misra/src/rules/DIR-4-15/PossibleMisuseOfUndetectedNaN.ql b/c/misra/src/rules/DIR-4-15/PossibleMisuseOfUndetectedNaN.ql new file mode 100644 index 0000000000..9315a4ed4c --- /dev/null +++ b/c/misra/src/rules/DIR-4-15/PossibleMisuseOfUndetectedNaN.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/possible-misuse-of-undetected-nan + * @name DIR-4-15: Evaluation of floating-point expressions shall not lead to the undetected generation of NaNs + * @description Evaluation of floating-point expressions shall not lead to the undetected generation + * of NaNs. + * @kind path-problem + * @precision low + * @problem.severity warning + * @tags external/misra/id/dir-4-15 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.misuseofnanfloatingpointvalue.MisuseOfNaNFloatingPointValue + +class PossibleMisuseOfUndetectedNaNQuery extends MisuseOfNaNFloatingPointValueSharedQuery { + PossibleMisuseOfUndetectedNaNQuery() { + this = FloatingTypes2Package::possibleMisuseOfUndetectedNaNQuery() + } +} diff --git a/c/misra/src/rules/DIR-4-2/UsageOfAssemblyLanguageShouldBeDocumented.ql b/c/misra/src/rules/DIR-4-2/UsageOfAssemblyLanguageShouldBeDocumented.ql new file mode 100644 index 0000000000..1afd57913e --- /dev/null +++ b/c/misra/src/rules/DIR-4-2/UsageOfAssemblyLanguageShouldBeDocumented.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/usage-of-assembly-language-should-be-documented + * @name DIR-4-2: All usage of assembly language should be documented + * @description Assembly language is not portable and should be documented. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/dir-4-2 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.usageofassemblernotdocumented.UsageOfAssemblerNotDocumented + +class UsageOfAssemblyLanguageShouldBeDocumentedQuery extends UsageOfAssemblerNotDocumentedSharedQuery +{ + UsageOfAssemblyLanguageShouldBeDocumentedQuery() { + this = Language2Package::usageOfAssemblyLanguageShouldBeDocumentedQuery() + } +} diff --git a/c/misra/src/rules/DIR-4-3/LanguageNotEncapsulatedAndIsolated.ql b/c/misra/src/rules/DIR-4-3/LanguageNotEncapsulatedAndIsolated.ql index fb9f00e9c4..698cbabf01 100644 --- a/c/misra/src/rules/DIR-4-3/LanguageNotEncapsulatedAndIsolated.ql +++ b/c/misra/src/rules/DIR-4-3/LanguageNotEncapsulatedAndIsolated.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/dir-4-3 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/DIR-4-4/SectionsOfCodeShallNotBeCommentedOut.ql b/c/misra/src/rules/DIR-4-4/SectionsOfCodeShallNotBeCommentedOut.ql new file mode 100644 index 0000000000..272a411f0e --- /dev/null +++ b/c/misra/src/rules/DIR-4-4/SectionsOfCodeShallNotBeCommentedOut.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/sections-of-code-shall-not-be-commented-out + * @name DIR-4-4: Sections of code should not be commented out + * @description Commented out code may become out of date leading to developer confusion. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/dir-4-4 + * maintainability + * readability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.sectionsofcodeshallnotbecommentedout.SectionsOfCodeShallNotBeCommentedOut + +class SectionsOfCodeShallNotBeCommentedOutQuery extends SectionsOfCodeShallNotBeCommentedOutSharedQuery +{ + SectionsOfCodeShallNotBeCommentedOutQuery() { + this = SyntaxPackage::sectionsOfCodeShallNotBeCommentedOutQuery() + } +} diff --git a/c/misra/src/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.ql b/c/misra/src/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.ql index 954bdf687b..5dd78fc340 100644 --- a/c/misra/src/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.ql +++ b/c/misra/src/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.ql @@ -8,6 +8,7 @@ * @tags external/misra/id/dir-4-5 * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.differentidentifiersnottypographicallyunambiguous.DifferentIdentifiersNotTypographicallyUnambiguous -class IdentifiersInTheSameNameSpaceUnambiguousQuery extends DifferentIdentifiersNotTypographicallyUnambiguousSharedQuery { +class IdentifiersInTheSameNameSpaceUnambiguousQuery extends DifferentIdentifiersNotTypographicallyUnambiguousSharedQuery +{ IdentifiersInTheSameNameSpaceUnambiguousQuery() { this = SyntaxPackage::identifiersInTheSameNameSpaceUnambiguousQuery() } diff --git a/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql b/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql new file mode 100644 index 0000000000..0e6c902441 --- /dev/null +++ b/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql @@ -0,0 +1,117 @@ +/** + * @id c/misra/plain-numerical-type-used-over-explicit-typedef + * @name DIR-4-6: Do not use plain numerical types over typedefs named after their explicit bit layout + * @description Using plain numerical types over typedefs with explicit sign and bit counts may lead + * to confusion on how much bits are allocated for a value. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/dir-4-6 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.BuiltInNumericTypes + +class BuiltInNumericType extends BuiltInType { + BuiltInNumericType() { + /* Exclude the plain char because it does not count as a numeric type */ + this.(CharType).isExplicitlySigned() + or + this.(CharType).isExplicitlyUnsigned() + or + this instanceof BuiltInIntegerType + or + this instanceof FloatType + or + this instanceof DoubleType + or + this instanceof LongDoubleType + or + this instanceof ComplexNumberType + } +} + +predicate forbiddenBuiltinNumericUsedInDecl(Variable var, string message) { + var.getType() instanceof BuiltInNumericType and + not var instanceof ExcludedVariable and + message = "The type " + var.getType() + " is not a fixed-width numeric type." +} + +class SizedTypeString extends string { + string pattern; + int size; + + bindingset[this] + pragma[inline] + SizedTypeString() { + pattern = "(u?int|c?float)(4|8|16|32|64|128)_t" and + this.regexpMatch(pattern) and + size = this.regexpCapture(pattern, 2).toInt() + } + + bindingset[this] + pragma[inline] + int getSize() { result = size } + + bindingset[this] + pragma[inline] + predicate isComplex() { this.charAt(0) = "c" } +} + +predicate forbiddenComplexType(CTypedefType typedef, string message) { + typedef.getName().(SizedTypeString).isComplex() and + ( + if typedef.getBaseType().stripTopLevelSpecifiers() instanceof ComplexNumberType + then + typedef.getSize() * 8 != typedef.getName().(SizedTypeString).getSize() * 2 and + message = "The typedef type " + typedef.getName() + " does not have its indicated real size." + else message = "The typedef type " + typedef.getName() + " is not a complex type." + ) +} + +predicate forbiddenRealType(CTypedefType typedef, string message) { + not typedef.getName().(SizedTypeString).isComplex() and + ( + if typedef.getBaseType().stripTopLevelSpecifiers() instanceof ComplexNumberType + then message = "The typedef name " + typedef.getName() + " does not indicate a complex type." + else ( + typedef.getSize() * 8 != typedef.getName().(SizedTypeString).getSize() and + message = "The typedef type " + typedef.getName() + " does not have its indicated size." + ) + ) +} + +predicate forbiddenTypedef(CTypedefType typedef, string message) { + /* If the typedef's name contains an explicit size */ + ( + if typedef.getName() instanceof SizedTypeString + then ( + forbiddenRealType(typedef, message) + or + forbiddenComplexType(typedef, message) + ) else ( + ( + // type def is to a built in numeric type + typedef.getBaseType() instanceof BuiltInNumericType and + // but does not include the size in the name + not typedef.getName() instanceof SizedTypeString + or + // this is a typedef to a forbidden type def + forbiddenTypedef(typedef.getBaseType(), _) + ) and + message = "The type " + typedef.getName() + " is not an alias to a fixed-width numeric type." + ) + ) +} + +from Element elem, string message +where + not isExcluded(elem, Types1Package::plainNumericalTypeUsedOverExplicitTypedefQuery()) and + ( + forbiddenBuiltinNumericUsedInDecl(elem, message) or + forbiddenTypedef(elem, message) + ) +select elem, message diff --git a/c/misra/src/rules/DIR-4-7/FunctionErrorInformationUntested.ql b/c/misra/src/rules/DIR-4-7/FunctionErrorInformationUntested.ql new file mode 100644 index 0000000000..0c0a3d7b1a --- /dev/null +++ b/c/misra/src/rules/DIR-4-7/FunctionErrorInformationUntested.ql @@ -0,0 +1,28 @@ +/** + * @id c/misra/function-error-information-untested + * @name DIR-4-7: If a function generates error information, then that error information shall be tested + * @description A function (whether it is part of the standard library, a third party library or a + * user defined function) may provide some means of indicating the occurrence of an + * error. This may be via a global error flag, a parametric error flag, a special + * return value or some other means. Whenever such a mechanism is provided by a + * function the calling program shall check for the indication of an error as soon as + * the function returns. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/dir-4-7 + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.functionerroneousreturnvaluenottested.FunctionErroneousReturnValueNotTested + +class FunctionErrorInformationUntestedQuery extends FunctionErroneousReturnValueNotTestedSharedQuery +{ + FunctionErrorInformationUntestedQuery() { + this = ContractsPackage::functionErrorInformationUntestedQuery() + } +} diff --git a/c/misra/src/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql b/c/misra/src/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql similarity index 86% rename from c/misra/src/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql rename to c/misra/src/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql index 1521b9602a..b32a0a4aee 100644 --- a/c/misra/src/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql +++ b/c/misra/src/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql @@ -1,15 +1,16 @@ /** * @id c/misra/object-with-no-pointer-dereference-should-be-opaque - * @name RULE-4-8: The implementation of an object shall be hidden if a pointer to its structure or union is never dereferenced within a translation unit + * @name DIR-4-8: The implementation of an object shall be hidden if a pointer to its structure or union is never dereferenced within a translation unit * @description If a pointer to a structure or union is never dereferenced within a translation * unit, then the implementation of the object should be hidden to prevent * unintentional changes. * @kind problem * @precision very-high * @problem.severity error - * @tags external/misra/id/rule-4-8 + * @tags external/misra/id/dir-4-8 * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/DIR-4-9/FunctionOverFunctionLikeMacro.ql b/c/misra/src/rules/DIR-4-9/FunctionOverFunctionLikeMacro.ql new file mode 100644 index 0000000000..582715e34f --- /dev/null +++ b/c/misra/src/rules/DIR-4-9/FunctionOverFunctionLikeMacro.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/function-over-function-like-macro + * @name DIR-4-9: A function should be used in preference to a function-like macro where they are interchangeable + * @description Using a function-like macro instead of a function can lead to unexpected program + * behaviour. + * @kind problem + * @precision medium + * @problem.severity recommendation + * @tags external/misra/id/dir-4-9 + * external/misra/audit + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.functionlikemacrosdefined.FunctionLikeMacrosDefined + +class FunctionOverFunctionLikeMacroQuery extends FunctionLikeMacrosDefinedSharedQuery { + FunctionOverFunctionLikeMacroQuery() { + this = Preprocessor6Package::functionOverFunctionLikeMacroQuery() + } +} diff --git a/c/misra/src/rules/DIR-5-1/PossibleDataRaceBetweenThreads.ql b/c/misra/src/rules/DIR-5-1/PossibleDataRaceBetweenThreads.ql new file mode 100644 index 0000000000..edf3705a9b --- /dev/null +++ b/c/misra/src/rules/DIR-5-1/PossibleDataRaceBetweenThreads.ql @@ -0,0 +1,162 @@ +/** + * @id c/misra/possible-data-race-between-threads + * @name DIR-5-1: There shall be no data races between threads + * @description Threads shall not access the same memory location concurrently without utilization + * of thread synchronization objects. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/misra/id/dir-5-1 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects +import codingstandards.c.SubObjects +import codingstandards.cpp.Concurrency + +newtype TNonReentrantOperation = + TReadWrite(SubObject object) { + object.getRootIdentity().getStorageDuration().isStatic() + or + object.getRootIdentity().getStorageDuration().isAllocated() + } or + TStdFunctionCall(FunctionCall call) { + call.getTarget() + .hasName([ + "setlocale", "tmpnam", "rand", "srand", "getenv", "getenv_s", "strok", "strerror", + "asctime", "ctime", "gmtime", "localtime", "mbrtoc16", "c16rtomb", "mbrtoc32", + "c32rtomb", "mbrlen", "mbrtowc", "wcrtomb", "mbsrtowcs", "wcsrtombs" + ]) + } + +class NonReentrantOperation extends TNonReentrantOperation { + string toString() { + exists(SubObject object | + this = TReadWrite(object) and + result = object.toString() + ) + or + exists(FunctionCall call | + this = TStdFunctionCall(call) and + result = call.getTarget().getName() + ) + } + + Expr getAReadExpr() { + exists(SubObject object | + this = TReadWrite(object) and + result = object.getAnAccess() + ) + or + this = TStdFunctionCall(result) + } + + Expr getAWriteExpr() { + exists(SubObject object, Assignment assignment | + this = TReadWrite(object) and + result = assignment and + assignment.getLValue() = object.getAnAccess() + ) + or + this = TStdFunctionCall(result) + } + + string getReadString() { + this = TReadWrite(_) and + result = "read operation" + or + this = TStdFunctionCall(_) and + result = "call to non-reentrant function" + } + + string getWriteString() { + this = TReadWrite(_) and + result = "write to object" + or + this = TStdFunctionCall(_) and + result = "call to non-reentrant function" + } + + Element getSourceElement() { + exists(SubObject object | + this = TReadWrite(object) and + result = object.getRootIdentity() + ) + or + this = TStdFunctionCall(result) + } +} + +class WritingThread extends ThreadedFunction { + NonReentrantOperation aWriteObject; + Expr aWriteExpr; + + WritingThread() { + aWriteExpr = aWriteObject.getAWriteExpr() and + // This function directly contains the write expression, or transitively calls the function + // that contains the write expression. + this.calls*(aWriteExpr.getEnclosingFunction()) and + // The write isn't synchronized with a mutex or condition object. + not aWriteExpr instanceof LockProtectedControlFlowNode and + // The write doesn't seem to be during a special initialization phase of the program. + not aWriteExpr.getEnclosingFunction().getName().matches(["%init%", "%boot%", "%start%"]) + } + + Expr getAWriteExpr() { result = aWriteExpr } +} + +class ReadingThread extends ThreadedFunction { + Expr aReadExpr; + + ReadingThread() { + exists(NonReentrantOperation op | + aReadExpr = op.getAReadExpr() and + this.calls*(aReadExpr.getEnclosingFunction()) and + not aReadExpr instanceof LockProtectedControlFlowNode + ) + } + + Expr getAReadExpr() { result = aReadExpr } +} + +predicate mayBeDataRace(Expr write, Expr read, NonReentrantOperation operation) { + exists(WritingThread wt | + wt.getAWriteExpr() = write and + write = operation.getAWriteExpr() and + exists(ReadingThread rt | + read = rt.getAReadExpr() and + read = operation.getAReadExpr() and + ( + wt.isMultiplySpawned() or + not wt = rt + ) + ) + ) +} + +from + WritingThread wt, ReadingThread rt, Expr write, Expr read, NonReentrantOperation operation, + string message, string writeString, string readString +where + not isExcluded(write, Concurrency9Package::possibleDataRaceBetweenThreadsQuery()) and + mayBeDataRace(write, read, operation) and + wt = min(WritingThread f | f.getAWriteExpr() = write | f order by f.getName()) and + rt = min(ReadingThread f | f.getAReadExpr() = read | f order by f.getName()) and + writeString = operation.getWriteString() and + readString = operation.getReadString() and + if wt.isMultiplySpawned() + then + message = + "Threaded " + writeString + + " $@ not synchronized from thread function $@ spawned from a loop." + else + message = + "Threaded " + writeString + + " $@ from thread function $@ is not synchronized with $@ from thread function $@." +select write, message, operation.getSourceElement(), operation.toString(), wt, wt.getName(), read, + "concurrent " + readString, rt, rt.getName() diff --git a/c/misra/src/rules/DIR-5-2/NotNoDeadlocksBetweenThreads.ql b/c/misra/src/rules/DIR-5-2/NotNoDeadlocksBetweenThreads.ql new file mode 100644 index 0000000000..5d949f56ed --- /dev/null +++ b/c/misra/src/rules/DIR-5-2/NotNoDeadlocksBetweenThreads.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/not-no-deadlocks-between-threads + * @name DIR-5-2: There shall be no deadlocks between threads + * @description Circular waits leading to thread deadlocks may be avoided by locking in a predefined + * order. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/dir-5-2 + * external/misra/c/2012/amendment4 + * correctness + * concurrency + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.preventdeadlockbylockinginpredefinedorder.PreventDeadlockByLockingInPredefinedOrder + +class NotNoDeadlocksBetweenThreadsQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery +{ + NotNoDeadlocksBetweenThreadsQuery() { + this = Concurrency6Package::notNoDeadlocksBetweenThreadsQuery() + } +} diff --git a/c/misra/src/rules/DIR-5-3/BannedDynamicThreadCreation.ql b/c/misra/src/rules/DIR-5-3/BannedDynamicThreadCreation.ql new file mode 100644 index 0000000000..4bb526306b --- /dev/null +++ b/c/misra/src/rules/DIR-5-3/BannedDynamicThreadCreation.ql @@ -0,0 +1,29 @@ +/** + * @id c/misra/banned-dynamic-thread-creation + * @name DIR-5-3: There shall be no dynamic thread creation + * @description Creating threads outside of a well-defined program start-up phase creates + * uncertainty in program behavior and concurrency overhead costs. + * @kind problem + * @precision low + * @problem.severity error + * @tags external/misra/id/dir-5-3 + * external/misra/c/2012/amendment4 + * external/misra/c/audit + * correctness + * maintainability + * concurrency + * performance + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency + +from CThreadCreateCall tc, Function enclosingFunction +where + not isExcluded(tc, Concurrency6Package::bannedDynamicThreadCreationQuery()) and + enclosingFunction = tc.getEnclosingFunction() and + not enclosingFunction.getName() = "main" +select tc, "Possible dynamic creation of thread outside initialization in function '$@'.", + enclosingFunction, enclosingFunction.toString() diff --git a/c/misra/src/rules/DIR-5-3/ThreadCreatedByThread.ql b/c/misra/src/rules/DIR-5-3/ThreadCreatedByThread.ql new file mode 100644 index 0000000000..207e763fa7 --- /dev/null +++ b/c/misra/src/rules/DIR-5-3/ThreadCreatedByThread.ql @@ -0,0 +1,38 @@ +/** + * @id c/misra/thread-created-by-thread + * @name DIR-5-3: Threads shall not be created by other threads + * @description Creating threads within threads creates uncertainty in program behavior and + * concurrency overhead costs. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/dir-5-3 + * external/misra/c/2012/amendment4 + * correctness + * maintainability + * concurrency + * performance + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency + +class CThreadRoot extends Function { + CThreadCreateCall threadCreate; + + CThreadRoot() { threadCreate.getFunction() = this } + + /* Get a function which is reachable from this function */ + Function getAReachableFunction() { calls*(result) } + + CThreadCreateCall getACThreadCreateCall() { result = threadCreate } +} + +from CThreadCreateCall tc, CThreadRoot threadRoot +where + not isExcluded(tc, Concurrency6Package::threadCreatedByThreadQuery()) and + tc.getEnclosingFunction() = threadRoot.getAReachableFunction() +select tc, "Thread creation call reachable from function '$@', which may also be $@.", threadRoot, + threadRoot.toString(), threadRoot.getACThreadCreateCall(), "started as a thread" diff --git a/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql b/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql new file mode 100644 index 0000000000..34a0f40fb6 --- /dev/null +++ b/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/language-extensions-should-not-be-used + * @name RULE-1-2: Language extensions should not be used + * @description Language extensions are not portable to other compilers and should not be used. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-1-2 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.AlertReporting +import codingstandards.c.Extensions + +from CCompilerExtension e +where not isExcluded(e, Language3Package::languageExtensionsShouldNotBeUsedQuery()) +select MacroUnwrapper::unwrapElement(e), e.getMessage() diff --git a/c/misra/src/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.ql b/c/misra/src/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.ql new file mode 100644 index 0000000000..00ef875985 --- /dev/null +++ b/c/misra/src/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/occurrence-of-undefined-behavior + * @name RULE-1-3: There shall be no occurrence of undefined or critical unspecified behavior + * @description Relying on undefined or unspecified behavior can result in unreliable programs. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-1-3 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.UndefinedBehavior + +from CUndefinedBehavior c +where not isExcluded(c, Language3Package::occurrenceOfUndefinedBehaviorQuery()) +select c, c.getReason() diff --git a/c/misra/src/rules/RULE-1-4/EmergentLanguageFeaturesUsed.ql b/c/misra/src/rules/RULE-1-4/EmergentLanguageFeaturesUsed.ql new file mode 100644 index 0000000000..a413b1c29a --- /dev/null +++ b/c/misra/src/rules/RULE-1-4/EmergentLanguageFeaturesUsed.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/emergent-language-features-used + * @name RULE-1-4: Emergent language features shall not be used + * @description Emergent language features may have unpredictable behavior and should not be used. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-1-4 + * maintainability + * readability + * external/misra/c/2012/amendment2 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Emergent + +from C11::EmergentLanguageFeature ef +where not isExcluded(ef, Language2Package::emergentLanguageFeaturesUsedQuery()) +select ef, "Usage of emergent language feature." diff --git a/c/misra/src/rules/RULE-1-5/CallToObsolescentFunctionGets.ql b/c/misra/src/rules/RULE-1-5/CallToObsolescentFunctionGets.ql new file mode 100644 index 0000000000..4994c4ea6e --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/CallToObsolescentFunctionGets.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/call-to-obsolescent-function-gets + * @name RULE-1-5: Disallowed usage of obsolescent function 'gets' + * @description The function 'gets' is an obsolescent language feature which was removed in C11. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-1-5 + * external/misra/c/2012/amendment3 + * security + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from FunctionCall fc +where + not isExcluded(fc, Language4Package::callToObsolescentFunctionGetsQuery()) and + fc.getTarget().hasGlobalOrStdName("gets") +select fc, "Call to obsolescent function 'gets'." diff --git a/c/misra/src/rules/RULE-1-5/FunctionTypesNotInPrototypeFormObsolete.ql b/c/misra/src/rules/RULE-1-5/FunctionTypesNotInPrototypeFormObsolete.ql new file mode 100644 index 0000000000..645285f438 --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/FunctionTypesNotInPrototypeFormObsolete.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/function-types-not-in-prototype-form-obsolete + * @name RULE-1-5: Function types shall be in prototype form with named parameters + * @description The use of non-prototype format parameter type declarators is an obsolescent + * language feature. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/misra/id/rule-1-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.functiontypesnotinprototypeformshared.FunctionTypesNotInPrototypeFormShared + +class FunctionTypesNotInPrototypeFormObsoleteQuery extends FunctionTypesNotInPrototypeFormSharedSharedQuery +{ + FunctionTypesNotInPrototypeFormObsoleteQuery() { + this = Language4Package::functionTypesNotInPrototypeFormObsoleteQuery() + } +} diff --git a/c/misra/src/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.ql b/c/misra/src/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.ql new file mode 100644 index 0000000000..9d10522ecf --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.ql @@ -0,0 +1,32 @@ +/** + * @id c/misra/invalid-define-or-undef-of-std-bool-macro + * @name RULE-1-5: Programs may not undefine or redefine the macros bool, true, or false + * @description Directives that undefine and/or redefine the standard boolean macros has been + * declared an obsolescent language feature since C99. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-1-5 + * maintainability + * readability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +string getABoolMacroName() { result = ["true", "false", "bool"] } + +from PreprocessorDirective directive, string opString, string macroName +where + not isExcluded(directive, Language4Package::invalidDefineOrUndefOfStdBoolMacroQuery()) and + macroName = getABoolMacroName() and + ( + macroName = directive.(Macro).getName() and + opString = "define" + or + macroName = directive.(PreprocessorUndef).getName() and + opString = "undefine" + ) +select directive, "Invalid " + opString + " of boolean standard macro '" + macroName + "'." diff --git a/c/misra/src/rules/RULE-1-5/MissingStaticSpecifierFuncRedeclarationObsolete.ql b/c/misra/src/rules/RULE-1-5/MissingStaticSpecifierFuncRedeclarationObsolete.ql new file mode 100644 index 0000000000..ba800885ef --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/MissingStaticSpecifierFuncRedeclarationObsolete.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/missing-static-specifier-func-redeclaration-obsolete + * @name RULE-1-5: If a function has internal linkage then all re-declarations shall include the static storage class + * @description Declaring a function with internal linkage without the static storage class + * specifier is an obselescent feature. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-1-5 + * readability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.missingstaticspecifierfunctionredeclarationshared.MissingStaticSpecifierFunctionRedeclarationShared + +class MissingStaticSpecifierFuncRedeclarationObsoleteQuery extends MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery +{ + MissingStaticSpecifierFuncRedeclarationObsoleteQuery() { + this = Language4Package::missingStaticSpecifierFuncRedeclarationObsoleteQuery() + } +} diff --git a/c/misra/src/rules/RULE-1-5/MissingStaticSpecifierObjectRedeclarationObsolete.ql b/c/misra/src/rules/RULE-1-5/MissingStaticSpecifierObjectRedeclarationObsolete.ql new file mode 100644 index 0000000000..9f9953aa6f --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/MissingStaticSpecifierObjectRedeclarationObsolete.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/missing-static-specifier-object-redeclaration-obsolete + * @name RULE-1-5: If an object has internal linkage then all re-declarations shall include the static storage class + * @description Declaring an identifier with internal linkage without the static storage class + * specifier is an obselescent feature. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-1-5 + * readability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.missingstaticspecifierobjectredeclarationshared.MissingStaticSpecifierObjectRedeclarationShared + +class MissingStaticSpecifierObjectRedeclarationObsoleteQuery extends MissingStaticSpecifierObjectRedeclarationSharedSharedQuery +{ + MissingStaticSpecifierObjectRedeclarationObsoleteQuery() { + this = Language4Package::missingStaticSpecifierObjectRedeclarationObsoleteQuery() + } +} diff --git a/c/misra/src/rules/RULE-1-5/SizeInReallocCallIsZero.ql b/c/misra/src/rules/RULE-1-5/SizeInReallocCallIsZero.ql new file mode 100644 index 0000000000..2b5cdaa851 --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/SizeInReallocCallIsZero.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/size-in-realloc-call-is-zero + * @name RULE-1-5: Size argument value in realloc call is equal zero + * @description Invoking realloc with a size argument set to zero is implementation-defined behavior + * and declared as an obsolete feature in C18. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-1-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.rangeanalysis.new.RangeAnalysis +import codingstandards.cpp.Realloc + +from ReallocCall call +where + not isExcluded(call, Language4Package::sizeInReallocCallIsZeroQuery()) and + call.sizeIsExactlyZero() +select call, + "Size argument '$@' may equal zero in realloc call, resulting in obsolescent and/or implementation-defined behavior.", + call.getSizeArgument(), call.getSizeArgument().toString() diff --git a/c/misra/src/rules/RULE-1-5/SizeInReallocCallMayBeZero.ql b/c/misra/src/rules/RULE-1-5/SizeInReallocCallMayBeZero.ql new file mode 100644 index 0000000000..3e883e45f4 --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/SizeInReallocCallMayBeZero.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/size-in-realloc-call-may-be-zero + * @name RULE-1-5: Size argument value in realloc call may equal zero + * @description Invoking realloc with a size argument set to zero is implementation-defined behavior + * and declared as an obsolete feature in C18. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/misra/id/rule-1-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Realloc + +from ReallocCall call +where + not isExcluded(call, Language4Package::sizeInReallocCallMayBeZeroQuery()) and + call.sizeMayBeZero() and + not call.sizeIsExactlyZero() +select call, + "Size argument '$@' equals zero in realloc call, resulting in obsolescent and/or implementation-defined behavior.", + call.getSizeArgument(), call.getSizeArgument().toString() diff --git a/c/misra/src/rules/RULE-1-5/UngetcCallOnStreamPositionZero.ql b/c/misra/src/rules/RULE-1-5/UngetcCallOnStreamPositionZero.ql new file mode 100644 index 0000000000..6a10c94030 --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/UngetcCallOnStreamPositionZero.ql @@ -0,0 +1,81 @@ +/** + * @id c/misra/ungetc-call-on-stream-position-zero + * @name RULE-1-5: Disallowed obsolescent usage of 'ungetc' on a file stream at position zero + * @description Calling the function 'ungetc' on a file stream with a position of zero is an + * obsolescent language feature. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-1-5 + * external/misra/c/2012/amendment3 + * security + * maintainability + * external/misra/obligation/required + */ + +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import semmle.code.cpp.controlflow.Dominance +import codingstandards.c.misra + +/** + * This is an inconclusive list, which is adequate, as RULE-21-3 provides + * assurance we won't have false negatives, or care too much about false + * positives. + */ +class MoveStreamPositionCall extends FunctionCall { + Expr streamArgument; + + MoveStreamPositionCall() { + getTarget().hasGlobalOrStdName("fgetc") and + streamArgument = getArgument(0) + or + getTarget().hasGlobalOrStdName("getc") and + streamArgument = getArgument(0) + or + getTarget().hasGlobalOrStdName("fget") and + streamArgument = getArgument(2) + or + getTarget().hasGlobalOrStdName("fscanf") and + streamArgument = getArgument(0) + or + getTarget().hasGlobalOrStdName("fsetpos") and + streamArgument = getArgument(0) + or + getTarget().hasGlobalOrStdName("fseek") and + streamArgument = getArgument(0) + or + getTarget().hasGlobalOrStdName("fread") and + streamArgument = getArgument(3) + } + + Expr getStreamArgument() { result = streamArgument } +} + +module FilePositionZeroFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { + node.asIndirectExpr().(FunctionCall).getTarget().hasGlobalOrStdName("fopen") + } + + predicate isSink(DataFlow::Node node) { + exists(FunctionCall fc | + fc.getTarget().hasGlobalOrStdName("ungetc") and + node.asIndirectExpr() = fc.getArgument(1) + ) + } + + predicate isBarrierIn(DataFlow::Node node) { + exists(MoveStreamPositionCall fc | node.asIndirectExpr() = fc.getStreamArgument()) + } +} + +module FilePositionZeroFlow = DataFlow::Global; + +import FilePositionZeroFlow::PathGraph + +from FilePositionZeroFlow::PathNode sink, FilePositionZeroFlow::PathNode source +where + not isExcluded(sink.getNode().asExpr(), Language4Package::ungetcCallOnStreamPositionZeroQuery()) and + FilePositionZeroFlow::flowPath(source, sink) +select sink.getNode(), source, sink, + "Obsolescent call to ungetc on file stream $@ at position zero.", source, source.toString() diff --git a/c/misra/src/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.ql b/c/misra/src/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.ql new file mode 100644 index 0000000000..e8abf1bbfb --- /dev/null +++ b/c/misra/src/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/use-of-obsolete-macro-atomic-var-init + * @name RULE-1-5: Disallowed usage of obsolete macro ATOMIC_VAR_INIT compiled as C18 + * @description The macro ATOMIC_VAR_INIT is has been declared an obsolescent language feature since + * C18. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-1-5 + * maintainability + * readability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from MacroInvocation invoke +where + not isExcluded(invoke, Language4Package::useOfObsoleteMacroAtomicVarInitQuery()) and + invoke.getMacroName() = "ATOMIC_VAR_INIT" +select invoke, + "Usage of macro ATOMIC_VAR_INIT() is declared obscelescent in C18, and discouraged in earlier C versions." diff --git a/c/misra/src/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.ql b/c/misra/src/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.ql new file mode 100644 index 0000000000..d06ba09f3d --- /dev/null +++ b/c/misra/src/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.ql @@ -0,0 +1,300 @@ +/** + * @id c/misra/operands-of-an-inappropriate-essential-type + * @name RULE-10-1: Operands shall not be of an inappropriate essential type + * @description Using an inappropriate essential type operand may lead to confusing or unexpected + * behavior when the operand is converted. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-1 + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +predicate hasComparableFloatValue(Expr e) { + exists(float value | + value = e.getValue().toFloat() or + value = -e.(UnaryMinusExpr).getOperand().getValue().toFloat() + | + value in [0.0, "Infinity".toFloat(), -"Infinity".toFloat()] + ) +} + +/** + * Holds if the operator `operator` has an operand `child` that is of an inappropriate essential type + * according to MISRA C 2012 Rule 10.1. + */ +predicate isInappropriateEssentialType( + Expr operator, Expr child, EssentialTypeCategory etc, int rationaleId +) { + etc = getEssentialTypeCategory(getEssentialType(child)) and + ( + child = operator.(ArrayExpr).getArrayOffset() and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyFloatingType(Real()) and + rationaleId = 1 + or + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = operator.(UnaryPlusExpr).getOperand() and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 5 + ) + or + child = operator.(UnaryMinusExpr).getOperand() and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 5 + or + etc = EssentiallyUnsignedType() and + rationaleId = 8 + ) + or + child = + [ + operator.(AddExpr).getAnOperand(), operator.(SubExpr).getAnOperand(), + operator.(IncrementOperation).getAnOperand(), operator.(DecrementOperation).getAnOperand(), + operator.(AssignAddExpr).getAnOperand(), operator.(AssignSubExpr).getAnOperand() + ] and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyEnumType() and + rationaleId = 5 + ) + or + child = + [operator.(IncrementOperation).getAnOperand(), operator.(DecrementOperation).getAnOperand()] and + ( + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = + [ + operator.(DivExpr).getAnOperand(), operator.(MulExpr).getAnOperand(), + operator.(AssignDivExpr).getAnOperand(), operator.(AssignMulExpr).getAnOperand() + ] and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 5 + ) + or + child = [operator.(RemExpr).getAnOperand(), operator.(AssignRemExpr).getAnOperand()] and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 5 + or + etc = EssentiallyFloatingType(Real()) and + rationaleId = 1 + or + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = operator.(RelationalOperation).getAnOperand() and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = operator.(EqualityOperation).getAnOperand() and + rationaleId = 10 and + not hasComparableFloatValue(operator.(EqualityOperation).getAnOperand()) and + etc = EssentiallyFloatingType(_) + or + child = [operator.(NotExpr).getAnOperand(), operator.(BinaryLogicalOperation).getAnOperand()] and + rationaleId = 2 and + ( + etc = EssentiallyCharacterType() + or + etc = EssentiallyEnumType() + or + etc = EssentiallySignedType() + or + etc = EssentiallyUnsignedType() + or + etc = EssentiallyFloatingType(_) + ) + or + child = + [ + operator.(LShiftExpr).getLeftOperand(), operator.(RShiftExpr).getLeftOperand(), + operator.(AssignLShiftExpr).getLValue(), operator.(AssignRShiftExpr).getLValue() + ] and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 6 // 5 also applies, but 6 is sufficient explanation + or + etc = EssentiallySignedType() and + rationaleId = 6 + or + etc = EssentiallyFloatingType(Real()) and + rationaleId = 1 + or + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = + [ + operator.(LShiftExpr).getRightOperand(), operator.(RShiftExpr).getRightOperand(), + operator.(AssignLShiftExpr).getRValue(), operator.(AssignRShiftExpr).getRValue() + ] and + // Integer constant non negative essentially signed types are allowed by exception + not (child.getValue().toInt() >= 0 and etc = EssentiallySignedType()) and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 7 + or + etc = EssentiallySignedType() and + rationaleId = 7 + or + etc = EssentiallyFloatingType(Real()) and + rationaleId = 1 + or + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = + [ + operator.(BinaryBitwiseOperation).getAnOperand(), + operator.(AssignBitwiseOperation).getAnOperand(), operator.(ComplementExpr).getAnOperand() + ] and + not operator instanceof LShiftExpr and + not operator instanceof RShiftExpr and + not operator instanceof AssignLShiftExpr and + not operator instanceof AssignRShiftExpr and + ( + etc = EssentiallyBooleanType() and + rationaleId = 3 + or + etc = EssentiallyCharacterType() and + rationaleId = 4 + or + etc = EssentiallyEnumType() and + rationaleId = 6 + or + etc = EssentiallySignedType() and + rationaleId = 6 + or + etc = EssentiallyFloatingType(Real()) and + rationaleId = 1 + or + etc = EssentiallyFloatingType(Complex()) and + rationaleId = 9 + ) + or + child = operator.(ConditionalExpr).getCondition() and + ( + etc = EssentiallyCharacterType() and + rationaleId = 2 + or + etc = EssentiallyEnumType() and + rationaleId = 2 + or + etc = EssentiallySignedType() and + rationaleId = 2 + or + etc = EssentiallyUnsignedType() and + rationaleId = 2 + or + etc = EssentiallyFloatingType(_) and + rationaleId = 2 + ) + ) +} + +string getRationaleMessage(int rationaleId, EssentialTypeCategory etc) { + rationaleId = 1 and + result = "Constraint violation from using an operand of essentially Floating type." + or + rationaleId = 2 and result = "Operand of " + etc + " type interpreted as a Boolean value." + or + rationaleId = 3 and result = "Operand of essentially Boolean type interpreted as a numeric value." + or + rationaleId = 4 and + result = "Operand of essentially Charater type interpreted as a numeric value." + or + rationaleId = 5 and + result = + "Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type." + or + rationaleId = 6 and + result = "Bitwise operator applied to operand of " + etc + " and not essentially unsigned." + or + rationaleId = 7 and + result = "Right hand operand of shift operator is " + etc + " and not not essentially unsigned." + or + rationaleId = 8 and + result = + "Operand of essentially Unsigned type will be converted to a signed type with the signedness dependent on the implemented size of int." + or + rationaleId = 9 and + result = "Use of essentially Complex type in this way is a constraint violation." + or + rationaleId = 10 and + result = + "Floating point numbers have inherent error such that comparisons should consider precision and not exact equality." +} + +from Expr operator, Expr child, int rationaleId, EssentialTypeCategory etc +where + not isExcluded(operator, EssentialTypesPackage::operandsOfAnInappropriateEssentialTypeQuery()) and + isInappropriateEssentialType(operator, child, etc, rationaleId) +select child, getRationaleMessage(rationaleId, etc) diff --git a/c/misra/src/rules/RULE-10-1/PointerTypeOnLogicalOperator.ql b/c/misra/src/rules/RULE-10-1/PointerTypeOnLogicalOperator.ql new file mode 100644 index 0000000000..b17f3710d5 --- /dev/null +++ b/c/misra/src/rules/RULE-10-1/PointerTypeOnLogicalOperator.ql @@ -0,0 +1,27 @@ +/** + * @id c/misra/pointer-type-on-logical-operator + * @name RULE-10-1: Logical operators should not be used with pointer types + * @description Using pointer types with logical operators should be avoid because it can cause + * confusing behavior. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-1 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from Expr logicalOperator, Expr operand +where + not isExcluded(operand, EssentialTypesPackage::pointerTypeOnLogicalOperatorQuery()) and + ( + operand = logicalOperator.(BinaryLogicalOperation).getAnOperand() + or + operand = logicalOperator.(NotExpr).getOperand() + ) and + operand.getType() instanceof PointerType +select operand, "Logical operators should not be used with pointer types." diff --git a/c/misra/src/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.ql b/c/misra/src/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.ql new file mode 100644 index 0000000000..0e98c6c570 --- /dev/null +++ b/c/misra/src/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.ql @@ -0,0 +1,43 @@ +/** + * @id c/misra/addition-subtraction-on-essentially-char-type + * @name RULE-10-2: Inappropriate use of essentially character type operands in addition and subtraction operations + * @description Expressions of essentially character type shall not be used inappropriately in + * addition and subtraction operations. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-10-2 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from BinaryArithmeticOperation addOrSub +where + not isExcluded(addOrSub, EssentialTypesPackage::additionSubtractionOnEssentiallyCharTypeQuery()) and + addOrSub.getOperator() = ["+", "-"] and + // At least one operand is essentially character type + ( + getEssentialTypeCategory(getEssentialType(addOrSub.getLeftOperand())) = + EssentiallyCharacterType() or + getEssentialTypeCategory(getEssentialType(addOrSub.getRightOperand())) = + EssentiallyCharacterType() + ) and + not ( + // But the overall essential type is not essentially character type + getEssentialTypeCategory(getEssentialType(addOrSub)) = EssentiallyCharacterType() + or + // Or this is a subtraction of one character with another, which is permitted, but produces an integral type + getEssentialTypeCategory(getEssentialType(addOrSub.getLeftOperand())) = + EssentiallyCharacterType() and + getEssentialTypeCategory(getEssentialType(addOrSub.getRightOperand())) = + EssentiallyCharacterType() and + addOrSub instanceof SubExpr + ) +select addOrSub, + "Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations" diff --git a/c/misra/src/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.ql b/c/misra/src/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.ql new file mode 100644 index 0000000000..7574531332 --- /dev/null +++ b/c/misra/src/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.ql @@ -0,0 +1,58 @@ +/** + * @id c/misra/assignment-of-incompatible-essential-type + * @name RULE-10-3: Do not assign to an object with a different essential type category or narrower essential type + * @description The value of an expression shall not be assigned to an object with a narrower + * essential type or of a different essential type category. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-3 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.c.misra.MisraExpressions + +from + Type lValueType, Expr rValue, Type lValueEssentialType, EssentialTypeCategory lValueTypeCategory, + Type rValueEssentialType, EssentialTypeCategory rValueTypeCategory, string message +where + not isExcluded(rValue, EssentialTypesPackage::assignmentOfIncompatibleEssentialTypeQuery()) and + isAssignmentToEssentialType(lValueType, rValue) and + lValueEssentialType = lValueType and + lValueTypeCategory = getEssentialTypeCategory(lValueEssentialType) and + rValueEssentialType = getEssentialType(rValue) and + rValueTypeCategory = getEssentialTypeCategory(rValueEssentialType) and + ( + not lValueTypeCategory = rValueTypeCategory and + message = + "Assignment of " + rValueTypeCategory + " value to an object of " + lValueTypeCategory + "." + or + lValueTypeCategory = rValueTypeCategory and + lValueEssentialType.getSize() < rValueEssentialType.getSize() and + message = + "Assignment of value of " + lValueTypeCategory + " of size " + rValueEssentialType.getSize() + + " bytes to an object narrower essential type of size " + lValueEssentialType.getSize() + + " bytes." + ) and + // Exception 1: Constant signed integers can be assigned to unsigned integers in certain cases + not exists(int const | + const = rValue.getValue().toInt() and + rValueTypeCategory = EssentiallySignedType() and + rValueEssentialType.getSize() <= any(IntType t | t.isSigned()).getSize() and + lValueTypeCategory = EssentiallyUnsignedType() and + const >= 0 and + const <= 2.pow(lValueEssentialType.getSize() * 8) + ) and + // Exception 4: Real floating point values may be assignable to complex floating point values + not ( + lValueTypeCategory = EssentiallyFloatingType(Complex()) and + rValueTypeCategory = EssentiallyFloatingType(Real()) and + lValueEssentialType.getSize() >= rValueEssentialType.getSize() * 2 + ) +select rValue, message diff --git a/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql b/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql new file mode 100644 index 0000000000..71681ad3bc --- /dev/null +++ b/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql @@ -0,0 +1,65 @@ +/** + * @id c/misra/operands-with-mismatched-essential-type-category + * @name RULE-10-4: Operator with usual arithmetic conversions shall have operands with the same essential type category + * @description Both operands of an operator in which the usual arithmetic conversions are performed + * shall have the same essential type category. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-4 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.c.misra.MisraExpressions + +from + OperationWithUsualArithmeticConversions op, Type leftOpEssentialType, Type rightOpEssentialType, + EssentialTypeCategory leftOpTypeCategory, EssentialTypeCategory rightOpTypeCategory, + string message +where + not isExcluded(op, EssentialTypesPackage::operandsWithMismatchedEssentialTypeCategoryQuery()) and + leftOpEssentialType = getEssentialType(op.getLeftOperand()) and + rightOpEssentialType = getEssentialType(op.getRightOperand()) and + leftOpTypeCategory = getEssentialTypeCategory(leftOpEssentialType) and + rightOpTypeCategory = getEssentialTypeCategory(rightOpEssentialType) and + ( + not leftOpTypeCategory = rightOpTypeCategory and + not ( + // Exception 3: Operands where both are real or complex floating types are allowed. + leftOpTypeCategory = EssentiallyFloatingType(_) and + rightOpTypeCategory = EssentiallyFloatingType(_) + ) and + message = + "The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: " + + leftOpTypeCategory + ", right operand: " + rightOpTypeCategory + ")." + or + // This is not technically covered by the rule, but the examples make it clear that this should + // be reported as non-compliant. + leftOpTypeCategory = EssentiallyEnumType() and + rightOpTypeCategory = EssentiallyEnumType() and + not leftOpEssentialType.getUnspecifiedType() = rightOpEssentialType.getUnspecifiedType() and + message = + "The operands of this operator with usual arithmetic conversions have mismatched essentially Enum types (left operand: " + + leftOpEssentialType + ", right operand: " + rightOpEssentialType + ")." + ) and + not ( + // Mismatch is permitted if using "+" or "+=" with one character operand and one integer operand + op.getOperator() = ["+", "+="] and + [leftOpTypeCategory, rightOpTypeCategory] = EssentiallyCharacterType() and + [leftOpTypeCategory, rightOpTypeCategory] = + [EssentiallyUnsignedType().(TEssentialTypeCategory), EssentiallySignedType()] + ) and + not ( + // Mismatch is permitted if using "+" or "+=" with one pointer operand and one integer operand + op.getOperator() = ["-", "-="] and + leftOpTypeCategory = EssentiallyCharacterType() and + rightOpTypeCategory = + [EssentiallyUnsignedType().(TEssentialTypeCategory), EssentiallySignedType()] + ) +select op, message diff --git a/c/misra/src/rules/RULE-10-5/InappropriateEssentialTypeCast.ql b/c/misra/src/rules/RULE-10-5/InappropriateEssentialTypeCast.ql new file mode 100644 index 0000000000..7cadb104ad --- /dev/null +++ b/c/misra/src/rules/RULE-10-5/InappropriateEssentialTypeCast.ql @@ -0,0 +1,85 @@ +/** + * @id c/misra/inappropriate-essential-type-cast + * @name RULE-10-5: The value of an expression should not be cast to an inappropriate essential type + * @description Casting the value of an expression to an inappropriate essential type may lead to + * confusing or unexpected behavior in the way the value is converted. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-5 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.c.misra.MisraExpressions + +predicate isIncompatibleEssentialTypeCast(EssentialTypeCategory fromCat, EssentialTypeCategory toCat) { + fromCat = EssentiallyBooleanType() and + toCat = + [ + EssentiallyCharacterType(), EssentiallyEnumType(), EssentiallySignedType(), + EssentiallyUnsignedType(), EssentiallyFloatingType(_).(TEssentialTypeCategory) + ] + or + fromCat = EssentiallyCharacterType() and + toCat = + [ + EssentiallyBooleanType(), EssentiallyEnumType(), + EssentiallyFloatingType(_).(TEssentialTypeCategory) + ] + or + fromCat = EssentiallyEnumType() and + toCat = [EssentiallyBooleanType(), EssentiallyEnumType().(TEssentialTypeCategory)] // NOTE only if different enum types + or + fromCat = EssentiallySignedType() and + toCat = [EssentiallyBooleanType(), EssentiallyEnumType().(TEssentialTypeCategory)] + or + fromCat = EssentiallyUnsignedType() and + toCat = [EssentiallyBooleanType(), EssentiallyEnumType().(TEssentialTypeCategory)] + or + fromCat = EssentiallyFloatingType(_) and + toCat = + [ + EssentiallyBooleanType(), EssentiallyCharacterType(), + EssentiallyEnumType().(TEssentialTypeCategory) + ] +} + +predicate isCastTypes( + Cast c, Type essentialFromType, Type essentialToType, EssentialTypeCategory fromCategory, + EssentialTypeCategory toCategory +) { + essentialFromType = getEssentialTypeBeforeConversions(c.getExpr()) and + essentialToType = c.getType() and + fromCategory = getEssentialTypeCategory(essentialFromType) and + toCategory = getEssentialTypeCategory(essentialToType) +} + +from + Cast c, Type essentialFromType, Type essentialToType, EssentialTypeCategory fromCategory, + EssentialTypeCategory toCategory, string message +where + not isExcluded(c, EssentialTypesPackage::inappropriateEssentialTypeCastQuery()) and + not c.isImplicit() and + isCastTypes(c, essentialFromType, essentialToType, fromCategory, toCategory) and + isIncompatibleEssentialTypeCast(fromCategory, toCategory) and + ( + if fromCategory = EssentiallyEnumType() and toCategory = EssentiallyEnumType() + then + // If from/to enum types, then only report if the essential types are different + not essentialToType = essentialFromType and + message = "Incompatible cast from " + essentialFromType + " to " + essentialToType + "." + else message = "Incompatible cast from " + fromCategory + " to " + toCategory + "." + ) and + not ( + // Exception - casting from `0` or `1` to a boolean type is permitted + (fromCategory = EssentiallySignedType() or fromCategory = EssentiallyUnsignedType()) and + toCategory = EssentiallyBooleanType() and + c.getExpr().getValue().toInt() = [0, 1] + ) +select c, message diff --git a/c/misra/src/rules/RULE-10-6/AssignmentToWiderEssentialType.ql b/c/misra/src/rules/RULE-10-6/AssignmentToWiderEssentialType.ql new file mode 100644 index 0000000000..8927e8570a --- /dev/null +++ b/c/misra/src/rules/RULE-10-6/AssignmentToWiderEssentialType.ql @@ -0,0 +1,30 @@ +/** + * @id c/misra/assignment-to-wider-essential-type + * @name RULE-10-6: The value of a composite expression shall not be assigned to an object with wider essential type + * @description Assigning a composite expression to an object with wider essential type can cause + * some unexpected conversions. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-6 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.c.misra.MisraExpressions + +from CompositeExpression ce, Type lValueType, Type compositeEssentialType +where + not isExcluded(ce, EssentialTypesPackage::assignmentToWiderEssentialTypeQuery()) and + isAssignmentToEssentialType(lValueType, ce) and + compositeEssentialType = getEssentialType(ce) and + lValueType.getSize() > compositeEssentialType.getSize() and + // Assignment to a different type category is prohibited by Rule 10.3, so we only report cases + // where the assignment is to the same type category. + getEssentialTypeCategory(lValueType) = getEssentialTypeCategory(compositeEssentialType) +select ce, "Assignment to wider essential type `" + lValueType.getName() + "`." diff --git a/c/misra/src/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.ql b/c/misra/src/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.ql new file mode 100644 index 0000000000..d674f78dc3 --- /dev/null +++ b/c/misra/src/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.ql @@ -0,0 +1,49 @@ +/** + * @id c/misra/implicit-conversion-of-composite-expression + * @name RULE-10-7: Implicit conversion of composite expression operand to wider essential type + * @description 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. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-7 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.c.misra.MisraExpressions + +bindingset[essentialTypeLeft, essentialTypeRight] +pragma[inline_late] +predicate isSameEssentialTypeCategory(Type essentialTypeLeft, Type essentialTypeRight) { + getEssentialTypeCategory(essentialTypeLeft) = getEssentialTypeCategory(essentialTypeRight) + or + // Complex and real floating types are considered interchangeable + getEssentialTypeCategory(essentialTypeLeft) = EssentiallyFloatingType(_) and + getEssentialTypeCategory(essentialTypeRight) = EssentiallyFloatingType(_) +} + +from + OperationWithUsualArithmeticConversions arith, CompositeExpression compositeOp, Expr otherOp, + Type compositeEssentialType, Type otherOpEssentialType +where + not isExcluded(arith, EssentialTypesPackage::implicitConversionOfCompositeExpressionQuery()) and + arith.getAnOperand() = compositeOp and + arith.getAnOperand() = otherOp and + not otherOp = compositeOp and + compositeEssentialType = getEssentialType(compositeOp) and + otherOpEssentialType = getEssentialType(otherOp) and + getEssentialSize(compositeEssentialType) < getEssentialSize(otherOpEssentialType) and + // Operands of a different type category in an operation with the usual arithmetic conversions is + // prohibited by Rule 10.4, so we only report cases here where the essential type categories are + // the same + isSameEssentialTypeCategory(compositeEssentialType, otherOpEssentialType) +select arith, + "Implicit conversion of $@ from " + compositeEssentialType + " to " + otherOpEssentialType, + compositeOp, "composite op" diff --git a/c/misra/src/rules/RULE-10-8/InappropriateCastOfCompositeExpression.ql b/c/misra/src/rules/RULE-10-8/InappropriateCastOfCompositeExpression.ql new file mode 100644 index 0000000000..b4d54bf2e8 --- /dev/null +++ b/c/misra/src/rules/RULE-10-8/InappropriateCastOfCompositeExpression.ql @@ -0,0 +1,48 @@ +/** + * @id c/misra/inappropriate-cast-of-composite-expression + * @name RULE-10-8: Composite expression explicitly casted to wider or different essential type + * @description The value of a composite expression shall not be cast to a different essential type + * category or a wider essential type. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-10-8 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.c.misra.MisraExpressions + +from + Cast c, CompositeExpression ce, Type castEssentialType, Type compositeExprEssentialType, + EssentialTypeCategory castTypeCategory, EssentialTypeCategory compositeTypeCategory, + string message +where + not isExcluded(ce, EssentialTypesPackage::inappropriateCastOfCompositeExpressionQuery()) and + c = ce.getExplicitlyConverted() and + compositeExprEssentialType = getEssentialTypeBeforeConversions(ce) and + castEssentialType = c.getType() and + castTypeCategory = getEssentialTypeCategory(castEssentialType) and + compositeTypeCategory = getEssentialTypeCategory(compositeExprEssentialType) and + ( + if + not castTypeCategory = compositeTypeCategory and + not ( + // Exception 2: Casts between real or complex floating types are allowed + castTypeCategory = EssentiallyFloatingType(_) and + compositeTypeCategory = EssentiallyFloatingType(_) + ) + then + message = + "Cast from " + compositeTypeCategory + " to " + castTypeCategory + " changes type category." + else ( + getEssentialSize(castEssentialType) > getEssentialSize(compositeExprEssentialType) and + message = "Cast from " + compositeTypeCategory + " to " + castTypeCategory + " widens type." + ) + ) +select ce, message diff --git a/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql b/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql index bfac04da6f..7678fc1d23 100644 --- a/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql +++ b/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql @@ -8,12 +8,13 @@ * @problem.severity error * @tags external/misra/id/rule-11-1 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers from CStyleCast cast, Type type, Type newType where diff --git a/c/misra/src/rules/RULE-11-10/AtomicQualifierAppliedToVoid.ql b/c/misra/src/rules/RULE-11-10/AtomicQualifierAppliedToVoid.ql new file mode 100644 index 0000000000..6440e84070 --- /dev/null +++ b/c/misra/src/rules/RULE-11-10/AtomicQualifierAppliedToVoid.ql @@ -0,0 +1,55 @@ +/** + * @id c/misra/atomic-qualifier-applied-to-void + * @name RULE-11-10: The _Atomic qualifier shall not be applied to the incomplete type void + * @description Conversions between types by using an _Atomic void type may result in undefined + * behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-11-10 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +class AtomicVoidType extends Type { + AtomicVoidType() { + hasSpecifier("atomic") and + getUnspecifiedType() instanceof VoidType + } +} + +predicate usesAtomicVoid(Type root) { + root instanceof AtomicVoidType + or + usesAtomicVoid(root.(DerivedType).getBaseType()) + or + usesAtomicVoid(root.(RoutineType).getReturnType()) + or + usesAtomicVoid(root.(RoutineType).getAParameterType()) + or + usesAtomicVoid(root.(FunctionPointerType).getReturnType()) + or + usesAtomicVoid(root.(FunctionPointerType).getAParameterType()) + or + usesAtomicVoid(root.(TypedefType).getBaseType()) +} + +class ExplicitType extends Type { + Element getDeclaration(string description) { + result.(DeclarationEntry).getType() = this and description = result.(DeclarationEntry).getName() + or + result.(CStyleCast).getType() = this and description = "Cast" + } +} + +from Element decl, ExplicitType explicitType, string elementDescription +where + not isExcluded(decl, Declarations9Package::atomicQualifierAppliedToVoidQuery()) and + decl = explicitType.getDeclaration(elementDescription) and + usesAtomicVoid(explicitType) +select decl, elementDescription + " declared with an atomic void type." diff --git a/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql b/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql index 007b43963b..5c16dc1afb 100644 --- a/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql +++ b/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql @@ -8,12 +8,13 @@ * @problem.severity error * @tags external/misra/id/rule-11-2 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.cpp.Type from Cast cast, Type type, Type newType diff --git a/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql b/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql index ede0a2834e..cf41cfafa5 100644 --- a/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql +++ b/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql @@ -9,12 +9,13 @@ * @problem.severity error * @tags external/misra/id/rule-11-3 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers from CStyleCast cast, Type baseTypeFrom, Type baseTypeTo where @@ -22,7 +23,11 @@ where baseTypeFrom = cast.getExpr().getType().(PointerToObjectType).getBaseType() and baseTypeTo = cast.getType().(PointerToObjectType).getBaseType() and // exception: cast to a char, signed char, or unsigned char is permitted - not baseTypeTo.stripType() instanceof CharType and + not ( + baseTypeTo.stripType() instanceof CharType and + // Exception does not apply to _Atomic types + not baseTypeFrom.hasSpecifier("atomic") + ) and ( ( baseTypeFrom.isVolatile() and not baseTypeTo.isVolatile() diff --git a/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql b/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql index 4071cf63b5..336f5d4643 100644 --- a/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql +++ b/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql @@ -8,20 +8,79 @@ * @problem.severity error * @tags external/misra/id/rule-11-4 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.Macro +import codingstandards.cpp.types.Pointers -from CStyleCast cast, Type typeFrom, Type typeTo +MacroInvocation getAMacroInvocation(CStyleCast cast) { result.getAnExpandedElement() = cast } + +Macro getPrimaryMacro(CStyleCast cast) { + exists(MacroInvocation mi | + mi = getAMacroInvocation(cast) and + not exists(MacroInvocation otherMi | + otherMi = getAMacroInvocation(cast) and otherMi.getParentInvocation() = mi + ) and + result = mi.getMacro() + ) +} + +Macro getNonFunctionPrimaryMacro(CStyleCast cast) { + result = getPrimaryMacro(cast) and + not result instanceof FunctionLikeMacro +} + +from + Locatable primaryLocation, CStyleCast cast, Type typeFrom, Type typeTo, string message, + string extraMessage, Locatable optionalPlaceholderLocation, string optionalPlaceholderMessage where - not isExcluded(cast, Pointers1Package::castBetweenObjectPointerAndDifferentObjectTypeQuery()) and + not isExcluded(cast, Pointers1Package::conversionBetweenPointerToObjectAndIntegerTypeQuery()) and typeFrom = cast.getExpr().getUnderlyingType() and typeTo = cast.getUnderlyingType() and - [typeFrom, typeTo] instanceof IntegralType and - [typeFrom, typeTo] instanceof PointerToObjectType and - not isNullPointerConstant(cast.getExpr()) -select cast, - "Cast performed between a pointer to object type and a pointer to an integer type." + ( + typeFrom instanceof PointerToObjectType and + typeTo instanceof IntegralType and + message = + "Cast from pointer to object type '" + typeFrom + "' to integer type '" + typeTo + "'" + + extraMessage + "." + or + typeFrom instanceof IntegralType and + typeTo instanceof PointerToObjectType and + message = + "Cast from integer type '" + typeFrom + "' to pointer to object type '" + typeTo + "'" + + extraMessage + "." + ) and + not isNullPointerConstant(cast.getExpr()) and + // If this alert is arising through a non-function-like macro expansion, flag the macro instead, to + // help make the alerts more manageable. We only do this for non-function-like macros because they + // cannot be context specific. + if exists(getNonFunctionPrimaryMacro(cast)) + then + primaryLocation = getNonFunctionPrimaryMacro(cast) and + extraMessage = "" and + optionalPlaceholderLocation = primaryLocation and + optionalPlaceholderMessage = "" + else ( + primaryLocation = cast and + // If the cast is in a macro expansion which is context specific, we still report the original + // location, but also add a link to the most specific macro that contains the cast, to aid + // validation. + if exists(getPrimaryMacro(cast)) + then + extraMessage = " from expansion of macro $@" and + exists(Macro m | + m = getPrimaryMacro(cast) and + optionalPlaceholderLocation = m and + optionalPlaceholderMessage = m.getName() + ) + else ( + extraMessage = "" and + optionalPlaceholderLocation = cast and + optionalPlaceholderMessage = "" + ) + ) +select primaryLocation, message, optionalPlaceholderLocation, optionalPlaceholderMessage diff --git a/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql b/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql index 3450f1ae90..b316b39a56 100644 --- a/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql +++ b/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql @@ -8,17 +8,18 @@ * @problem.severity error * @tags external/misra/id/rule-11-5 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers from Cast cast, VoidPointerType type, PointerToObjectType newType where not isExcluded(cast, Pointers1Package::conversionFromPointerToVoidIntoPointerToObjectQuery()) and - type = cast.getExpr().getUnderlyingType() and + type = cast.getExpr().getUnspecifiedType() and newType = cast.getUnderlyingType() and not isNullPointerConstant(cast.getExpr()) select cast, diff --git a/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql b/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql index b36d8dafb1..2293ede61e 100644 --- a/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql +++ b/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql @@ -8,12 +8,13 @@ * @problem.severity error * @tags external/misra/id/rule-11-6 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers from CStyleCast cast, Type typeFrom, Type typeTo where @@ -22,5 +23,5 @@ where typeTo = cast.getUnderlyingType() and [typeFrom, typeTo] instanceof ArithmeticType and [typeFrom, typeTo] instanceof VoidPointerType and - not isNullPointerConstant(cast.getExpr()) + not cast.getExpr() instanceof Zero select cast, "Cast performed between a pointer to void type and an arithmetic type." diff --git a/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql b/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql index 2aa49ae2a0..82ac620aa7 100644 --- a/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql +++ b/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql @@ -8,12 +8,13 @@ * @problem.severity error * @tags external/misra/id/rule-11-7 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers class MisraNonIntegerArithmeticType extends Type { MisraNonIntegerArithmeticType() { @@ -31,5 +32,4 @@ where typeTo = cast.getUnderlyingType() and [typeFrom, typeTo] instanceof MisraNonIntegerArithmeticType and [typeFrom, typeTo] instanceof PointerToObjectType -select cast, - "Cast performed between a pointer to void type and a non-integer arithmetic type." \ No newline at end of file +select cast, "Cast performed between a pointer to object type and a non-integer arithmetic type." diff --git a/c/misra/src/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.ql b/c/misra/src/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.ql index 17b0df1a0e..c0f447d5b5 100644 --- a/c/misra/src/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.ql +++ b/c/misra/src/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-11-8 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -23,5 +24,9 @@ where baseTypeFrom.isVolatile() and not baseTypeTo.isVolatile() and qualificationName = "volatile" or baseTypeFrom.isConst() and not baseTypeTo.isConst() and qualificationName = "const" + or + baseTypeFrom.hasSpecifier("atomic") and + not baseTypeTo.hasSpecifier("atomic") and + qualificationName = "atomic" ) select cast, "Cast of pointer removes " + qualificationName + " qualification from its base type." diff --git a/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql b/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql index 844229de2f..28b256e85c 100644 --- a/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql +++ b/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql @@ -7,27 +7,37 @@ * @problem.severity error * @tags external/misra/id/rule-11-9 * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.cpp.Type - from Zero zero, Expr e, string type where not isExcluded(zero, Pointers1Package::macroNullNotUsedAsIntegerNullPointerConstantQuery()) and - // exclude the base-case (NULL macros and void pointer casts) - not isNullPointerConstant(zero) and + // Exclude the base-case (NULL macros and void pointer casts) + // Note: we cannot use the isNullPointerConstant predicate here because it permits + // the use of `0` without casting, which is prohibited here. + not ( + zero.findRootCause() instanceof NullMacro + or + // integer constant `0` explicitly cast to void pointer + exists(Conversion c | c = zero.getConversion() | + not c.isImplicit() and + c.getUnderlyingType() instanceof VoidPointerType + ) + ) and ( // ?: operator exists(ConditionalExpr parent | ( - parent.getThen().getAChild*() = zero and parent.getElse().getType() instanceof PointerType + parent.getThen() = zero and parent.getElse().getType() instanceof PointerType or - parent.getElse().getAChild*() = zero and parent.getThen().getType() instanceof PointerType + parent.getElse() = zero and parent.getThen().getType() instanceof PointerType ) and // exclude a common conditional pattern used in macros such as 'assert' not parent.isInMacroExpansion() and diff --git a/c/misra/src/rules/RULE-12-1/ImplicitPrecedenceOfOperatorsInExpression.ql b/c/misra/src/rules/RULE-12-1/ImplicitPrecedenceOfOperatorsInExpression.ql index 005fffa32d..134068463c 100644 --- a/c/misra/src/rules/RULE-12-1/ImplicitPrecedenceOfOperatorsInExpression.ql +++ b/c/misra/src/rules/RULE-12-1/ImplicitPrecedenceOfOperatorsInExpression.ql @@ -9,6 +9,7 @@ * @problem.severity warning * @tags external/misra/id/rule-12-1 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-12-1/UnenclosedSizeofOperand.ql b/c/misra/src/rules/RULE-12-1/UnenclosedSizeofOperand.ql index 8975e7dff7..0081de320c 100644 --- a/c/misra/src/rules/RULE-12-1/UnenclosedSizeofOperand.ql +++ b/c/misra/src/rules/RULE-12-1/UnenclosedSizeofOperand.ql @@ -9,6 +9,7 @@ * @problem.severity warning * @tags external/misra/id/rule-12-1 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-12-2/RightHandOperandOfAShiftRange.ql b/c/misra/src/rules/RULE-12-2/RightHandOperandOfAShiftRange.ql new file mode 100644 index 0000000000..da7a0f181e --- /dev/null +++ b/c/misra/src/rules/RULE-12-2/RightHandOperandOfAShiftRange.ql @@ -0,0 +1,71 @@ +/** + * @id c/misra/right-hand-operand-of-a-shift-range + * @name RULE-12-2: The right operand of a shift shall be smaller then the width in bits of the left operand + * @description The right hand operand of a shift operator shall lie in the range zero to one less + * than the width in bits of the essential type of the left hand operand. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-12-2 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +class ShiftExpr extends BinaryBitwiseOperation { + ShiftExpr() { this instanceof LShiftExpr or this instanceof RShiftExpr } +} + +MacroInvocation getAMacroInvocation(ShiftExpr se) { result.getAnExpandedElement() = se } + +Macro getPrimaryMacro(ShiftExpr se) { + exists(MacroInvocation mi | + mi = getAMacroInvocation(se) and + not exists(MacroInvocation otherMi | + otherMi = getAMacroInvocation(se) and otherMi.getParentInvocation() = mi + ) and + result = mi.getMacro() + ) +} + +from + ShiftExpr e, Expr right, int max_val, float lowerBound, float upperBound, Type essentialType, + string extraMessage, Locatable optionalPlaceholderLocation, string optionalPlaceholderMessage +where + not isExcluded(right, Contracts7Package::rightHandOperandOfAShiftRangeQuery()) and + right = e.getRightOperand().getFullyConverted() and + essentialType = getEssentialType(e.getLeftOperand()) and + max_val = (8 * essentialType.getSize()) - 1 and + upperBound = upperBound(right) and + lowerBound = lowerBound(right) and + ( + lowerBound < 0 or + upperBound > max_val + ) and + // If this shift happens inside a macro, then report the macro as well + // for easier validation + ( + if exists(getPrimaryMacro(e)) + then + extraMessage = " from expansion of macro $@" and + exists(Macro m | + m = getPrimaryMacro(e) and + optionalPlaceholderLocation = m and + optionalPlaceholderMessage = m.getName() + ) + else ( + extraMessage = "" and + optionalPlaceholderLocation = e and + optionalPlaceholderMessage = "" + ) + ) +select right, + "The possible range of the right operand of the shift operator (" + lowerBound + ".." + upperBound + + ") is outside the the valid shift range (0.." + max_val + + ") for the essential type of the left operand (" + essentialType + ")" + extraMessage + ".", + optionalPlaceholderLocation, optionalPlaceholderMessage diff --git a/c/misra/src/rules/RULE-12-3/CommaOperatorShouldNotBeUsed.ql b/c/misra/src/rules/RULE-12-3/CommaOperatorShouldNotBeUsed.ql index d6d00044dd..bccb382804 100644 --- a/c/misra/src/rules/RULE-12-3/CommaOperatorShouldNotBeUsed.ql +++ b/c/misra/src/rules/RULE-12-3/CommaOperatorShouldNotBeUsed.ql @@ -7,6 +7,7 @@ * @problem.severity recommendation * @tags external/misra/id/rule-12-3 * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ @@ -15,7 +16,5 @@ import codingstandards.c.misra import codingstandards.cpp.rules.commaoperatorused.CommaOperatorUsed class CommaOperatorShouldNotBeUsedQuery extends CommaOperatorUsedSharedQuery { - CommaOperatorShouldNotBeUsedQuery() { - this = BannedPackage::commaOperatorShouldNotBeUsedQuery() - } + CommaOperatorShouldNotBeUsedQuery() { this = BannedPackage::commaOperatorShouldNotBeUsedQuery() } } diff --git a/c/misra/src/rules/RULE-12-4/ConstantUnsignedIntegerExpressionsWrapAround.ql b/c/misra/src/rules/RULE-12-4/ConstantUnsignedIntegerExpressionsWrapAround.ql new file mode 100644 index 0000000000..1ebbf184bb --- /dev/null +++ b/c/misra/src/rules/RULE-12-4/ConstantUnsignedIntegerExpressionsWrapAround.ql @@ -0,0 +1,30 @@ +/** + * @id c/misra/constant-unsigned-integer-expressions-wrap-around + * @name RULE-12-4: Evaluation of constant expressions should not lead to unsigned integer wrap-around + * @description Unsigned integer expressions do not strictly overflow, but instead wrap around in a + * modular way. Any constant unsigned integer expressions that in effect "overflow" + * will not be detected by the compiler. Although there may be good reasons at run-time + * to rely on the modular arithmetic provided by unsigned integer types, the reasons + * for using it at compile-time to evaluate a constant expression are less obvious. Any + * instance of an unsigned integer constant expression wrapping around is therefore + * likely to indicate a programming error. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-12-4 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.constantunsignedintegerexpressionswraparound.ConstantUnsignedIntegerExpressionsWrapAround + +class ConstantUnsignedIntegerExpressionsWrapAroundQuery extends ConstantUnsignedIntegerExpressionsWrapAroundSharedQuery +{ + ConstantUnsignedIntegerExpressionsWrapAroundQuery() { + this = IntegerOverflowPackage::constantUnsignedIntegerExpressionsWrapAroundQuery() + } +} diff --git a/c/misra/src/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.ql b/c/misra/src/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.ql new file mode 100644 index 0000000000..2e080419e1 --- /dev/null +++ b/c/misra/src/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/sizeof-operator-used-on-array-type-param + * @name RULE-12-5: The sizeof operator should not be used on an array type function parameter + * @description Using sizeof operator on an array type function parameter leads to unintended + * results. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-12-5 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra + +from SizeofExprOperator sizeof +where + not isExcluded(sizeof, Types1Package::sizeofOperatorUsedOnArrayTypeParamQuery()) and + exists(Parameter param | + sizeof.getExprOperand().(VariableAccess).getTarget() = param and + param.getType() instanceof ArrayType + ) +select sizeof, + "The sizeof operator is called on an array-type parameter " + sizeof.getExprOperand() + "." diff --git a/c/misra/src/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.ql b/c/misra/src/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.ql new file mode 100644 index 0000000000..5085e5dc7b --- /dev/null +++ b/c/misra/src/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.ql @@ -0,0 +1,40 @@ +/** + * @id c/misra/atomic-aggregate-object-directly-accessed + * @name RULE-12-6: Structure and union members of atomic objects shall not be directly accessed + * @description Accessing a member of an atomic structure or union results in undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-12-6 + * external/misra/c/2012/amendment4 + * correctness + * concurrency + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from Expr expr, Field field +where + not isExcluded(expr, Concurrency6Package::atomicAggregateObjectDirectlyAccessedQuery()) and + not expr.isUnevaluated() and + ( + exists(FieldAccess fa | + expr = fa and + fa.getQualifier().getType().hasSpecifier("atomic") and + field = fa.getTarget() + ) + or + exists(PointerFieldAccess fa | + expr = fa and + fa.getQualifier() + .getType() + .stripTopLevelSpecifiers() + .(PointerType) + .getBaseType() + .hasSpecifier("atomic") and + field = fa.getTarget() + ) + ) +select expr, "Invalid access to member '$@' on atomic struct or union.", field, field.getName() diff --git a/c/misra/src/rules/RULE-13-1/InitializerListsContainPersistentSideEffects.ql b/c/misra/src/rules/RULE-13-1/InitializerListsContainPersistentSideEffects.ql index 3c97fc06ca..69ecbede58 100644 --- a/c/misra/src/rules/RULE-13-1/InitializerListsContainPersistentSideEffects.ql +++ b/c/misra/src/rules/RULE-13-1/InitializerListsContainPersistentSideEffects.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-13-1 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -25,8 +26,8 @@ from AggregateLiteral initList, SideEffect effect where not isExcluded(initList, SideEffects1Package::initializerListsContainPersistentSideEffectsQuery()) and ( - initList.(ArrayOrVectorAggregateLiteral).getElementExpr(_) = effect + initList.(ArrayOrVectorAggregateLiteral).getAnElementExpr(_) = effect or - initList.(ClassAggregateLiteral).getFieldExpr(_) = effect + initList.(ClassAggregateLiteral).getAFieldExpr(_) = effect ) select initList, "Initializer list constains persistent $@", effect, "side effect" diff --git a/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql b/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql new file mode 100644 index 0000000000..86756668a8 --- /dev/null +++ b/c/misra/src/rules/RULE-13-2/UnsequencedAtomicReads.ql @@ -0,0 +1,117 @@ +/** + * @id c/misra/unsequenced-atomic-reads + * @name RULE-13-2: The value of an atomic variable shall not depend on the evaluation order of interleaved threads + * @description The value of an atomic variable shall not depend on evaluation order and + * interleaving of threads. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-13-2 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import semmle.code.cpp.dataflow.TaintTracking +import codingstandards.c.misra +import codingstandards.c.Ordering +import codingstandards.c.orderofevaluation.VariableAccessOrdering +import codingstandards.cpp.StdFunctionOrMacro + +class AtomicAccessInFullExpressionOrdering extends Ordering::Configuration { + AtomicAccessInFullExpressionOrdering() { this = "AtomicAccessInFullExpressionOrdering" } + + override predicate isCandidate(Expr e1, Expr e2) { + exists(AtomicVariableAccess a, AtomicVariableAccess b, FullExpr e | a = e1 and b = e2 | + a.getTarget() = b.getTarget() and + a.getARead().(ConstituentExpr).getFullExpr() = e and + b.getARead().(ConstituentExpr).getFullExpr() = e and + not a = b + ) + } +} + +/** + * A read of a variable specified as `_Atomic`. + * + * Note, it may be accessed directly, or by passing its address into the std atomic functions. + */ +class AtomicVariableAccess extends VariableAccess { + AtomicVariableAccess() { getTarget().getType().hasSpecifier("atomic") } + + /* Get the `atomic_load()` call this VarAccess occurs in. */ + Expr getAtomicFunctionRead() { + exists(AddressOfExpr addrParent, AtomicReadOrWriteCall fc | + fc.getName().matches("atomic_load%") and + // StdFunctionOrMacro arguments are not necessarily reliable, so we look for any AddressOfExpr + // that is an argument to a call to `atomic_load`. + addrParent = fc.getArgument(0) and + addrParent.getAnOperand() = this and + result = fc.getExpr() + ) + } + + /* Get the `atomic_store()` call this VarAccess occurs in. */ + Expr getAtomicFunctionWrite(Expr storedValue) { + exists(AddressOfExpr addrParent, AtomicReadOrWriteCall fc | + addrParent = fc.getArgument(0) and + addrParent.getAnOperand() = this and + result = fc.getExpr() and + ( + fc.getName().matches(["%store%", "%exchange%", "%fetch_%"]) and + not fc.getName().matches("%compare%") and + storedValue = fc.getArgument(1) + or + fc.getName().matches(["%compare%"]) and + storedValue = fc.getArgument(2) + ) + ) + } + + /** + * Gets an assigned expr, either in the form `x = ` or `atomic_store(&x, )`. + */ + Expr getAnAssignedExpr() { + exists(getAtomicFunctionWrite(result)) + or + exists(AssignExpr assign | + assign.getLValue() = this and + result = assign.getRValue() + ) + } + + /** + * Gets the expression holding this variable access, either in the form `x` or `atomic_read(&x)`. + */ + Expr getARead() { + result = getAtomicFunctionRead() + or + result = this + } +} + +from + AtomicAccessInFullExpressionOrdering config, FullExpr e, Variable v, AtomicVariableAccess va1, + AtomicVariableAccess va2, Expr va1Read, Expr va2Read +where + not isExcluded(e, SideEffects3Package::unsequencedAtomicReadsQuery()) and + va1Read = va1.getARead() and + va2Read = va2.getARead() and + e = va1Read.(ConstituentExpr).getFullExpr() and + // Careful here. The `VariableAccess` in a pair of atomic function calls may not be unsequenced, + // for instance in gcc where atomic functions expand to StmtExprs, which have clear sequences. + // In this case, the result of `getARead()` for a pair of atomic function calls may be + // unsequenced even though the `VariableAccess`es within those calls are not. + config.isUnsequenced(va1Read, va2Read) and + v = va1.getTarget() and + v = va2.getTarget() and + // Exclude cases where the variable is assigned a value tainted by the other variable access. + not exists(Expr write | + write = va1.getAnAssignedExpr() and + TaintTracking::localTaint(DataFlow::exprNode(va2.getARead()), DataFlow::exprNode(write)) + ) and + // Impose an ordering, show the first access. + va1.getLocation().isBefore(va2.getLocation(), _) +select e, "Atomic variable $@ has a $@ that is unsequenced with $@.", v, v.getName(), va1, + "previous read", va2, "another read" diff --git a/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql b/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql new file mode 100644 index 0000000000..90b0315e88 --- /dev/null +++ b/c/misra/src/rules/RULE-13-2/UnsequencedSideEffects.ql @@ -0,0 +1,249 @@ +/** + * @id c/misra/unsequenced-side-effects + * @name RULE-13-2: The value of an expression and its persistent side effects depend on its evaluation order + * @description The value of an expression and its persistent side effects are depending on the + * evaluation order resulting in unpredictable behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-13-2 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Ordering +import codingstandards.c.SideEffects + +class VariableEffectOrAccess extends Expr { + VariableEffectOrAccess() { + this instanceof VariableEffect or + this instanceof VariableAccess + } +} + +pragma[noinline] +predicate partOfFullExpr(VariableEffectOrAccess e, FullExpr fe) { + ( + exists(VariableEffect ve | e = ve and ve.getAnAccess() = fe.getAChild+() and not ve.isPartial()) + or + e.(VariableAccess) = fe.getAChild+() + ) +} + +class ConstituentExprOrdering extends Ordering::Configuration { + ConstituentExprOrdering() { this = "ConstituentExprOrdering" } + + override predicate isCandidate(Expr e1, Expr e2) { + exists(FullExpr fe | + partOfFullExpr(e1, fe) and + partOfFullExpr(e2, fe) + ) + } +} + +predicate sameFullExpr(FullExpr fe, VariableAccess va1, VariableAccess va2) { + partOfFullExpr(va1, fe) and + partOfFullExpr(va2, fe) and + va1 != va2 and + exists(Variable v1, Variable v2 | + // Use `pragma[only_bind_into]` to prevent CP between variable accesses. + va1.getTarget() = pragma[only_bind_into](v1) and va2.getTarget() = pragma[only_bind_into](v2) + | + v1.isVolatile() and v2.isVolatile() + or + not (v1.isVolatile() and v2.isVolatile()) and + v1 = v2 + ) +} + +int getLeafCount(LeftRightOperation bop) { + if + not bop.getLeftOperand() instanceof BinaryOperation and + not bop.getRightOperand() instanceof BinaryOperation + then result = 2 + else + if + bop.getLeftOperand() instanceof BinaryOperation and + not bop.getRightOperand() instanceof BinaryOperation + then result = 1 + getLeafCount(bop.getLeftOperand()) + else + if + not bop.getLeftOperand() instanceof BinaryOperation and + bop.getRightOperand() instanceof BinaryOperation + then result = 1 + getLeafCount(bop.getRightOperand()) + else result = getLeafCount(bop.getLeftOperand()) + getLeafCount(bop.getRightOperand()) +} + +class LeftRightOperation extends Expr { + LeftRightOperation() { + this instanceof BinaryOperation or + this instanceof AssignOperation or + this instanceof AssignExpr + } + + Expr getLeftOperand() { + result = this.(BinaryOperation).getLeftOperand() + or + result = this.(AssignOperation).getLValue() + or + result = this.(AssignExpr).getLValue() + } + + Expr getRightOperand() { + result = this.(BinaryOperation).getRightOperand() + or + result = this.(AssignOperation).getRValue() + or + result = this.(AssignExpr).getRValue() + } + + Expr getAnOperand() { + result = getLeftOperand() or + result = getRightOperand() + } +} + +int getOperandIndexIn(FullExpr fullExpr, Expr operand) { + result = getOperandIndex(fullExpr, operand) + or + fullExpr.(Call).getArgument(result).getAChild*() = operand +} + +int getOperandIndex(LeftRightOperation binop, Expr operand) { + if operand = binop.getAnOperand() + then + operand = binop.getLeftOperand() and + result = 0 + or + operand = binop.getRightOperand() and + result = getLeafCount(binop.getLeftOperand()) + 1 + or + operand = binop.getRightOperand() and + not binop.getLeftOperand() instanceof LeftRightOperation and + result = 1 + else ( + // Child of left operand that is a binary operation. + result = getOperandIndex(binop.getLeftOperand(), operand) + or + // Child of left operand that is not a binary operation. + result = 0 and + not binop.getLeftOperand() instanceof LeftRightOperation and + binop.getLeftOperand().getAChild+() = operand + or + // Child of right operand and both left and right operands are binary operations. + result = + getLeafCount(binop.getLeftOperand()) + getOperandIndex(binop.getRightOperand(), operand) + or + // Child of right operand and left operand is not a binary operation. + result = 1 + getOperandIndex(binop.getRightOperand(), operand) and + not binop.getLeftOperand() instanceof LeftRightOperation + or + // Child of right operand that is not a binary operation and the left operand is a binary operation. + result = getLeafCount(binop.getLeftOperand()) + 1 and + binop.getRightOperand().getAChild+() = operand and + not binop.getRightOperand() instanceof LeftRightOperation + or + // Child of right operand that is not a binary operation and the left operand is not a binary operation. + result = 1 and + not binop.getLeftOperand() instanceof LeftRightOperation and + not binop.getRightOperand() instanceof LeftRightOperation and + binop.getRightOperand().getAChild+() = operand + ) +} + +predicate inConditionalThen(ConditionalExpr ce, Expr e) { + e = ce.getThen() + or + exists(Expr parent | + inConditionalThen(ce, parent) and + parent.getAChild() = e + ) +} + +predicate inConditionalElse(ConditionalExpr ce, Expr e) { + e = ce.getElse() + or + exists(Expr parent | + inConditionalElse(ce, parent) and + parent.getAChild() = e + ) +} + +predicate isUnsequencedEffect( + ConstituentExprOrdering orderingConfig, FullExpr fullExpr, VariableEffect variableEffect1, + VariableAccess va1, VariableAccess va2, Locatable placeHolder, string label +) { + // The two access are scoped to the same full expression. + sameFullExpr(fullExpr, va1, va2) and + // We are only interested in effects that change an object, + // i.e., exclude patterns suchs as `b->data[b->cursor++]` where `b` is considered modified and read or `foo.bar = 1` where `=` modifies to both `foo` and `bar`. + not variableEffect1.isPartial() and + variableEffect1.getAnAccess() = va1 and + ( + exists(VariableEffect variableEffect2 | + not variableEffect2.isPartial() and + variableEffect2.getAnAccess() = va2 and + // If the effect is not local (happens in a different function) we use the call with the access as a proxy. + ( + va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and + va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and + orderingConfig.isUnsequenced(variableEffect1, variableEffect2) + or + va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and + not va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and + exists(Call call | + call.getAnArgument() = va2 and call.getEnclosingStmt() = va1.getEnclosingStmt() + | + orderingConfig.isUnsequenced(variableEffect1, call) + ) + or + not va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and + va2.getEnclosingStmt() = variableEffect2.getEnclosingStmt() and + exists(Call call | + call.getAnArgument() = va1 and call.getEnclosingStmt() = va2.getEnclosingStmt() + | + orderingConfig.isUnsequenced(call, variableEffect2) + ) + ) and + // Break the symmetry of the ordering relation by requiring that the first expression is located before the second. + // This builds upon the assumption that the expressions are part of the same full expression as specified in the ordering configuration. + getOperandIndexIn(fullExpr, va1) < getOperandIndexIn(fullExpr, va2) and + placeHolder = variableEffect2 and + label = "side effect" + ) + or + placeHolder = va2 and + label = "read" and + not exists(VariableEffect variableEffect2 | variableEffect1 != variableEffect2 | + variableEffect2.getAnAccess() = va2 + ) and + ( + va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and + orderingConfig.isUnsequenced(variableEffect1, va2) + or + not va1.getEnclosingStmt() = variableEffect1.getEnclosingStmt() and + exists(Call call | + call.getAnArgument() = va1 and call.getEnclosingStmt() = va2.getEnclosingStmt() + | + orderingConfig.isUnsequenced(call, va2) + ) + ) and + // The read is not used to compute the effect on the variable. + // E.g., exclude x = x + 1 + not variableEffect1.getAChild+() = va2 + ) and + // Both are evaluated + not exists(ConditionalExpr ce | inConditionalThen(ce, va1) and inConditionalElse(ce, va2)) +} + +from + ConstituentExprOrdering orderingConfig, FullExpr fullExpr, VariableEffect variableEffect1, + VariableAccess va1, VariableAccess va2, Locatable placeHolder, string label +where + not isExcluded(fullExpr, SideEffects3Package::unsequencedSideEffectsQuery()) and + isUnsequencedEffect(orderingConfig, fullExpr, variableEffect1, va1, va2, placeHolder, label) +select fullExpr, "The expression contains unsequenced $@ to $@ and $@ to $@.", variableEffect1, + "side effect", va1, va1.getTarget().getName(), placeHolder, label, va2, va2.getTarget().getName() diff --git a/c/misra/src/rules/RULE-13-3/SideEffectAndCrementInFullExpression.ql b/c/misra/src/rules/RULE-13-3/SideEffectAndCrementInFullExpression.ql index 3dd03120c8..173827e04e 100644 --- a/c/misra/src/rules/RULE-13-3/SideEffectAndCrementInFullExpression.ql +++ b/c/misra/src/rules/RULE-13-3/SideEffectAndCrementInFullExpression.ql @@ -10,6 +10,7 @@ * @tags external/misra/id/rule-13-3 * readability * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql b/c/misra/src/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql index 6938f8e627..c840947b1f 100644 --- a/c/misra/src/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql +++ b/c/misra/src/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql @@ -9,14 +9,17 @@ * @tags external/misra/id/rule-13-4 * correctness * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ import cpp import codingstandards.c.misra +import codingstandards.cpp.rules.resultofanassignmentoperatorshouldnotbeused.ResultOfAnAssignmentOperatorShouldNotBeUsed -from AssignExpr e -where - not isExcluded(e, SideEffects1Package::resultOfAnAssignmentOperatorShouldNotBeUsedQuery()) and - not exists(ExprStmt s | s.getExpr() = e) -select e, "Use of an assignment operator's result." +class ResultOfAnAssignmentOperatorShouldNotBeUsedQuery extends ResultOfAnAssignmentOperatorShouldNotBeUsedSharedQuery +{ + ResultOfAnAssignmentOperatorShouldNotBeUsedQuery() { + this = SideEffects1Package::resultOfAnAssignmentOperatorShouldNotBeUsedQuery() + } +} diff --git a/c/misra/src/rules/RULE-13-5/PossibleSuppressedSideEffectInLogicOperatorOperand.ql b/c/misra/src/rules/RULE-13-5/PossibleSuppressedSideEffectInLogicOperatorOperand.ql index 90faf9ec23..9a5b7b2b7b 100644 --- a/c/misra/src/rules/RULE-13-5/PossibleSuppressedSideEffectInLogicOperatorOperand.ql +++ b/c/misra/src/rules/RULE-13-5/PossibleSuppressedSideEffectInLogicOperatorOperand.ql @@ -9,6 +9,7 @@ * @problem.severity error * @tags external/misra/id/rule-13-5 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-13-6/SizeofOperandWithSideEffect.ql b/c/misra/src/rules/RULE-13-6/SizeofOperandWithSideEffect.ql index 10317b1169..759ad9b06a 100644 --- a/c/misra/src/rules/RULE-13-6/SizeofOperandWithSideEffect.ql +++ b/c/misra/src/rules/RULE-13-6/SizeofOperandWithSideEffect.ql @@ -8,7 +8,8 @@ * @problem.severity error * @tags external/misra/id/rule-13-6 * correctness - * external/misra/obligation/mandatory + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required */ import cpp diff --git a/c/misra/src/rules/RULE-14-1/LoopOverEssentiallyFloatType.ql b/c/misra/src/rules/RULE-14-1/LoopOverEssentiallyFloatType.ql new file mode 100644 index 0000000000..3d351c898e --- /dev/null +++ b/c/misra/src/rules/RULE-14-1/LoopOverEssentiallyFloatType.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/loop-over-essentially-float-type + * @name RULE-14-1: A loop counter shall not have essentially floating type + * @description A floating point loop counter can cause confusing behavior when incremented. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-14-1 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.cpp.Loops + +from ForStmt forLoop, Variable loopIterationVariable +where + not isExcluded(loopIterationVariable, EssentialTypesPackage::loopOverEssentiallyFloatTypeQuery()) and + getAnIterationVariable(forLoop) = loopIterationVariable and + getEssentialTypeCategory(loopIterationVariable.getType()) = EssentiallyFloatingType(_) +select loopIterationVariable, + "Loop iteration variable " + loopIterationVariable.getName() + " is essentially Floating type." diff --git a/c/misra/src/rules/RULE-14-2/ForLoopNotWellFormed.ql b/c/misra/src/rules/RULE-14-2/ForLoopNotWellFormed.ql new file mode 100644 index 0000000000..7b3dc3c8dc --- /dev/null +++ b/c/misra/src/rules/RULE-14-2/ForLoopNotWellFormed.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/for-loop-not-well-formed + * @name RULE-14-2: A for loop shall be well-formed + * @description A well-formed for loop makes code easier to review. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-14-2 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Loops + +from ForStmt for, Element reasonLocation, string reason, string reasonLabel +where + not isExcluded(for, Statements4Package::forLoopNotWellFormedQuery()) and + isInvalidLoop(for, reason, reasonLocation, reasonLabel) +select for, "For loop is not well formed, " + reason + ".", reasonLocation, reasonLabel diff --git a/c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql b/c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql new file mode 100644 index 0000000000..1bd2708750 --- /dev/null +++ b/c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql @@ -0,0 +1,80 @@ +/** + * @id c/misra/controlling-expr-invariant + * @name RULE-14-3: Controlling expressions shall not be invariant + * @description If a controlling expression has an invariant value then it is possible that there is + * a programming error. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-14-3 + * correctness + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from Expr expr, string message +where + not isExcluded(expr, Statements5Package::controllingExprInvariantQuery()) and + ( + exists(IfStmt ifStmt | + ( + ifStmt.getControllingExpr() = expr and + ( + conditionAlwaysFalse(expr) + or + conditionAlwaysTrue(expr) + ) + ) + ) and + message = "Controlling expression in if statement has an invariant value." + or + exists(Loop loop | + loop.getControllingExpr() = expr and + ( + conditionAlwaysFalse(expr) and + not ( + getEssentialTypeCategory(getEssentialType(expr)) instanceof EssentiallyBooleanType and + expr.getValue() = "0" + ) + or + conditionAlwaysTrue(expr) and + // Exception allows for infinite loops, but we only permit that for literals like `true` + not expr instanceof Literal + ) + ) and + message = "Controlling expression in loop statement has an invariant value." + or + exists(SwitchStmt switch | + switch.getControllingExpr() = expr and + ( + conditionAlwaysFalse(expr) or + conditionAlwaysTrue(expr) + ) + ) and + message = "Controlling expression in switch statement has an invariant value." + or + exists(ConditionalExpr conditional | + conditional.getCondition() = expr and + ( + conditionAlwaysFalse(expr) or + conditionAlwaysTrue(expr) + ) + ) and + message = "Controlling expression in conditional statement has an invariant value." + ) and + // Exclude cases where the controlling expressions is affected by a macro, because they can appear + // invariant in a particular invocation, but be variant between invocations. + not ( + expr.isAffectedByMacro() and + // Permit boolean literal macros + not expr instanceof BooleanLiteral + ) and + // Exclude template variables, because they can be instantiated with different values. + not expr = any(TemplateVariable tv).getAnInstantiation().getAnAccess() +select expr, message diff --git a/c/misra/src/rules/RULE-14-4/NonBooleanIfCondition.ql b/c/misra/src/rules/RULE-14-4/NonBooleanIfCondition.ql new file mode 100644 index 0000000000..f9a24d9492 --- /dev/null +++ b/c/misra/src/rules/RULE-14-4/NonBooleanIfCondition.ql @@ -0,0 +1,28 @@ +/** + * @id c/misra/non-boolean-if-condition + * @name RULE-14-4: The condition of an if-statement shall have type bool + * @description Non boolean conditions can be confusing for developers. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-14-4 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from Expr condition, Type essentialType +where + not isExcluded(condition, Statements4Package::nonBooleanIfConditionQuery()) and + exists(IfStmt ifStmt | + not ifStmt.isFromUninstantiatedTemplate(_) and + condition = ifStmt.getCondition() and + essentialType = getEssentialType(ifStmt.getCondition()) and + not getEssentialTypeCategory(essentialType) = EssentiallyBooleanType() + ) +select condition, "If condition has non boolean essential type " + essentialType + "." diff --git a/c/misra/src/rules/RULE-14-4/NonBooleanIterationCondition.ql b/c/misra/src/rules/RULE-14-4/NonBooleanIterationCondition.ql new file mode 100644 index 0000000000..8418993db2 --- /dev/null +++ b/c/misra/src/rules/RULE-14-4/NonBooleanIterationCondition.ql @@ -0,0 +1,37 @@ +/** + * @id c/misra/non-boolean-iteration-condition + * @name RULE-14-4: The condition of an iteration statement shall have type bool + * @description Non boolean conditions can be confusing for developers. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-14-4 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +/** A macro within the source location of this project. */ +class UserProvidedMacro extends Macro { + UserProvidedMacro() { exists(this.getFile().getRelativePath()) } +} + +/** A macro defined within a library used by this project. */ +class LibraryMacro extends Macro { + LibraryMacro() { not this instanceof UserProvidedMacro } +} + +from Expr condition, Loop l, Type essentialType +where + not isExcluded(condition, Statements4Package::nonBooleanIterationConditionQuery()) and + // Exclude loops generated from library macros + not l = any(LibraryMacro lm).getAnInvocation().getAGeneratedElement() and + condition = l.getCondition() and + essentialType = getEssentialType(condition) and + not getEssentialTypeCategory(essentialType) = EssentiallyBooleanType() +select condition, "Iteration condition has non boolean type " + essentialType + "." diff --git a/c/misra/src/rules/RULE-15-1/GotoStatementUsed.ql b/c/misra/src/rules/RULE-15-1/GotoStatementUsed.ql new file mode 100644 index 0000000000..84c7dbd408 --- /dev/null +++ b/c/misra/src/rules/RULE-15-1/GotoStatementUsed.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/goto-statement-used + * @name RULE-15-1: The goto statement should not be used + * @description The goto statement shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-1 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.gotostatementshouldnotbeused.GotoStatementShouldNotBeUsed + +class GotoStatementUsedQuery extends GotoStatementShouldNotBeUsedSharedQuery { + GotoStatementUsedQuery() { this = Statements6Package::gotoStatementUsedQuery() } +} diff --git a/c/misra/src/rules/RULE-15-2/GotoLabelLocationCondition.ql b/c/misra/src/rules/RULE-15-2/GotoLabelLocationCondition.ql new file mode 100644 index 0000000000..623fb9baed --- /dev/null +++ b/c/misra/src/rules/RULE-15-2/GotoLabelLocationCondition.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/goto-label-location-condition + * @name RULE-15-2: The goto statement shall jump to a label declared later in the same function + * @description Unconstrained use of goto can lead to unstructured code. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-2 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition + +class GotoLabelLocationConditionQuery extends GotoStatementConditionSharedQuery { + GotoLabelLocationConditionQuery() { this = Statements2Package::gotoLabelLocationConditionQuery() } +} diff --git a/c/misra/src/rules/RULE-15-3/GotoLabelBlockCondition.ql b/c/misra/src/rules/RULE-15-3/GotoLabelBlockCondition.ql new file mode 100644 index 0000000000..a88f3170de --- /dev/null +++ b/c/misra/src/rules/RULE-15-3/GotoLabelBlockCondition.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/goto-label-block-condition + * @name RULE-15-3: The goto statement and any of its label shall be declared or enclosed in the same block + * @description Any label referenced by a goto statement shall be declared in the same block, or in + * any block enclosing the goto statement. + * @kind problem + * @precision high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-3 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.gotoreferencealabelinsurroundingblock.GotoReferenceALabelInSurroundingBlock + +class GotoLabelBlockConditionQuery extends GotoReferenceALabelInSurroundingBlockSharedQuery { + GotoLabelBlockConditionQuery() { this = Statements2Package::gotoLabelBlockConditionQuery() } +} diff --git a/c/misra/src/rules/RULE-15-4/LoopIterationCondition.ql b/c/misra/src/rules/RULE-15-4/LoopIterationCondition.ql new file mode 100644 index 0000000000..b172a2c1ea --- /dev/null +++ b/c/misra/src/rules/RULE-15-4/LoopIterationCondition.ql @@ -0,0 +1,56 @@ +/** + * @id c/misra/loop-iteration-condition + * @name RULE-15-4: There should be no more than one break or goto statement used to terminate any iteration statement + * @description More than one break or goto statement in iteration conditions may lead to + * readability and maintainability issues. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-4 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +/** + * A breaking statement. + */ +class BreakOrGotoStmt extends JumpStmt { + BreakOrGotoStmt() { + this instanceof BreakStmt or + this instanceof GotoStmt + } + + /** + * Gets a loop this breaks out of, if any. + * + * - This can produce no results if this is a `break` and the enclosing breakable is a switch statement. + * - This can produce no result if this is a `goto`, and the target is within the same nearest enclosing loop. + * - This can produce multiple results if this is a `goto`, and the target is outside multiple enclosing loops. + */ + Loop getABrokenLoop() { + result = this.(BreakStmt).getBreakable() + or + exists(GotoStmt goto | + goto = this and + // Find any loop that encloses this goto + result.getChildStmt*() = goto and + // But does not enclose the target of the goto i.e. the goto breaks out of it + not result.getChildStmt*() = goto.getTarget() + ) + } +} + +from Loop loop, BreakOrGotoStmt breakOrGoto +where + not isExcluded(loop, Statements2Package::loopIterationConditionQuery()) and + // More than one break or goto statement in the loop + count(BreakOrGotoStmt terminationStmt | terminationStmt.getABrokenLoop() = loop) > 1 and + // Report a break or goto statement + breakOrGoto.getABrokenLoop() = loop +select loop, "Iteration statement contains more than one $@.", breakOrGoto, + "break or goto statement" diff --git a/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql b/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql new file mode 100644 index 0000000000..8e777d7332 --- /dev/null +++ b/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql @@ -0,0 +1,44 @@ +/** + * @id c/misra/function-return-condition + * @name RULE-15-5: A function should have a single point of exit at the end + * @description Not having a single point of exit in a function can lead to unintentional behaviour. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-5 + * maintainability + * readability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +class UserWrittenReturnStmt extends ReturnStmt { + UserWrittenReturnStmt() { not this.isCompilerGenerated() } +} + +from Function func, string message, UserWrittenReturnStmt returnStmt +where + not isExcluded(func, Statements5Package::functionReturnConditionQuery()) and + func.hasDefinition() and + // Ignore functions which have multiple bodies + count(func.getBlock()) = 1 and + // Ignore functions which are compiler generated + not func.isCompilerGenerated() and + // Report all the return statements in the function + returnStmt.getEnclosingFunction() = func and + ( + // There is more than one return statement + count(UserWrittenReturnStmt return | return.getEnclosingFunction() = func) > 1 and + message = "Function has more than one $@." + or + // There is exactly one return statement + count(UserWrittenReturnStmt return | return.getEnclosingFunction() = func) = 1 and + // But it is not the last statement in the function + not func.getBlock().getLastStmt() instanceof UserWrittenReturnStmt and + message = "The $@ is not the last statement of the function." + ) +select func, message, returnStmt, "return statement" diff --git a/c/misra/src/rules/RULE-15-6/LoopCompoundCondition.ql b/c/misra/src/rules/RULE-15-6/LoopCompoundCondition.ql new file mode 100644 index 0000000000..9cc5bf9dda --- /dev/null +++ b/c/misra/src/rules/RULE-15-6/LoopCompoundCondition.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/loop-compound-condition + * @name RULE-15-6: The statement forming the body of a loop shall be a compound statement + * @description if the body of a loop is not enclosed in braces, then this can lead to incorrect + * execution, and is hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-6 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from Loop loop +where + not isExcluded(loop, Statements3Package::loopCompoundConditionQuery()) and + not loop.getStmt() instanceof BlockStmt +select loop, "Loop body not enclosed within braces." diff --git a/c/misra/src/rules/RULE-15-6/SelectionCompoundCondition.ql b/c/misra/src/rules/RULE-15-6/SelectionCompoundCondition.ql new file mode 100644 index 0000000000..f84c142414 --- /dev/null +++ b/c/misra/src/rules/RULE-15-6/SelectionCompoundCondition.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/selection-compound-condition + * @name RULE-15-6: The statement forming the body of a slection statement shall be a compound statement + * @description if the body of a selection statement is not enclosed in braces, then this can lead + * to incorrect execution, and is hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-6 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from IfStmt ifStmt +where + not isExcluded(ifStmt, Statements3Package::selectionCompoundConditionQuery()) and + not ifStmt.getChildStmt() instanceof BlockStmt +select ifStmt, "If statement not enclosed within braces." diff --git a/c/misra/src/rules/RULE-15-6/SwitchCompoundCondition.ql b/c/misra/src/rules/RULE-15-6/SwitchCompoundCondition.ql new file mode 100644 index 0000000000..1d446f323f --- /dev/null +++ b/c/misra/src/rules/RULE-15-6/SwitchCompoundCondition.ql @@ -0,0 +1,27 @@ +/** + * @id c/misra/switch-compound-condition + * @name RULE-15-6: The statement forming the body of a switch shall be a compound statement + * @description If the body of a switch is not enclosed in braces, then this can lead to incorrect + * execution, and is hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-6 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.SwitchStatement + +from SwitchStmt switch +where + not isExcluded(switch, Statements3Package::switchCompoundConditionQuery()) and + ( + switch.getStmt() instanceof ArtificialBlock or + not switch.getStmt() instanceof BlockStmt + ) +select switch, "Switch body not enclosed within braces." diff --git a/c/misra/src/rules/RULE-15-7/IfElseEndCondition.ql b/c/misra/src/rules/RULE-15-7/IfElseEndCondition.ql new file mode 100644 index 0000000000..ee06f484fe --- /dev/null +++ b/c/misra/src/rules/RULE-15-7/IfElseEndCondition.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/if-else-end-condition + * @name RULE-15-7: All if / else if constructs shall be terminated with an else statement + * @description Terminating an `if...else` construct is a defensive programming technique. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-7 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct + +class IfElseEndConditionQuery extends IfElseTerminationConstructSharedQuery { + IfElseEndConditionQuery() { this = Statements3Package::ifElseEndConditionQuery() } +} diff --git a/c/misra/src/rules/RULE-16-1/6-4-3_grammar_1.png b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_1.png new file mode 100644 index 0000000000..d6adfd06c1 Binary files /dev/null and b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_1.png differ diff --git a/c/misra/src/rules/RULE-16-1/6-4-3_grammar_2.png b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_2.png new file mode 100644 index 0000000000..4a7ae673e8 Binary files /dev/null and b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_2.png differ diff --git a/c/misra/src/rules/RULE-16-1/6-4-3_grammar_3.png b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_3.png new file mode 100644 index 0000000000..7b58e6f41e Binary files /dev/null and b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_3.png differ diff --git a/c/misra/src/rules/RULE-16-1/SwitchCaseStartCondition.ql b/c/misra/src/rules/RULE-16-1/SwitchCaseStartCondition.ql new file mode 100644 index 0000000000..4ceca23d8f --- /dev/null +++ b/c/misra/src/rules/RULE-16-1/SwitchCaseStartCondition.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/switch-case-start-condition + * @name RULE-16-1: A well formed switch statement must start with a case clause + * @description The switch statement syntax is weak and may lead to unspecified behaviour. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-1 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition + +class SwitchCaseStartConditionQuery extends SwitchCasePositionConditionSharedQuery { + SwitchCaseStartConditionQuery() { this = Statements3Package::switchCaseStartConditionQuery() } +} diff --git a/c/misra/src/rules/RULE-16-1/SwitchStmtNotWellFormed.ql b/c/misra/src/rules/RULE-16-1/SwitchStmtNotWellFormed.ql new file mode 100644 index 0000000000..644994562a --- /dev/null +++ b/c/misra/src/rules/RULE-16-1/SwitchStmtNotWellFormed.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/switch-stmt-not-well-formed + * @name RULE-16-1: A well formed switch statement should only have expression, compound, selection, iteration or try statements within its body + * @description The switch statement syntax is weak and may lead to unspecified behaviour. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-1 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed + +class SwitchStmtNotWellFormedQuery extends SwitchNotWellFormedSharedQuery { + SwitchStmtNotWellFormedQuery() { this = Statements3Package::switchStmtNotWellFormedQuery() } +} diff --git a/c/misra/src/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.ql b/c/misra/src/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.ql new file mode 100644 index 0000000000..45ad0519bb --- /dev/null +++ b/c/misra/src/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/nest-switch-label-in-switch-statement + * @name RULE-16-2: Nested switch labels shall not be used + * @description Nested switch labels can lead to unstructured code. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-2 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch + +class NestSwitchLabelInSwitchStatementQuery extends NestedLabelInSwitchSharedQuery { + NestSwitchLabelInSwitchStatementQuery() { + this = Statements1Package::nestSwitchLabelInSwitchStatementQuery() + } +} diff --git a/c/misra/src/rules/RULE-16-3/BreakShallTerminateSwitchClause.ql b/c/misra/src/rules/RULE-16-3/BreakShallTerminateSwitchClause.ql new file mode 100644 index 0000000000..5ff30b53e0 --- /dev/null +++ b/c/misra/src/rules/RULE-16-3/BreakShallTerminateSwitchClause.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/break-shall-terminate-switch-clause + * @name RULE-16-3: An unconditional break statement shall terminate every switch-clause + * @description An unterminated switch-clause occurring at the end of a switch statement may fall + * into switch clauses which are added later. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-16-3 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from SwitchCase case +where + not isExcluded(case, Statements1Package::breakShallTerminateSwitchClauseQuery()) and + not case.terminatesInBreakStmt() and + not case.getFollowingStmt() instanceof SwitchCase +select case, "The switch $@ does not terminate with a break statement.", case, "clause" diff --git a/c/misra/src/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql b/c/misra/src/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql new file mode 100644 index 0000000000..441e30b7e7 --- /dev/null +++ b/c/misra/src/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql @@ -0,0 +1,47 @@ +/** + * @id c/misra/every-switch-shall-have-default-label + * @name RULE-16-4: Every switch statement shall have a default label + * @description A default label that has no statements or a comment explaining why this is correct + * indicates a missing implementation that may result in unexpected behavior when the + * default case is executed. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-16-4 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +Stmt getFirstNonBlockStatement(BlockStmt bs) { + exists(Stmt nextStmt | nextStmt = bs.getStmt(0) | + if nextStmt instanceof BlockStmt + then result = getFirstNonBlockStatement(nextStmt) + else result = nextStmt + ) +} + +Stmt getFirstStatement(DefaultCase case) { + exists(Stmt next | next = case.getFollowingStmt() | + if next instanceof BlockStmt then result = getFirstNonBlockStatement(next) else result = next + ) +} + +from SwitchStmt switch, string message +where + not isExcluded(switch, Statements1Package::everySwitchShallHaveDefaultLabelQuery()) and + not switch.hasDefaultCase() and + message = "has missing default clause." + or + exists(SwitchCase case, BreakStmt break | + switch.getDefaultCase() = case and + getFirstStatement(case) = break and + not exists(Comment comment | comment.getCommentedElement() = break) and + message = + "has default label that does not terminate in a statement or comment before break statement" + ) +select switch, "$@ statement " + message, switch, "Switch" diff --git a/c/misra/src/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql b/c/misra/src/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql new file mode 100644 index 0000000000..5a93477b9a --- /dev/null +++ b/c/misra/src/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/default-not-first-or-last-of-switch + * @name RULE-16-5: A default label shall appear as either the first or the last switch label or a switch statement + * @description Locating the default label is easier when it is the first or last label. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-5 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.SwitchStatement + +from SwitchStmt switch, SwitchCase defaultCase +where + not isExcluded(switch, Statements1Package::defaultNotFirstOrLastOfSwitchQuery()) and + switch.getDefaultCase() = defaultCase and + exists(defaultCase.getPreviousSwitchCase()) and + finalClauseInSwitchNotDefault(switch) +select defaultCase, "$@ statement does not have $@ case as first or last switch label.", switch, + "Switch", defaultCase, "default" diff --git a/c/misra/src/rules/RULE-16-6/SwitchClauseNumberCondition.ql b/c/misra/src/rules/RULE-16-6/SwitchClauseNumberCondition.ql new file mode 100644 index 0000000000..0259f8023d --- /dev/null +++ b/c/misra/src/rules/RULE-16-6/SwitchClauseNumberCondition.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/switch-clause-number-condition + * @name RULE-16-6: Every switch statement shall have at least two switch-clauses + * @description Switch Statements with a single path are redundant and may cause programming errors. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-6 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from SwitchStmt switch +where + not isExcluded(switch, Statements2Package::switchClauseNumberConditionQuery()) and + count(SwitchCase case | + switch.getASwitchCase() = case and + case.getNextSwitchCase() != case.getFollowingStmt() + ) + 1 < 2 +select switch, "$@ statement has a single path.", switch, "Switch" diff --git a/c/misra/src/rules/RULE-16-7/SwitchExpressionBoolCondition.ql b/c/misra/src/rules/RULE-16-7/SwitchExpressionBoolCondition.ql new file mode 100644 index 0000000000..06be288e2c --- /dev/null +++ b/c/misra/src/rules/RULE-16-7/SwitchExpressionBoolCondition.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/switch-expression-bool-condition + * @name RULE-16-7: A switch-expression shall not have essentially Boolean type + * @description An `if-else` construct is more appropriate for boolean controlled expression. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-16-7 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from SwitchStmt switch, Expr controllingExpr +where + not isExcluded(switch, Statements2Package::switchExpressionBoolConditionQuery()) and + controllingExpr = switch.getControllingExpr() and + getEssentialTypeCategory(getEssentialType(controllingExpr)) = EssentiallyBooleanType() +select controllingExpr, "The condition of this $@ statement has boolean type", switch, "switch" diff --git a/c/misra/src/rules/RULE-17-1/FeaturesOfStdarghUsed.ql b/c/misra/src/rules/RULE-17-1/FeaturesOfStdarghUsed.ql index 1cde8b98f2..ddccb58ad1 100644 --- a/c/misra/src/rules/RULE-17-1/FeaturesOfStdarghUsed.ql +++ b/c/misra/src/rules/RULE-17-1/FeaturesOfStdarghUsed.ql @@ -7,6 +7,7 @@ * @problem.severity error * @tags external/misra/id/rule-17-1 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.ql b/c/misra/src/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.ql new file mode 100644 index 0000000000..1e32793c3f --- /dev/null +++ b/c/misra/src/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.ql @@ -0,0 +1,27 @@ +/** + * @id c/misra/non-void-return-type-of-noreturn-function + * @name RULE-17-10: A function declared with _noreturn shall have a return type of void + * @description Function declared with _noreturn will by definition not return a value, and should + * be declared to return void. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-17-10 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Noreturn + +from NoreturnFunction f, Type returnType +where + not isExcluded(f, NoReturnPackage::nonVoidReturnTypeOfNoreturnFunctionQuery()) and + returnType = f.getType() and + not returnType instanceof VoidType and + not f.isCompilerGenerated() +select f, + "The function " + f.getName() + " is declared _noreturn but has a return type of " + + returnType.toString() + "." diff --git a/c/misra/src/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.ql b/c/misra/src/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.ql new file mode 100644 index 0000000000..4dd939effe --- /dev/null +++ b/c/misra/src/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.ql @@ -0,0 +1,29 @@ +/** + * @id c/misra/function-with-no-returning-branch-should-be-noreturn + * @name RULE-17-11: A function without a branch that returns shall be declared with _Noreturn + * @description Functions which cannot return should be declared with _Noreturn. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-17-11 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Noreturn + +from Function f +where + not isExcluded(f, NoReturnPackage::functionWithNoReturningBranchShouldBeNoreturnQuery()) and + not f instanceof NoreturnFunction and + not mayReturn(f) and + f.hasDefinition() and + not f.getName() = "main" and // Allowed exception; _Noreturn main() is undefined behavior. + // Harden against c++ cases. + not f.isFromUninstantiatedTemplate(_) and + not f.isDeleted() and + not f.isCompilerGenerated() +select f, "The function " + f.getName() + " cannot return and should be declared as _Noreturn." diff --git a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql new file mode 100644 index 0000000000..c95612b7ba --- /dev/null +++ b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql @@ -0,0 +1,33 @@ +/** + * @id c/misra/function-addresses-should-address-operator + * @name RULE-17-12: A function identifier should only be called with a parenthesized parameter list or used with a & + * @description A function identifier should only be called with a parenthesized parameter list or + * used with a & (address-of). + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-12 + * readability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +predicate isImplicitlyAddressed(FunctionAccess access) { + not access.getParent() instanceof AddressOfExpr and + // Note: the following *seems* to only exist in c++ codebases, for instance, + // when calling a member. In c, this syntax should always extract as a + // [FunctionCall] rather than a [ExprCall] of a [FunctionAccess]. Still, this + // is a good pattern to be defensive against. + not exists(ExprCall call | call.getExpr() = access) +} + +from FunctionAccess funcAccess +where + not isExcluded(funcAccess, FunctionTypesPackage::functionAddressesShouldAddressOperatorQuery()) and + isImplicitlyAddressed(funcAccess) +select funcAccess, + "The address of function " + funcAccess.getTarget().getName() + + " is taken without the & operator." diff --git a/c/misra/src/rules/RULE-17-2/RecursiveFunctionCondition.ql b/c/misra/src/rules/RULE-17-2/RecursiveFunctionCondition.ql new file mode 100644 index 0000000000..c7cb818119 --- /dev/null +++ b/c/misra/src/rules/RULE-17-2/RecursiveFunctionCondition.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/recursive-function-condition + * @name RULE-17-2: Functions shall not call themselves, either directly or indirectly + * @description Recursive function may cause memory and system failure issues. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-2 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from FunctionCall fc, Function f, string msg +where + not isExcluded(fc, Statements3Package::recursiveFunctionConditionQuery()) and + fc.getEnclosingFunction() = f and + fc.getTarget().calls*(f) and + if fc.getTarget() = f + then msg = f + " calls itself directly." + else msg = f + " is indirectly recursive via this call to $@." +select fc, msg, fc.getTarget(), fc.getTarget().getName() diff --git a/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql b/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql new file mode 100644 index 0000000000..af6c9bccad --- /dev/null +++ b/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql @@ -0,0 +1,30 @@ +/** + * @id c/misra/function-declared-implicitly + * @name RULE-17-3: A function shall not be declared implicitly + * @description Omission of type specifiers may not be supported by some compilers. Additionally + * implicit typing can lead to undefined behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-3 + * correctness + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers + +from FunctionDeclarationEntry fde +where + not isExcluded(fde, Declarations6Package::functionDeclaredImplicitlyQuery()) and + ( + //use before declaration + fde.isImplicit() + or + //declared but type not explicit + isDeclaredImplicit(fde.getDeclaration()) + ) +select fde, "Function declaration is implicit." diff --git a/c/misra/src/rules/RULE-17-4/NonVoidFunctionReturnCondition.ql b/c/misra/src/rules/RULE-17-4/NonVoidFunctionReturnCondition.ql new file mode 100644 index 0000000000..1529a403c9 --- /dev/null +++ b/c/misra/src/rules/RULE-17-4/NonVoidFunctionReturnCondition.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/non-void-function-return-condition + * @name RULE-17-4: All exit paths from a function with non-void return type shall have an explicit return statement + * @description Not returning with an expression from a non-void function can lead to undefined + * behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-4 + * correctness + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.nonvoidfunctiondoesnotreturn.NonVoidFunctionDoesNotReturn + +class NonVoidFunctionReturnConditionQuery extends NonVoidFunctionDoesNotReturnSharedQuery { + NonVoidFunctionReturnConditionQuery() { + this = Statements5Package::nonVoidFunctionReturnConditionQuery() + } +} diff --git a/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql b/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql new file mode 100644 index 0000000000..1a142ddb22 --- /dev/null +++ b/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql @@ -0,0 +1,78 @@ +/** + * @id c/misra/array-function-argument-number-of-elements + * @name RULE-17-5: An array founction argument shall have an appropriate number of elements + * @description The function argument corresponding to an array parameter shall have an appropriate + * number of elements. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-17-5 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.dataflow.DataFlow + +/** + * Models a function parameter of type array with specified size + * ``` + * void f1(int ar[3]); + * ``` + */ +class ArrayParameter extends Parameter { + ArrayParameter() { this.getType().(ArrayType).hasArraySize() } + + Expr getAMatchingArgument() { + exists(FunctionCall fc | + this.getFunction() = fc.getTarget() and + result = fc.getArgument(this.getIndex()) + ) + } + + int getArraySize() { result = this.getType().(ArrayType).getArraySize() } +} + +/** + * The number of initialized elements in an ArrayAggregateLiteral. + * In the following examples the result=2 + * ``` + * int arr3[3] = {1, 2}; + * int arr2[2] = {1, 2, 3}; + * ``` + */ +int countElements(ArrayAggregateLiteral l) { result = count(l.getAnElementExpr(_)) } + +module SmallArrayConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ArrayAggregateLiteral } + + predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(ArrayParameter p).getAMatchingArgument() + } +} + +module SmallArrayFlow = DataFlow::Global; + +from Expr arg, ArrayParameter p +where + not isExcluded(arg, Contracts6Package::arrayFunctionArgumentNumberOfElementsQuery()) and + arg = p.getAMatchingArgument() and + ( + // the argument is a value and not an array + not arg.getType() instanceof DerivedType + or + // the argument is an array too small + arg.getType().(ArrayType).getArraySize() < p.getArraySize() + or + // the argument is a pointer and its value does not come from a literal of the correct + arg.getType() instanceof PointerType and + not exists(ArrayAggregateLiteral l | + SmallArrayFlow::flow(DataFlow::exprNode(l), DataFlow::exprNode(arg)) and + countElements(l) >= p.getArraySize() + ) + ) +select arg, + "The function argument does not have a sufficient number or elements declared in the $@.", p, + "parameter" diff --git a/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql b/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql new file mode 100644 index 0000000000..0a1232b6ad --- /dev/null +++ b/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/use-of-array-static + * @name RULE-17-6: The declaration of an array parameter shall not contain the static keyword between the [ ] + * @description Using the static keyword in an array type is error prone, and relies on the + * programmer to adhere to the guarantees to avoid undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-6 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra + +from Parameter p +where + not isExcluded(p, StaticPackage::useOfArrayStaticQuery()) and + p.getType().(ArrayType).hasSpecifier("static") +select p, "Parameter " + p + " is declared as an array type using the static keyword." diff --git a/c/misra/src/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.ql b/c/misra/src/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.ql new file mode 100644 index 0000000000..934aeb79d3 --- /dev/null +++ b/c/misra/src/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.ql @@ -0,0 +1,28 @@ +/** + * @id c/misra/value-returned-by-a-function-not-used + * @name RULE-17-7: Return values should be used or cast to void + * @description The value returned by a function having non-void return type shall be used or cast + * to void. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-7 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from Call c +where + not isExcluded(c, Contracts6Package::valueReturnedByAFunctionNotUsedQuery()) and + // Calls in `ExprStmt`s indicate that the return value is ignored + c.getParent() instanceof ExprStmt and + // Ignore calls to void functions or where the return value is cast to `void` + not c.getActualType() instanceof VoidType and + // Exclude cases where the function call is generated within a macro, as the user of the macro is + // not necessarily able to address thoes results + not c.isAffectedByMacro() +select c, "The value returned by this call shall be used or cast to `void`." diff --git a/c/misra/src/rules/RULE-17-8/ModificationOfFunctionParameter.ql b/c/misra/src/rules/RULE-17-8/ModificationOfFunctionParameter.ql index 6867455a45..95cddb57d3 100644 --- a/c/misra/src/rules/RULE-17-8/ModificationOfFunctionParameter.ql +++ b/c/misra/src/rules/RULE-17-8/ModificationOfFunctionParameter.ql @@ -9,6 +9,7 @@ * @problem.severity warning * @tags external/misra/id/rule-17-8 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-17-9/ReturnStatementInNoreturnFunction.ql b/c/misra/src/rules/RULE-17-9/ReturnStatementInNoreturnFunction.ql new file mode 100644 index 0000000000..dedac9da9e --- /dev/null +++ b/c/misra/src/rules/RULE-17-9/ReturnStatementInNoreturnFunction.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/return-statement-in-noreturn-function + * @name RULE-17-9: Verify that a function declared with _Noreturn does not return + * @description Returning inside a function declared with _Noreturn is undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-9 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.functionnoreturnattributecondition.FunctionNoReturnAttributeCondition + +class ReturnStatementInNoreturnFunctionQuery extends FunctionNoReturnAttributeConditionSharedQuery { + ReturnStatementInNoreturnFunctionQuery() { + this = NoReturnPackage::returnStatementInNoreturnFunctionQuery() + } +} diff --git a/c/misra/src/rules/RULE-18-1/PointerAndDerivedPointerMustAddressSameArray.ql b/c/misra/src/rules/RULE-18-1/PointerAndDerivedPointerMustAddressSameArray.ql index 689d711ec2..c8944bd30d 100644 --- a/c/misra/src/rules/RULE-18-1/PointerAndDerivedPointerMustAddressSameArray.ql +++ b/c/misra/src/rules/RULE-18-1/PointerAndDerivedPointerMustAddressSameArray.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-18-1 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.donotusepointerarithmetictoaddressdifferentarrays.DoNotUsePointerArithmeticToAddressDifferentArrays -class PointerAndDerivedPointerMustAddressSameArrayQuery extends DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery { +class PointerAndDerivedPointerMustAddressSameArrayQuery extends DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery +{ PointerAndDerivedPointerMustAddressSameArrayQuery() { this = Pointers1Package::pointerAndDerivedPointerMustAddressSameArrayQuery() } diff --git a/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql b/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql new file mode 100644 index 0000000000..dc1433d5e4 --- /dev/null +++ b/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql @@ -0,0 +1,51 @@ +/** + * @id c/misra/pointers-to-variably-modified-array-types-used + * @name RULE-18-10: Pointers to variably-modified array types shall not be used + * @description Pointers to variably-modified array types shall not be used, as these pointer types + * are frequently incompatible with other fixed or variably sized arrays, resulting in + * undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-18-10 + * external/misra/c/2012/amendment4 + * correctness + * security + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.VariablyModifiedTypes + +from VmtDeclarationEntry v, string declstr, string adjuststr, string relationstr +where + not isExcluded(v, InvalidMemory3Package::pointersToVariablyModifiedArrayTypesUsedQuery()) and + // Capture only pointers to VLA types, not raw VLA types. + not v.getVlaType() = v.getType() and + ( + if v instanceof ParameterDeclarationEntry + then declstr = "Parameter " + else + if v instanceof VariableDeclarationEntry + then declstr = "Variable " + else declstr = "Declaration " + ) and + ( + if + v instanceof ParameterDeclarationEntry and + v.getType() instanceof ParameterAdjustedVariablyModifiedType + then adjuststr = "adjusted to" + else adjuststr = "declared with" + ) and + ( + if v.getType().(PointerType).getBaseType() instanceof CandidateVlaType + then relationstr = "pointer to" + else relationstr = "with inner" + ) and + // Remove results that appear to be unreliable, potentially from a macro. + not v.appearsDuplicated() +select v, + declstr + v.getName() + " is " + adjuststr + " variably-modified type, " + relationstr + + " variable length array of non constant size $@ and element type '" + + v.getVlaType().getVariableBaseType() + "'", v.getSizeExpr(), v.getSizeExpr().toString() diff --git a/c/misra/src/rules/RULE-18-2/SubtractionBetweenPointersMustAddressSameArray.ql b/c/misra/src/rules/RULE-18-2/SubtractionBetweenPointersMustAddressSameArray.ql index 4eb5c5b7fb..ec3a30d5ba 100644 --- a/c/misra/src/rules/RULE-18-2/SubtractionBetweenPointersMustAddressSameArray.ql +++ b/c/misra/src/rules/RULE-18-2/SubtractionBetweenPointersMustAddressSameArray.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-18-2 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.donotsubtractpointersaddressingdifferentarrays.DoNotSubtractPointersAddressingDifferentArrays -class SubtractionBetweenPointersMustAddressSameArrayQuery extends DoNotSubtractPointersAddressingDifferentArraysSharedQuery { +class SubtractionBetweenPointersMustAddressSameArrayQuery extends DoNotSubtractPointersAddressingDifferentArraysSharedQuery +{ SubtractionBetweenPointersMustAddressSameArrayQuery() { this = Pointers1Package::subtractionBetweenPointersMustAddressSameArrayQuery() } diff --git a/c/misra/src/rules/RULE-18-3/RelationalOperatorComparesPointerToDifferentArray.ql b/c/misra/src/rules/RULE-18-3/RelationalOperatorComparesPointerToDifferentArray.ql index 7e6cd78a6a..4624cea616 100644 --- a/c/misra/src/rules/RULE-18-3/RelationalOperatorComparesPointerToDifferentArray.ql +++ b/c/misra/src/rules/RULE-18-3/RelationalOperatorComparesPointerToDifferentArray.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-18-3 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.donotuserelationaloperatorswithdifferingarrays.DoNotUseRelationalOperatorsWithDifferingArrays -class RelationalOperatorComparesPointerToDifferentArrayQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery { +class RelationalOperatorComparesPointerToDifferentArrayQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery +{ RelationalOperatorComparesPointerToDifferentArrayQuery() { this = Pointers1Package::relationalOperatorComparesPointerToDifferentArrayQuery() } diff --git a/c/misra/src/rules/RULE-18-4/DoNotUseAdditionOrSubtractionOperatorsOnPointers.ql b/c/misra/src/rules/RULE-18-4/DoNotUseAdditionOrSubtractionOperatorsOnPointers.ql index 3263640266..a1a1ad367b 100644 --- a/c/misra/src/rules/RULE-18-4/DoNotUseAdditionOrSubtractionOperatorsOnPointers.ql +++ b/c/misra/src/rules/RULE-18-4/DoNotUseAdditionOrSubtractionOperatorsOnPointers.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-18-4 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.useonlyarrayindexingforpointerarithmetic.UseOnlyArrayIndexingForPointerArithmetic -class DoNotUseAdditionOrSubtractionOperatorsOnPointersQuery extends UseOnlyArrayIndexingForPointerArithmeticSharedQuery { +class DoNotUseAdditionOrSubtractionOperatorsOnPointersQuery extends UseOnlyArrayIndexingForPointerArithmeticSharedQuery +{ DoNotUseAdditionOrSubtractionOperatorsOnPointersQuery() { this = Pointers1Package::doNotUseAdditionOrSubtractionOperatorsOnPointersQuery() } diff --git a/c/misra/src/rules/RULE-18-5/NoMoreThanTwoLevelsOfPointerNestingInDeclarations.ql b/c/misra/src/rules/RULE-18-5/NoMoreThanTwoLevelsOfPointerNestingInDeclarations.ql index c9d99469e0..f467c41804 100644 --- a/c/misra/src/rules/RULE-18-5/NoMoreThanTwoLevelsOfPointerNestingInDeclarations.ql +++ b/c/misra/src/rules/RULE-18-5/NoMoreThanTwoLevelsOfPointerNestingInDeclarations.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-18-5 * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.donotusemorethantwolevelsofpointerindirection.DoNotUseMoreThanTwoLevelsOfPointerIndirection -class NoMoreThanTwoLevelsOfPointerNestingInDeclarationsQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery { +class NoMoreThanTwoLevelsOfPointerNestingInDeclarationsQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery +{ NoMoreThanTwoLevelsOfPointerNestingInDeclarationsQuery() { this = Pointers1Package::noMoreThanTwoLevelsOfPointerNestingInDeclarationsQuery() } diff --git a/c/misra/src/rules/RULE-18-6/AutomaticStorageObjectAddressCopiedToOtherObject.ql b/c/misra/src/rules/RULE-18-6/AutomaticStorageObjectAddressCopiedToOtherObject.ql index 499b730e2f..efbc8d1334 100644 --- a/c/misra/src/rules/RULE-18-6/AutomaticStorageObjectAddressCopiedToOtherObject.ql +++ b/c/misra/src/rules/RULE-18-6/AutomaticStorageObjectAddressCopiedToOtherObject.ql @@ -9,6 +9,7 @@ * @problem.severity error * @tags external/misra/id/rule-18-6 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -16,7 +17,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.donotcopyaddressofautostorageobjecttootherobject.DoNotCopyAddressOfAutoStorageObjectToOtherObject -class AutomaticStorageObjectAddressCopiedToOtherObjectQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery { +class AutomaticStorageObjectAddressCopiedToOtherObjectQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery +{ AutomaticStorageObjectAddressCopiedToOtherObjectQuery() { this = Pointers1Package::automaticStorageObjectAddressCopiedToOtherObjectQuery() } diff --git a/c/misra/src/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.ql b/c/misra/src/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.ql new file mode 100644 index 0000000000..6a520447d1 --- /dev/null +++ b/c/misra/src/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.ql @@ -0,0 +1,39 @@ +/** + * @id c/misra/thread-local-object-address-copied-to-global-object + * @name RULE-18-6: The address of an object with thread-local storage shall not be copied to a global object + * @description Storing the address of a thread-local object in a global object will result in + * undefined behavior if the address is accessed after the relevant thread is + * terminated. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-6 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects +import codingstandards.cpp.Concurrency + +from AssignExpr assignment, Element threadLocal, ObjectIdentity static +where + not isExcluded(assignment, Pointers1Package::threadLocalObjectAddressCopiedToGlobalObjectQuery()) and + assignment.getLValue() = static.getASubobjectAccess() and + static.getStorageDuration().isStatic() and + ( + exists(ObjectIdentity threadLocalObj | + threadLocal = threadLocalObj and + assignment.getRValue() = threadLocalObj.getASubobjectAddressExpr() and + threadLocalObj.getStorageDuration().isThread() + ) + or + exists(TSSGetFunctionCall getCall | + threadLocal = getCall.getKey() and + assignment.getRValue() = getCall + ) + ) +select assignment, "Thread local object $@ address copied to static object $@.", + threadLocal.getLocation(), threadLocal.toString(), static.getLocation(), static.toString() diff --git a/c/misra/src/rules/RULE-18-7/FlexibleArrayMembersDeclared.ql b/c/misra/src/rules/RULE-18-7/FlexibleArrayMembersDeclared.ql new file mode 100644 index 0000000000..73f0732ba5 --- /dev/null +++ b/c/misra/src/rules/RULE-18-7/FlexibleArrayMembersDeclared.ql @@ -0,0 +1,20 @@ +/** + * @id c/misra/flexible-array-members-declared + * @name RULE-18-7: Flexible array members shall not be declared + * @description The use of flexible array members can lead to unexpected program behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-7 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Variable + +from FlexibleArrayMember f +where not isExcluded(f, Declarations6Package::flexibleArrayMembersDeclaredQuery()) +select f, "Flexible array member declared." diff --git a/c/misra/src/rules/RULE-18-8/VariableLengthArrayTypesUsed.ql b/c/misra/src/rules/RULE-18-8/VariableLengthArrayTypesUsed.ql new file mode 100644 index 0000000000..cf19c02eca --- /dev/null +++ b/c/misra/src/rules/RULE-18-8/VariableLengthArrayTypesUsed.ql @@ -0,0 +1,68 @@ +/** + * @id c/misra/variable-length-array-types-used + * @name RULE-18-8: Variable-length array types shall not be used + * @description Using a variable length array can lead to unexpected or undefined program behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-8 + * correctness + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +/** + * Typedefs may be declared as VLAs, eg, `typedef int vla[x];`. This query finds types that refer to + * such typedef types, for instance `vla foo;` or adding a dimension via `vla bar[10];`. + * + * Consts and other specifiers may be added, but `vla *ptr;` is not a VLA any more, and is excluded. + */ +class VlaTypedefType extends Type { + VlaDeclStmt vlaDecl; + ArrayType arrayType; + + VlaTypedefType() { + // Holds for direct references to the typedef type: + this = vlaDecl.getType() and + vlaDecl.getType() instanceof TypedefType and + arrayType = vlaDecl.getType().stripTopLevelSpecifiers() + or + // Handle arrays of VLA typedefs, and carefully handle specified VLA typedef types, as + // `stripTopLevelSpecifiers` resolves past the VLA typedef type. + exists(DerivedType dt, VlaTypedefType vlaType | + (dt instanceof ArrayType or dt instanceof SpecifiedType) and + this = dt and + vlaType = dt.getBaseType() and + vlaDecl = vlaType.getVlaDeclStmt() and + arrayType = vlaType.getArrayType() + ) + } + + VlaDeclStmt getVlaDeclStmt() { result = vlaDecl } + + ArrayType getArrayType() { result = arrayType } +} + +from Variable v, Expr size, ArrayType arrayType, VlaDeclStmt vlaDecl, string typeStr +where + not isExcluded(v, Declarations7Package::variableLengthArrayTypesUsedQuery()) and + size = vlaDecl.getVlaDimensionStmt(0).getDimensionExpr() and + ( + // Holds is if v is a variable declaration: + v = vlaDecl.getVariable() and + arrayType = v.getType().stripTopLevelSpecifiers() + or + // Holds is if v is a typedef declaration: + exists(VlaTypedefType typedef | + v.getType() = typedef and + arrayType = typedef.getArrayType() and + vlaDecl = typedef.getVlaDeclStmt() + ) + ) and + typeStr = arrayType.getBaseType().toString() +select v, "Variable length array of element type '" + typeStr + "' with non-constant size $@.", + size, size.toString() diff --git a/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql b/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql new file mode 100644 index 0000000000..da73214859 --- /dev/null +++ b/c/misra/src/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql @@ -0,0 +1,68 @@ +/** + * @id c/misra/array-to-pointer-conversion-of-temporary-object + * @name RULE-18-9: An object with temporary lifetime shall not undergo array to pointer conversion + * @description Modifying or accessing elements of an array with temporary lifetime that has been + * converted to a pointer will result in undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-18-9 + * external/misra/c/2012/amendment3 + * correctness + * security + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects + +/** + * Holds if the value of an expression is used or stored. + * + * For instance, `(x)` does not use any values, but `x + y` uses `x` and `y`. + * + * A pointer-to-array conversion does not need to be flagged if the result of + * that conversion is not used or stored. + */ +predicate isUsedOrStored(Expr e) { + e = any(Operation o).getAnOperand() + or + e = any(ConditionalExpr c).getCondition() + or + e = any(Call c).getAnArgument() + or + e = any(VariableDeclarationEntry d).getDeclaration().getInitializer().getExpr() + or + e = any(ClassAggregateLiteral l).getAFieldExpr(_) +} + +/** + * Find expressions that defer their value directly to an inner expression + * value. + * + * When an array is on the rhs of a comma expr, or in the then/else branch of a + * ternary expr, and the result us used as a pointer, then the ArrayToPointer + * conversion is marked inside comma expr/ternary expr, on the operands. These + * conversions are only non-compliant if they flow into an operation or store. + * + * Full flow analysis with localFlowStep should not be necessary, and may cast a + * wider net than needed for some queries, potentially resulting in false + * positives. + */ +Expr temporaryObjectFlowStep(Expr e) { + e = result.(CommaExpr).getRightOperand() + or + e = result.(ConditionalExpr).getThen() + or + e = result.(ConditionalExpr).getElse() +} + +from FieldAccess fa, TemporaryObjectIdentity temporary, ArrayToPointerConversion conversion +where + not isExcluded(conversion, InvalidMemory3Package::arrayToPointerConversionOfTemporaryObjectQuery()) and + fa = temporary.getASubobjectAccess() and + conversion.getExpr() = fa and + isUsedOrStored(temporaryObjectFlowStep*(conversion.getExpr())) +select conversion, "Array to pointer conversion of array $@ from temporary object $@.", + fa.getTarget(), fa.getTarget().getName(), temporary, temporary.toString() diff --git a/c/misra/src/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.ql b/c/misra/src/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.ql new file mode 100644 index 0000000000..5ccc8316ec --- /dev/null +++ b/c/misra/src/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.ql @@ -0,0 +1,47 @@ +/** + * @id c/misra/modifiable-l-value-subscripted-with-temporary-lifetime + * @name RULE-18-9: Usage of the subscript operator on an object with temporary lifetime shall not return a modifiable value + * @description Modifying elements of an array with temporary lifetime will result in undefined + * behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-18-9 + * external/misra/c/2012/amendment3 + * correctness + * security + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects +import codeql.util.Boolean + +predicate usedAsModifiableLvalue(Expr expr, Boolean allowArrayAccess) { + exists(Assignment parent | parent.getLValue() = expr) + or + exists(CrementOperation parent | parent.getOperand() = expr) + or + exists(AddressOfExpr parent | parent.getOperand() = expr) + or + // Don't report `x.y[0].m[0]++` twice. Recurse with `allowArrayAccess` set to false. + exists(FieldAccess parent | + parent.getQualifier() = expr and usedAsModifiableLvalue(parent, false) + ) + or + allowArrayAccess = true and + exists(ArrayExpr parent | parent.getArrayBase() = expr and usedAsModifiableLvalue(parent, true)) +} + +from ArrayExpr expr, FieldAccess fieldAccess, TemporaryObjectIdentity tempObject +where + not isExcluded(expr, + InvalidMemory3Package::modifiableLValueSubscriptedWithTemporaryLifetimeQuery()) and + expr = tempObject.getASubobjectAccess() and + fieldAccess = expr.getArrayBase() and + not expr.isUnevaluated() and + usedAsModifiableLvalue(expr, true) +select expr, + "Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ ", + fieldAccess, fieldAccess.getTarget().getName(), tempObject, tempObject.toString() diff --git a/c/misra/src/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.ql b/c/misra/src/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.ql new file mode 100644 index 0000000000..31c24dcdd8 --- /dev/null +++ b/c/misra/src/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.ql @@ -0,0 +1,55 @@ +/** + * @id c/misra/object-assigned-to-an-overlapping-object + * @name RULE-19-1: An object shall not be assigned to an overlapping object + * @description An object shall not be copied or assigned to an overlapping object. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-19-1 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +VariableAccess getAQualifier(VariableAccess va) { result = va.getQualifier+() } + +int getAccessByteOffset(FieldAccess fa) { + not fa.getQualifier() instanceof FieldAccess and result = fa.getTarget().getByteOffset() + or + result = fa.getTarget().getByteOffset() + getAccessByteOffset(fa.getQualifier()) +} + +predicate overlaps(FieldAccess fa1, FieldAccess fa2) { + exists(int startfa1, int endfa1, int startfa2, int endfa2 | + startfa1 = getAccessByteOffset(fa1) and + endfa1 = startfa1 + fa1.getTarget().getType().getSize() - 1 and + startfa2 = getAccessByteOffset(fa2) and + endfa2 = startfa2 + fa2.getTarget().getType().getSize() - 1 + | + startfa1 = startfa2 and endfa1 = endfa2 + or + startfa1 > startfa2 and endfa1 < endfa2 + or + startfa1 < startfa2 and endfa1 < endfa2 and endfa1 > startfa2 + or + startfa1 > startfa2 and endfa1 > endfa2 and startfa1 < endfa2 + ) +} + +from AssignExpr assignExpr, Expr lhs, Expr rhs, ValueFieldAccess valuelhs, ValueFieldAccess valuerhs +where + not isExcluded(assignExpr, Contracts7Package::objectAssignedToAnOverlappingObjectQuery()) and + lhs.getType() instanceof Union and + rhs.getType() instanceof Union and + lhs = getAQualifier(assignExpr.getLValue()) and + rhs = getAQualifier(assignExpr.getRValue()) and + globalValueNumber(lhs) = globalValueNumber(rhs) and + valuerhs = assignExpr.getRValue() and + valuelhs = assignExpr.getLValue() and // a.b.c == ((a.b).c) + overlaps(valuelhs, valuerhs) +select assignExpr, "An object $@ assigned to overlapping object $@.", valuelhs, + valuelhs.getTarget().getName(), valuerhs, valuerhs.getTarget().getName() diff --git a/c/misra/src/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.ql b/c/misra/src/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.ql new file mode 100644 index 0000000000..33de4f84b6 --- /dev/null +++ b/c/misra/src/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.ql @@ -0,0 +1,100 @@ +/** + * @id c/misra/object-copied-to-an-overlapping-object + * @name RULE-19-1: An object shall not be copied to an overlapping object + * @description An object shall not be copied to an overlapping object. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-19-1 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * Offset in bytes of a field access + */ +int getAccessByteOffset(FieldAccess fa) { + not fa.getQualifier() instanceof FieldAccess and result = fa.getTarget().getByteOffset() + or + result = fa.getTarget().getByteOffset() + getAccessByteOffset(fa.getQualifier()) +} + +class OverlappingCopy extends Locatable { + Expr src; + Expr dst; + + OverlappingCopy() { + this.(MacroInvocation).getMacroName() = "memcpy" and + src = this.(MacroInvocation).getExpr().getChild(1) and + dst = this.(MacroInvocation).getExpr().getChild(0) + or + this.(FunctionCall).getTarget().hasGlobalName("memcpy") and + src = this.(FunctionCall).getArgument(1) and + dst = this.(FunctionCall).getArgument(0) + } + + Expr getSrc() { result = src } + + Expr getDst() { result = dst } + + Expr getBase(Expr e) { + result = + [ + e.(VariableAccess), e.(PointerAddExpr).getLeftOperand(), + e.(AddressOfExpr).getOperand().(ArrayExpr).getArrayBase+(), + e.(AddressOfExpr).getOperand().(ValueFieldAccess).getQualifier+() + ] + } + + int getOffset(Expr e) { + result = + [ + e.(PointerAddExpr).getRightOperand().getValue().toInt(), + e.(AddressOfExpr).getOperand().(ArrayExpr).getArrayOffset().getValue().toInt(), + getAccessByteOffset(e.(AddressOfExpr).getOperand()), + ] + or + e instanceof VariableAccess and result = 0 + } + + int getCount() { + result = + upperBound([this.(MacroInvocation).getExpr().getChild(2), this.(FunctionCall).getArgument(2)]) + } + + // source and destination overlap + predicate overlaps() { + globalValueNumber(this.getBase(src)) = globalValueNumber(this.getBase(dst)) and + exists(int dstStart, int dstEnd, int srcStart, int srcEnd | + dstStart = this.getOffset(dst) and + dstEnd = dstStart + this.getCount() - 1 and + srcStart = this.getOffset(src) and + srcEnd = srcStart + this.getCount() - 1 and + ( + srcStart >= dstStart and srcEnd <= dstEnd + or + srcStart <= dstStart and srcEnd > dstStart + or + srcStart < dstEnd and srcEnd >= dstStart + ) and + // Exception 1: exact overlap and compatible type + not ( + srcStart = dstStart and + srcEnd = dstEnd and + this.getBase(src).getUnspecifiedType() = this.getBase(dst).getUnspecifiedType() + ) + ) + } +} + +from OverlappingCopy copy +where + not isExcluded(copy, Contracts7Package::objectCopiedToAnOverlappingObjectQuery()) and + copy.overlaps() +select copy, "The object to copy $@ overlaps the object to copy $@.", copy.getSrc(), "from", + copy.getDst(), "to" diff --git a/c/misra/src/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.ql b/c/misra/src/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.ql index b3028d9add..14d01c47e3 100644 --- a/c/misra/src/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.ql +++ b/c/misra/src/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.ql @@ -7,6 +7,7 @@ * @problem.severity warning * @tags external/misra/id/rule-19-2 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-2-1/UnreachableCode.ql b/c/misra/src/rules/RULE-2-1/UnreachableCode.ql new file mode 100644 index 0000000000..020338913a --- /dev/null +++ b/c/misra/src/rules/RULE-2-1/UnreachableCode.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/unreachable-code + * @name RULE-2-1: A project shall not contain unreachable code + * @description Unreachable code complicates the program and can indicate a possible mistake on the + * part of the programmer. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-1 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.unreachablecode.UnreachableCode + +class UnreachableCodeQuery extends UnreachableCodeSharedQuery { + UnreachableCodeQuery() { this = DeadCodePackage::unreachableCodeQuery() } +} diff --git a/c/misra/src/rules/RULE-2-2/DeadCode.ql b/c/misra/src/rules/RULE-2-2/DeadCode.ql new file mode 100644 index 0000000000..97c3808607 --- /dev/null +++ b/c/misra/src/rules/RULE-2-2/DeadCode.ql @@ -0,0 +1,97 @@ +/** + * @id c/misra/dead-code + * @name RULE-2-2: There shall be no dead code + * @description Dead code complicates the program and can indicate a possible mistake on the part of + * the programmer. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-2 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.alertreporting.HoldsForAllCopies +import codingstandards.cpp.deadcode.UselessAssignments + +/** + * Gets an explicit cast from `e` if one exists. + */ +Cast getExplicitCast(Expr e) { + exists(Conversion c | c = e.getExplicitlyConverted() | + result = c + or + result = c.(ParenthesisExpr).getExpr() + ) +} + +class ExprStmtExpr extends Expr { + ExprStmtExpr() { exists(ExprStmt es | es.getExpr() = this) } +} + +/** + * An "operation" as defined by MISRA C Rule 2.2 that is dead, i.e. it's removal has no effect on + * the behaviour of the program. + */ +class DeadOperationInstance extends Expr { + string description; + + DeadOperationInstance() { + // Exclude cases nested within macro expansions, because the code may be "live" in other + // expansions + isNotWithinMacroExpansion(this) and + exists(ExprStmtExpr e | + if exists(getExplicitCast(e)) + then + this = getExplicitCast(e) and + // void conversions are permitted + not getExplicitCast(e) instanceof VoidConversion and + description = "Cast operation is unused" + else ( + this = e and + ( + if e instanceof Assignment + then + exists(SsaDefinition sd, LocalScopeVariable v | + e = sd.getDefinition() and + sd.getDefiningValue(v).isPure() and + // The definition is useless + isUselessSsaDefinition(sd, v) and + description = "Assignment to " + v.getName() + " is unused and has no side effects" + ) + else ( + e.isPure() and + description = "Result of operation is unused and has no side effects" + ) + ) + ) + ) + } + + string getDescription() { result = description } +} + +class DeadOperation = HoldsForAllCopies::LogicalResultElement; + +from + DeadOperation deadOperation, DeadOperationInstance instance, string message, Element explainer, + string explainerDescription +where + not isExcluded(instance, DeadCodePackage::deadCodeQuery()) and + instance = deadOperation.getAnElementInstance() and + if instance instanceof FunctionCall + then + message = instance.getDescription() + " from call to function $@" and + explainer = instance.(FunctionCall).getTarget() and + explainerDescription = explainer.(Function).getName() + else ( + message = instance.getDescription() and + // Ignore the explainer + explainer = instance and + explainerDescription = "" + ) +select deadOperation, message + ".", explainer, explainerDescription diff --git a/c/misra/src/rules/RULE-2-3/UnusedTypeDeclarations.ql b/c/misra/src/rules/RULE-2-3/UnusedTypeDeclarations.ql new file mode 100644 index 0000000000..b4c6bbf42c --- /dev/null +++ b/c/misra/src/rules/RULE-2-3/UnusedTypeDeclarations.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/unused-type-declarations + * @name RULE-2-3: A project should not contain unused type declarations + * @description Unused type declarations are either redundant or indicate a possible mistake on the + * part of the programmer. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-3 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.unusedtypedeclarations.UnusedTypeDeclarations + +class UnusedTypeDeclarationsQuery extends UnusedTypeDeclarationsSharedQuery { + UnusedTypeDeclarationsQuery() { this = DeadCodePackage::unusedTypeDeclarationsQuery() } +} diff --git a/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql b/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql new file mode 100644 index 0000000000..9ad460068b --- /dev/null +++ b/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql @@ -0,0 +1,36 @@ +/** + * @id c/misra/unused-tag-declaration + * @name RULE-2-4: A project should not contain unused tag declarations + * @description Unused tag declarations are either redundant or indicate a possible mistake on the + * part of the programmer. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-4 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.Uses + +from UserType s +where + not isExcluded(s, DeadCodePackage::unusedTagDeclarationQuery()) and + // ignore structs without a tag name + not s.isAnonymous() and + // typedefs do not have a "tag" name, so this rule does not apply to them + not s instanceof TypedefType and + // Not mentioned anywhere + not exists(TypeMention tm | tm.getMentionedType() = s) and + // Exclude any struct that is fully generated from a macro expansion, as it may be used in other + // expansions of the same macro. + // Note: due to a bug in the CodeQL CLI version 2.9.4, this will currently have no effect, because + // `isInMacroExpansion` is broken for `UserType`s. + not s.isInMacroExpansion() and + // Exclude template parameters, in case this is run on C++ code. + not s instanceof TypeTemplateParameter +select s, "struct " + s.getName() + " has an unused tag." diff --git a/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql b/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql new file mode 100644 index 0000000000..2b5a8e8c1d --- /dev/null +++ b/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql @@ -0,0 +1,40 @@ +/** + * @id c/misra/unused-macro-declaration + * @name RULE-2-5: A project should not contain unused macro declarations + * @description Unused macro declarations are either redundant or indicate a possible mistake on the + * part of the programmer. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-5 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from Macro m +where + not isExcluded(m, DeadCodePackage::unusedMacroDeclarationQuery()) and + // We consider a macro "used" if there is a macro access + not exists(MacroAccess ma | ma.getMacro() = m) and + // Or if there exists a check whether the macro is defined which the extractor + // hasn't been able to tie to a macro (usually because this use came before + // the macro was defined e.g. a header guard) + not exists(PreprocessorBranchDirective bd | + // Covers the #ifdef and #ifndef cases + bd.getHead() = m.getName() + or + // Covers the use of defined() to check if a macro is defined + m.getName() = bd.getHead().regexpCapture(".*defined *\\(? *([^\\s()]+) *\\)?\\.*", 1) + ) and + // We consider a macro "used" if the name is undef-ed at some point in the same file, or a file + // that includes the file defining the macro. This will over approximate use in the case of a + // macro which is defined, then undefined, then re-defined but not used. + not exists(PreprocessorUndef u | + u.getName() = m.getName() and u.getFile().getAnIncludedFile*() = m.getFile() + ) +select m, "Macro " + m.getName() + " is unused." diff --git a/c/misra/src/rules/RULE-2-6/UnusedLabelDeclaration.ql b/c/misra/src/rules/RULE-2-6/UnusedLabelDeclaration.ql new file mode 100644 index 0000000000..7838c5fc1f --- /dev/null +++ b/c/misra/src/rules/RULE-2-6/UnusedLabelDeclaration.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/unused-label-declaration + * @name RULE-2-6: A function should not contain unused label declarations + * @description Unused label declarations are either redundant or indicate a possible mistake on the + * part of the programmer. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-6 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from LabelStmt label +where + not isExcluded(label, DeadCodePackage::unusedLabelDeclarationQuery()) and + // No GotoStmt jumps to this label + not exists(GotoStmt gs | gs.hasName() and gs.getTarget() = label) and + // The address of the label is never taken + not exists(LabelLiteral literal | literal.getLabel() = label) +select label, "Label " + label.getName() + " is unused." diff --git a/c/misra/src/rules/RULE-2-7/UnusedParameter.ql b/c/misra/src/rules/RULE-2-7/UnusedParameter.ql new file mode 100644 index 0000000000..e27caee50b --- /dev/null +++ b/c/misra/src/rules/RULE-2-7/UnusedParameter.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/unused-parameter + * @name RULE-2-7: There should be no unused parameters in functions + * @description Unused parameters can indicate a mistake when implementing the function. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-2-7 + * readability + * maintainability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.unusedparameter.UnusedParameter + +class UnusedParameterQuery extends UnusedParameterSharedQuery { + UnusedParameterQuery() { this = DeadCodePackage::unusedParameterQuery() } +} diff --git a/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql new file mode 100644 index 0000000000..13355b7f74 --- /dev/null +++ b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/unused-object-definition + * @name RULE-2-8: A project should not contain unused object definitions + * @description Object definitions which are unused should be removed. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-2-8 + * maintainability + * performance + * external/misra/c/2012/amendment4 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.deadcode.UnusedObjects + +from ReportDeadObject report +where + not isExcluded(report.getPrimaryElement(), DeadCode2Package::unusedObjectDefinitionQuery()) and + not report.hasAttrUnused() +select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), + report.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql new file mode 100644 index 0000000000..4eb1ad9773 --- /dev/null +++ b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/unused-object-definition-strict + * @name RULE-2-8: A project should not contain '__attribute__((unused))' object definitions + * @description A strict query which reports all unused object definitions with + * '__attribute__((unused))'. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-2-8 + * maintainability + * performance + * external/misra/c/2012/amendment4 + * external/misra/c/strict + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.deadcode.UnusedObjects + +from ReportDeadObject report +where + not isExcluded(report.getPrimaryElement(), DeadCode2Package::unusedObjectDefinitionStrictQuery()) and + report.hasAttrUnused() +select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), + report.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-20-1/IncludeDirectivesPrecededByDirectivesOrComments.ql b/c/misra/src/rules/RULE-20-1/IncludeDirectivesPrecededByDirectivesOrComments.ql index aa0d733eb2..ba78abcb5e 100644 --- a/c/misra/src/rules/RULE-20-1/IncludeDirectivesPrecededByDirectivesOrComments.ql +++ b/c/misra/src/rules/RULE-20-1/IncludeDirectivesPrecededByDirectivesOrComments.ql @@ -8,6 +8,7 @@ * @problem.severity warning * @tags external/misra/id/rule-20-1 * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-20-10/PreprocessorHashOperatorsShouldNotBeUsed.ql b/c/misra/src/rules/RULE-20-10/PreprocessorHashOperatorsShouldNotBeUsed.ql index 3d6b30f875..016589af94 100644 --- a/c/misra/src/rules/RULE-20-10/PreprocessorHashOperatorsShouldNotBeUsed.ql +++ b/c/misra/src/rules/RULE-20-10/PreprocessorHashOperatorsShouldNotBeUsed.ql @@ -8,6 +8,7 @@ * @problem.severity warning * @tags external/misra/id/rule-20-10 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ @@ -15,7 +16,7 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.hashoperatorsused.HashOperatorsUsed -class HashOperatorsUsedInQuery extends HashOperatorsUsedQuery { +class HashOperatorsUsedInQuery extends HashOperatorsUsedSharedQuery { HashOperatorsUsedInQuery() { this = Preprocessor1Package::preprocessorHashOperatorsShouldNotBeUsedQuery() } diff --git a/c/misra/src/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.ql b/c/misra/src/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.ql index 6ea7aa0a13..fc87186d3e 100644 --- a/c/misra/src/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.ql +++ b/c/misra/src/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.ql @@ -8,19 +8,16 @@ * @problem.severity warning * @tags external/misra/id/rule-20-11 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.cpp.Macro +import codingstandards.cpp.rules.macroparameterfollowinghash.MacroParameterFollowingHash -from Macro m -where - not isExcluded(m, Preprocessor2Package::moreThanOneHashOperatorInMacroDefinitionQuery()) and - exists(StringizingOperator one, TokenPastingOperator two | - one.getMacro() = m and - two.getMacro() = m and - one.getOffset() < two.getOffset() - ) -select m, "Macro definition uses an # operator followed by a ## operator." +class MoreThanOneHashOperatorInMacroDefinitionQuery extends MacroParameterFollowingHashSharedQuery { + MoreThanOneHashOperatorInMacroDefinitionQuery() { + this = Preprocessor2Package::moreThanOneHashOperatorInMacroDefinitionQuery() + } +} diff --git a/c/misra/src/rules/RULE-20-12/MacroParameterUsedAsHashOperand.ql b/c/misra/src/rules/RULE-20-12/MacroParameterUsedAsHashOperand.ql index 779c14176c..da66f66fb2 100644 --- a/c/misra/src/rules/RULE-20-12/MacroParameterUsedAsHashOperand.ql +++ b/c/misra/src/rules/RULE-20-12/MacroParameterUsedAsHashOperand.ql @@ -10,30 +10,17 @@ * @tags external/misra/id/rule-20-12 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.cpp.Macro +import codingstandards.cpp.rules.amixedusemacroargumentsubjecttoexpansion.AMixedUseMacroArgumentSubjectToExpansion -from FunctionLikeMacro m, MacroInvocation mi, int i, string expanded, string param -where - not isExcluded(m, Preprocessor2Package::macroParameterUsedAsHashOperandQuery()) and - mi = m.getAnInvocation() and - param = m.getParameter(i) and - ( - exists(TokenPastingOperator op | op.getMacro() = m and op.getOperand() = param) - or - exists(StringizingOperator op | op.getMacro() = m and op.getOperand() = param) - ) and - // An expansion that is equal to "" means the expansion is not used and is optimized away by EDG. This happens when the expanded argument is an operand to `#` or `##`. - // This check ensure there is an expansion that is used. - expanded = mi.getExpandedArgument(i) and - not expanded = "" and - exists(Macro furtherExpandedMacro | - mi.getUnexpandedArgument(i).matches(furtherExpandedMacro.getName() + "%") - ) -select m, - "Macro " + m.getName() + " contains use of parameter " + m.getParameter(i) + - " used in multiple contexts." +class MacroParameterUsedAsHashOperandQuery extends AMixedUseMacroArgumentSubjectToExpansionSharedQuery +{ + MacroParameterUsedAsHashOperandQuery() { + this = Preprocessor2Package::macroParameterUsedAsHashOperandQuery() + } +} diff --git a/c/misra/src/rules/RULE-20-2/ForbiddenCharactersInHeaderFileName.ql b/c/misra/src/rules/RULE-20-2/ForbiddenCharactersInHeaderFileName.ql index 9ef3c76c49..d9942c3e56 100644 --- a/c/misra/src/rules/RULE-20-2/ForbiddenCharactersInHeaderFileName.ql +++ b/c/misra/src/rules/RULE-20-2/ForbiddenCharactersInHeaderFileName.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-20-2 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -15,7 +16,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.preprocessorincludesforbiddenheadernames.PreprocessorIncludesForbiddenHeaderNames -class PreprocessorIncludesForbiddenHeaderNames extends PreprocessorIncludesForbiddenHeaderNamesQuery { +class PreprocessorIncludesForbiddenHeaderNames extends PreprocessorIncludesForbiddenHeaderNamesSharedQuery +{ PreprocessorIncludesForbiddenHeaderNames() { this = Preprocessor1Package::forbiddenCharactersInHeaderFileNameQuery() } diff --git a/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql b/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql index 6b9ae71120..210e081bb1 100644 --- a/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql +++ b/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql @@ -11,6 +11,7 @@ * correctness * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-20-5/UndefShouldNotBeUsed.ql b/c/misra/src/rules/RULE-20-5/UndefShouldNotBeUsed.ql index c253c795e8..15bec51bf8 100644 --- a/c/misra/src/rules/RULE-20-5/UndefShouldNotBeUsed.ql +++ b/c/misra/src/rules/RULE-20-5/UndefShouldNotBeUsed.ql @@ -8,6 +8,7 @@ * @tags external/misra/id/rule-20-5 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql b/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql index 3006b9ab15..e0fc8e4510 100644 --- a/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql +++ b/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-20-6 * readability * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -16,7 +17,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument -class FunctionLikeMacroArgsContainHashTokenCQueryQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery { +class FunctionLikeMacroArgsContainHashTokenCQueryQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery +{ FunctionLikeMacroArgsContainHashTokenCQueryQuery() { this = Preprocessor4Package::functionLikeMacroArgsContainHashTokenCQueryQuery() } diff --git a/c/misra/src/rules/RULE-20-7/MacroParameterNotEnclosedInParenthesesCQuery.ql b/c/misra/src/rules/RULE-20-7/MacroParameterNotEnclosedInParenthesesCQuery.ql index 257bc8d9f7..e557f99a18 100644 --- a/c/misra/src/rules/RULE-20-7/MacroParameterNotEnclosedInParenthesesCQuery.ql +++ b/c/misra/src/rules/RULE-20-7/MacroParameterNotEnclosedInParenthesesCQuery.ql @@ -10,6 +10,7 @@ * @tags external/misra/id/rule-20-7 * correctness * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -17,7 +18,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.macroparameternotenclosedinparentheses.MacroParameterNotEnclosedInParentheses -class MacroParameterNotEnclosedInParenthesesCQueryQuery extends MacroParameterNotEnclosedInParenthesesSharedQuery { +class MacroParameterNotEnclosedInParenthesesCQueryQuery extends MacroParameterNotEnclosedInParenthesesSharedQuery +{ MacroParameterNotEnclosedInParenthesesCQueryQuery() { this = Preprocessor5Package::macroParameterNotEnclosedInParenthesesCQueryQuery() } diff --git a/c/misra/src/rules/RULE-20-8/ControllingExpressionIfDirective.ql b/c/misra/src/rules/RULE-20-8/ControllingExpressionIfDirective.ql index 72495b5d5b..5e2c1fbc27 100644 --- a/c/misra/src/rules/RULE-20-8/ControllingExpressionIfDirective.ql +++ b/c/misra/src/rules/RULE-20-8/ControllingExpressionIfDirective.ql @@ -9,29 +9,25 @@ * @tags external/misra/id/rule-20-8 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra +import codingstandards.cpp.PreprocessorDirective /* A controlling expression is evaluated if it is not excluded (guarded by another controlling expression that is not taken). This translates to it either being taken or not taken. */ predicate isEvaluated(PreprocessorBranch b) { b.wasTaken() or b.wasNotTaken() } -class IfOrElifPreprocessorBranch extends PreprocessorBranch { - IfOrElifPreprocessorBranch() { - this instanceof PreprocessorIf or this instanceof PreprocessorElif - } -} - /** * Looks like it contains a single macro, which may be undefined */ -class SimpleMacroPreprocessorBranch extends IfOrElifPreprocessorBranch { +class SimpleMacroPreprocessorBranch extends PreprocessorIfOrElif { SimpleMacroPreprocessorBranch() { this.getHead().regexpMatch("[a-zA-Z_][a-zA-Z0-9_]+") } } -class SimpleNumericPreprocessorBranch extends IfOrElifPreprocessorBranch { +class SimpleNumericPreprocessorBranch extends PreprocessorIfOrElif { SimpleNumericPreprocessorBranch() { this.getHead().regexpMatch("[0-9]+") } } @@ -39,7 +35,7 @@ class ZeroOrOnePreprocessorBranch extends SimpleNumericPreprocessorBranch { ZeroOrOnePreprocessorBranch() { this.getHead().regexpMatch("[0|1]") } } -predicate containsOnlySafeOperators(IfOrElifPreprocessorBranch b) { +predicate containsOnlySafeOperators(PreprocessorIfOrElif b) { containsOnlyDefinedOperator(b) or //logic: comparison operators eval last, so they make it safe? @@ -47,7 +43,7 @@ predicate containsOnlySafeOperators(IfOrElifPreprocessorBranch b) { } //all defined operators is definitely safe -predicate containsOnlyDefinedOperator(IfOrElifPreprocessorBranch b) { +predicate containsOnlyDefinedOperator(PreprocessorIfOrElif b) { forall(string portion | portion = b.getHead() @@ -65,7 +61,7 @@ class BinaryValuedMacro extends Macro { BinaryValuedMacro() { this.getBody().regexpMatch("\\(?(0|1)\\)?") } } -from IfOrElifPreprocessorBranch b, string msg +from PreprocessorIfOrElif b, string msg where not isExcluded(b, Preprocessor3Package::controllingExpressionIfDirectiveQuery()) and isEvaluated(b) and diff --git a/c/misra/src/rules/RULE-20-9/IdentifiersUsedInPreprocessorExpression.ql b/c/misra/src/rules/RULE-20-9/IdentifiersUsedInPreprocessorExpression.ql index df01ec00e4..be6f3c00f3 100644 --- a/c/misra/src/rules/RULE-20-9/IdentifiersUsedInPreprocessorExpression.ql +++ b/c/misra/src/rules/RULE-20-9/IdentifiersUsedInPreprocessorExpression.ql @@ -10,6 +10,7 @@ * @tags external/misra/id/rule-20-9 * correctness * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -17,7 +18,7 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.undefinedmacroidentifiers.UndefinedMacroIdentifiers -class UndefinedMacroIdentifiersUsedInQuery extends UndefinedMacroIdentifiersQuery { +class UndefinedMacroIdentifiersUsedInQuery extends UndefinedMacroIdentifiersSharedQuery { UndefinedMacroIdentifiersUsedInQuery() { this = Preprocessor1Package::identifiersUsedInPreprocessorExpressionQuery() } diff --git a/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql b/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql index b37b5cb92e..86d8426df8 100644 --- a/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql +++ b/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql @@ -10,6 +10,7 @@ * correctness * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.ql b/c/misra/src/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.ql index bd79074f3e..0ad9c350f2 100644 --- a/c/misra/src/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.ql +++ b/c/misra/src/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.ql @@ -7,6 +7,7 @@ * @problem.severity error * @tags external/misra/id/rule-21-10 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -25,4 +26,4 @@ where f.getFile().getBaseName() = "wchar.h" ) ) -select fc, "Call to banned function $@.", f, f.getName() +select fc, "Call to banned function " + f.getName() + "." diff --git a/c/misra/src/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.ql b/c/misra/src/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.ql index f0903a3d52..50c4d48cb6 100644 --- a/c/misra/src/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.ql +++ b/c/misra/src/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.ql @@ -7,7 +7,8 @@ * @problem.severity error * @tags external/misra/id/rule-21-11 * correctness - * external/misra/obligation/required + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory */ import cpp @@ -17,5 +18,6 @@ from Macro m, MacroInvocation mi where not isExcluded(mi, BannedPackage::standardHeaderFileTgmathhUsedQuery()) and mi.getMacro() = m and - m.getFile().getBaseName() = "tgmath.h" -select mi, "Call to banned macro $@.", m, m.getName() + m.getFile().getBaseName() = "tgmath.h" and + not mi.getParentInvocation().getMacro().getFile().getBaseName() = "tgmath.h" +select mi, "Call to banned macro " + m.getName() + "." diff --git a/c/misra/src/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.ql b/c/misra/src/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.ql index 43a46a7e22..b8d17de8aa 100644 --- a/c/misra/src/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.ql +++ b/c/misra/src/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.ql @@ -8,6 +8,7 @@ * @problem.severity warning * @tags external/misra/id/rule-21-12 * correctness + * external/misra/c/2012/amendment2 * external/misra/obligation/advisory */ @@ -17,7 +18,8 @@ import codingstandards.c.misra class FPExceptionHandlingFunction extends Function { FPExceptionHandlingFunction() { this.hasName([ - "feclearexcept", "fegetexceptflag", "feraiseexcept", "fesetexceptflag", "fetestexcept" + "feclearexcept", "fegetexceptflag", "feraiseexcept", "fesetexceptflag", "fetestexcept", + "fesetenv", "feupdateenv", "fesetround" ]) and this.getFile().getBaseName() = "fenv.h" } @@ -32,22 +34,30 @@ class FPExceptionHandlingMacro extends Macro { } } -from Locatable call, Locatable def, string name, string kind +from Locatable element, string name, string message where - not isExcluded(call, BannedPackage::exceptionHandlingFeaturesOfFenvhUsedQuery()) and + not isExcluded(element, BannedPackage::exceptionHandlingFeaturesOfFenvhUsedQuery()) and ( + exists(Include include | + include.getIncludedFile().getBaseName() = "fenv.h" and + message = "Include of banned header" and + name = "fenv.h" and + element = include + ) + or exists(FPExceptionHandlingFunction f | - def = f and - call = f.getACallToThisFunction() and + element = f.getACallToThisFunction() and name = f.getName() and - kind = "function" + message = "Call to banned function" ) or exists(FPExceptionHandlingMacro m | - def = m and - call = m.getAnInvocation() and + element = m.getAnInvocation() and name = m.getName() and - kind = "macro" + message = "Expansion of banned macro" and + // Exclude macro invocations expanded from other macro invocations from macros in fenv.h. + not element.(MacroInvocation).getParentInvocation().getMacro().getFile().getBaseName() = + "fenv.h" ) ) -select call, "Call to banned " + kind + " $@.", def, name +select element, message + " '" + name + "'." diff --git a/c/misra/src/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.ql b/c/misra/src/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.ql new file mode 100644 index 0000000000..b7ccf534fa --- /dev/null +++ b/c/misra/src/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.ql @@ -0,0 +1,29 @@ +/** + * @id c/misra/ctype-function-arg-not-unsigned-char-or-eof + * @name RULE-21-13: function arguments shall be represented as unsigned char + * @description Passing arguments to functions outside the range of unsigned char or EOF + * causes undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-13 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.CharFunctions +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from UseOfToOrIsChar ctypeCall +where + not isExcluded(ctypeCall, + StandardLibraryFunctionTypesPackage::ctypeFunctionArgNotUnsignedCharOrEofQuery()) and + not exists(Expr ctypeCallArgument | ctypeCallArgument = ctypeCall.getConvertedArgument() | + /* The argument's value should be in the EOF + `unsigned char` range. */ + -1 <= lowerBound(ctypeCallArgument) and upperBound(ctypeCallArgument) <= 255 + ) +select ctypeCall, + "The function " + ctypeCall + " accepts an argument " + ctypeCall.getConvertedArgument() + + " that is not an unsigned char nor an EOF." diff --git a/c/misra/src/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.ql b/c/misra/src/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.ql new file mode 100644 index 0000000000..b487f5b9b5 --- /dev/null +++ b/c/misra/src/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.ql @@ -0,0 +1,82 @@ +/** + * @id c/misra/memcmp-used-to-compare-null-terminated-strings + * @name RULE-21-14: The Standard Library function memcmp shall not be used to compare null terminated strings + * @description Using memcmp to compare null terminated strings may give unexpected results because + * memcmp compares by size with no consideration for the null terminator. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-14 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import semmle.code.cpp.dataflow.TaintTracking +import NullTerminatedStringToMemcmpFlow::PathGraph + +// Data flow from a StringLiteral or from an array of characters, to a memcmp call +module NullTerminatedStringToMemcmpConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof StringLiteral + or + exists(Variable v, ArrayAggregateLiteral aal | + aal = v.getInitializer().getExpr() and + // The array element type is an essentially character type + getEssentialTypeCategory(aal.getElementType()) = EssentiallyCharacterType() and + // Includes a null terminator somewhere in the array initializer + aal.getAnElementExpr(_).getValue().toInt() = 0 + | + // For local variables, use the array aggregate literal as the source + aal = source.asExpr() + or + // ArrayAggregateLiterals used as initializers for global variables are not viable sources + // for global data flow, so we instead report variable accesses as sources, where the variable + // is constant or is not assigned in the program + v instanceof GlobalVariable and + source.asExpr() = v.getAnAccess() and + ( + v.isConst() + or + not exists(Expr e | e = v.getAnAssignedValue() and not e = aal) + ) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(FunctionCall memcmp | + memcmp.getTarget().hasGlobalOrStdName("memcmp") and + sink.asExpr() = memcmp.getArgument([0, 1]) + ) + } +} + +module NullTerminatedStringToMemcmpFlow = TaintTracking::Global; + +from + FunctionCall memcmp, NullTerminatedStringToMemcmpFlow::PathNode source, + NullTerminatedStringToMemcmpFlow::PathNode sink, + NullTerminatedStringToMemcmpFlow::PathNode source1, + NullTerminatedStringToMemcmpFlow::PathNode arg1, + NullTerminatedStringToMemcmpFlow::PathNode source2, + NullTerminatedStringToMemcmpFlow::PathNode arg2 +where + not isExcluded(memcmp, EssentialTypesPackage::memcmpUsedToCompareNullTerminatedStringsQuery()) and + memcmp.getTarget().hasGlobalOrStdName("memcmp") and + arg1.getNode().asExpr() = memcmp.getArgument(0) and + arg2.getNode().asExpr() = memcmp.getArgument(1) and + // There is a path from a null-terminated string to each argument + NullTerminatedStringToMemcmpFlow::flowPath(source1, arg1) and + NullTerminatedStringToMemcmpFlow::flowPath(source2, arg2) and + // Produce multiple paths for each result, one for each source/arg pair + ( + source = source1 and sink = arg1 + or + source = source2 and sink = arg2 + ) +select memcmp, source, sink, "memcmp used to compare $@ with $@.", source1, + "null-terminated string", source2, "null-terminated string" diff --git a/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql b/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql new file mode 100644 index 0000000000..28dce7b638 --- /dev/null +++ b/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql @@ -0,0 +1,47 @@ +/** + * @id c/misra/memcpy-memmove-memcmp-arg-not-pointers-to-compatible-types + * @name RULE-21-15: The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers + * @description Passing pointers to incompatible types as arguments to memcpy, memmove and memcmp + * indicates programmers' confusion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-15 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.Pointers + +class MemCmpMoveCpy extends Function { + // Couldn't extend BuiltInFunction because it misses `memcmp` + MemCmpMoveCpy() { + this.getName().regexpMatch("mem(cmp|cpy|move)") and + this.getADeclaration().getAFile().(HeaderFile).getBaseName() = "string.h" + } +} + +from FunctionCall fc +where + not isExcluded(fc, + StandardLibraryFunctionTypesPackage::memcpyMemmoveMemcmpArgNotPointersToCompatibleTypesQuery()) and + exists(MemCmpMoveCpy memfun, Type dstType, Type srcType | fc.getTarget() = memfun | + dstType = fc.getArgument(0).getUnspecifiedType() and + srcType = fc.getArgument(1).getUnspecifiedType() and + ( + /* Case 1: dst and src are pointer types */ + dstType instanceof PointerType and + srcType instanceof PointerType + or + /* Case 2: dst and src are array types */ + dstType instanceof ArrayType and + srcType instanceof ArrayType + ) and + not dstType = srcType + ) +select fc, + "The dest type " + fc.getArgument(0).getUnspecifiedType() + " and src type " + + fc.getArgument(1).getUnspecifiedType() + " of function " + fc.getTarget() + + " are not compatible." diff --git a/c/misra/src/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.ql b/c/misra/src/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.ql new file mode 100644 index 0000000000..cb70567660 --- /dev/null +++ b/c/misra/src/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.ql @@ -0,0 +1,47 @@ +/** + * @id c/misra/memcmp-on-inappropriate-essential-type-args + * @name RULE-21-16: Do not use memcmp on pointers to characters or composite types such as structs and unions + * @description The pointer arguments to the Standard Library function memcmp shall point to either + * a pointer type, an essentially signed type, an essentially unsigned type, an + * essentially Boolean type or an essentially enum type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-16 + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from FunctionCall memcmp, Expr arg, Type argBaseType +where + not isExcluded(arg, EssentialTypesPackage::memcmpOnInappropriateEssentialTypeArgsQuery()) and + memcmp.getTarget().hasGlobalOrStdName("memcmp") and + // Pointer arguments + arg = memcmp.getArgument([0, 1]) and + exists(DerivedType pt | + // Must be a pointer type or array type + ( + pt instanceof PointerType or + pt instanceof ArrayType + ) and + pt = arg.getType() and + argBaseType = pt.getBaseType() and + // Doesn't point to a pointer type + not argBaseType instanceof PointerType and + // Doesn't point to a type which is essentially signed, unsigned, boolean or enum + not exists(EssentialTypeCategory typeCategory | + typeCategory = getEssentialTypeCategory(argBaseType) + | + typeCategory = EssentiallySignedType() or + typeCategory = EssentiallyUnsignedType() or + typeCategory = EssentiallyBooleanType() or + typeCategory = EssentiallyEnumType() + ) + ) +select arg, "Argument is a pointer to " + argBaseType + "." diff --git a/c/misra/src/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.ql b/c/misra/src/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.ql new file mode 100644 index 0000000000..31d3434c58 --- /dev/null +++ b/c/misra/src/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.ql @@ -0,0 +1,28 @@ +/** + * @id c/misra/string-function-pointer-argument-out-of-bounds + * @name RULE-21-17: Use of the string handling functions from shall not result in accesses beyond the bounds + * @description Use of string manipulation functions from with improper buffer sizes can + * result in out-of-bounds buffer accesses. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-21-17 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.OutOfBounds + +class RULE_21_17_Subset_FC = OOB::SimpleStringLibraryFunctionCall; + +from + RULE_21_17_Subset_FC fc, string message, Expr bufferArg, string bufferArgStr, + Expr sizeOrOtherBufferArg, string otherStr +where + not isExcluded(fc, OutOfBoundsPackage::stringFunctionPointerArgumentOutOfBoundsQuery()) and + OOB::problems(fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr) +select fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr diff --git a/c/misra/src/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.ql b/c/misra/src/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.ql new file mode 100644 index 0000000000..22ccc14b69 --- /dev/null +++ b/c/misra/src/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.ql @@ -0,0 +1,36 @@ +/** + * @id c/misra/string-library-size-argument-out-of-bounds + * @name RULE-21-18: The size_t argument passed to any function in shall have an appropriate value + * @description Passing a size_t argument that is non-positive or greater than the size of the + * smallest buffer argument to any function in may result in out-of-bounds + * buffer accesses. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-21-18 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.OutOfBounds + +class RULE_21_18_Subset_FC extends OOB::BufferAccessLibraryFunctionCall { + RULE_21_18_Subset_FC() { + this.getTarget().getName() = + OOB::getNameOrInternalName([ + "mem" + ["chr", "cmp", "cpy", "move", "set"], "str" + ["ncat", "ncmp", "ncpy", "xfrm"] + ]) + } +} + +from + RULE_21_18_Subset_FC fc, string message, Expr bufferArg, string bufferArgStr, + Expr sizeOrOtherBufferArg, string otherStr +where + not isExcluded(fc, OutOfBoundsPackage::stringLibrarySizeArgumentOutOfBoundsQuery()) and + OOB::problems(fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr) +select fc, message, bufferArg, bufferArgStr, sizeOrOtherBufferArg, otherStr diff --git a/c/misra/src/rules/RULE-21-19/ValuesReturnedByLocaleSettingUsedAsPtrToConst.ql b/c/misra/src/rules/RULE-21-19/ValuesReturnedByLocaleSettingUsedAsPtrToConst.ql index 8643918ab9..6fa3ad92be 100644 --- a/c/misra/src/rules/RULE-21-19/ValuesReturnedByLocaleSettingUsedAsPtrToConst.ql +++ b/c/misra/src/rules/RULE-21-19/ValuesReturnedByLocaleSettingUsedAsPtrToConst.ql @@ -4,11 +4,12 @@ * @description The pointers returned by the Standard Library functions localeconv, getenv, * setlocale or, strerror shall only be used as if they have pointer to const-qualified * type. - * @kind problem + * @kind path-problem * @precision very-high * @problem.severity error * @tags external/misra/id/rule-21-19 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/mandatory */ diff --git a/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql b/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql index 89140222da..80ad8386bc 100644 --- a/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql +++ b/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql @@ -9,6 +9,7 @@ * correctness * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointers.ql b/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointers.ql index c193e899db..6441add7fc 100644 --- a/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointers.ql +++ b/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointers.ql @@ -9,6 +9,7 @@ * @problem.severity error * @tags external/misra/id/rule-21-20 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/mandatory */ diff --git a/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointersWarn.ql b/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointersWarn.ql index ba1e75cd4c..e7e97e2639 100644 --- a/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointersWarn.ql +++ b/c/misra/src/rules/RULE-21-20/CallToSetlocaleInvalidatesOldPointersWarn.ql @@ -9,6 +9,7 @@ * @problem.severity warning * @tags external/misra/id/rule-21-20 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/mandatory */ @@ -16,7 +17,8 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.rules.invalidatedenvstringpointerswarn.InvalidatedEnvStringPointersWarn -class CallToSetlocaleInvalidatesOldPointersWarnQuery extends InvalidatedEnvStringPointersWarnSharedQuery { +class CallToSetlocaleInvalidatesOldPointersWarnQuery extends InvalidatedEnvStringPointersWarnSharedQuery +{ CallToSetlocaleInvalidatesOldPointersWarnQuery() { this = Contracts2Package::callToSetlocaleInvalidatesOldPointersWarnQuery() } diff --git a/c/misra/src/rules/RULE-21-21/SystemOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-21/SystemOfStdlibhUsed.ql index 1b5e8fcaa3..81dd6ba1a3 100644 --- a/c/misra/src/rules/RULE-21-21/SystemOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-21/SystemOfStdlibhUsed.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-21-21 * security + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -19,4 +20,4 @@ where not isExcluded(call, BannedPackage::systemOfStdlibhUsedQuery()) and call.getTarget() = target and target.hasGlobalOrStdName("system") -select call, "Call to banned function $@.", target, target.getName() +select call, "Call to banned function " + target.getName() + "." diff --git a/c/misra/src/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.ql b/c/misra/src/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.ql new file mode 100644 index 0000000000..fc8565ade5 --- /dev/null +++ b/c/misra/src/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.ql @@ -0,0 +1,52 @@ +/** + * @id c/misra/tg-math-argument-with-invalid-essential-type + * @name RULE-21-22: All operand arguments to type-generic macros in shall have an appropriate essential type + * @description All operand arguments to any type-generic macros in shall have an + * appropriate essential type. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-21-22 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.TgMath +import codingstandards.c.misra.EssentialTypes + +EssentialTypeCategory getAnAllowedEssentialTypeCategory(TgMathInvocation call) { + result = EssentiallySignedType() + or + result = EssentiallyUnsignedType() + or + result = EssentiallyFloatingType(Real()) + or + call.allowsComplex() and + result = EssentiallyFloatingType(Complex()) +} + +string getAllowedTypesString(TgMathInvocation call) { + if call.allowsComplex() + then result = "essentially signed, unsigned, or floating type" + else result = "essentially signed, unsigned, or real floating type" +} + +from + TgMathInvocation call, Expr convertedArg, Expr unconverted, int argIndex, Type type, + EssentialTypeCategory category +where + not isExcluded(call, EssentialTypes2Package::tgMathArgumentWithInvalidEssentialTypeQuery()) and + // We must handle conversions specially, as clang inserts casts in the macro body we want to ignore. + convertedArg = call.getExplicitlyConvertedOperandArgument(argIndex) and + unconverted = convertedArg.getUnconverted() and + // Do not use `convertedArg.getEssentialType()`, as that is affected by clang's casts in the macro body. + type = getEssentialTypeBeforeConversions(convertedArg) and + category = getEssentialTypeCategory(type) and + not category = getAnAllowedEssentialTypeCategory(call) +select unconverted, + "Argument " + (argIndex + 1) + " provided to type-generic macro '" + call.getMacroName() + + "' has " + category.toString().toLowerCase() + ", which is not " + getAllowedTypesString(call) + + "." diff --git a/c/misra/src/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.ql b/c/misra/src/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.ql new file mode 100644 index 0000000000..34d3b62b2c --- /dev/null +++ b/c/misra/src/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.ql @@ -0,0 +1,73 @@ +/** + * @id c/misra/tg-math-arguments-with-differing-standard-type + * @name RULE-21-23: Operand arguments for an invocation of a type-generic macro shall have the same standard type + * @description All operand arguments to any multi-argument type-generic macros in shall + * have the same standard type. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-21-23 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.TgMath + +string argTypesString(TgMathInvocation call, int i) { + exists(string typeStr | + typeStr = getEffectiveStandardType(call.getOperandArgument(i)).toString() and + ( + i = 0 and result = typeStr + or + i > 0 and result = argTypesString(call, i - 1) + ", " + typeStr + ) + ) +} + +/** + * If the range of values can be represented as a signed int, it is promoted to signed int. + * + * A value may also promote to unsigned int but only if `int` cannot represent the range of + * values. Which basically means only an `unsigned int` promotes to `unsigned int`, so we don't + * need to do anything in this case. + * + * An unsigned int bitfield with fewer than 32 bits is promoted to `int`. + */ +predicate promotesToSignedInt(Expr e) { + exists(int intBits, int intBytes | + intBytes = any(IntType t).getSize() and + intBits = intBytes * 8 and + ( + e.(FieldAccess).getTarget().(BitField).getNumBits() < intBits + or + e.getUnderlyingType().(IntegralType).getSize() < intBytes + ) + ) +} + +Type getPromotedType(Expr e) { + if promotesToSignedInt(e) then result.(IntType).isSigned() else result = e.getUnderlyingType() +} + +Type canonicalize(Type type) { + if type instanceof IntegralType + then result = type.(IntegralType).getCanonicalArithmeticType() + else result = type +} + +Type getEffectiveStandardType(Expr e) { result = canonicalize(getPromotedType(e)) } + +from TgMathInvocation call, Type firstType +where + not isExcluded(call, EssentialTypes2Package::tgMathArgumentsWithDifferingStandardTypeQuery()) and + firstType = getEffectiveStandardType(call.getExplicitlyConvertedOperandArgument(0)) and + not forall(Expr arg | arg = call.getExplicitlyConvertedOperandArgument(_) | + firstType = getEffectiveStandardType(arg) + ) +select call, + "Call to type-generic macro '" + call.getMacroName() + + "' has arguments with differing standard types (" + + argTypesString(call, call.getNumberOfOperandArguments() - 1) + ")." diff --git a/c/misra/src/rules/RULE-21-24/CallToBannedRandomFunction.ql b/c/misra/src/rules/RULE-21-24/CallToBannedRandomFunction.ql new file mode 100644 index 0000000000..8066cc80cb --- /dev/null +++ b/c/misra/src/rules/RULE-21-24/CallToBannedRandomFunction.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/call-to-banned-random-function + * @name RULE-21-24: The random number generator functions of shall not be used + * @description The standard functions rand() and srand() will not give high quality random results + * in all implementations and are therefore banned. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-21-24 + * security + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from FunctionCall call, string name +where + not isExcluded(call, Banned2Package::callToBannedRandomFunctionQuery()) and + name = ["rand", "srand"] and + call.getTarget().hasGlobalOrStdName(name) +select call, "Call to banned random number generation function '" + name + "'." diff --git a/c/misra/src/rules/RULE-21-25/InvalidMemoryOrderArgument.ql b/c/misra/src/rules/RULE-21-25/InvalidMemoryOrderArgument.ql new file mode 100644 index 0000000000..684b4e50cb --- /dev/null +++ b/c/misra/src/rules/RULE-21-25/InvalidMemoryOrderArgument.ql @@ -0,0 +1,110 @@ +/** + * @id c/misra/invalid-memory-order-argument + * @name RULE-21-25: All memory synchronization operations shall be executed in sequentially consistent order + * @description Only the memory ordering of 'memory_order_seq_cst' is fully portable and consistent. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-25 + * external/misra/c/2012/amendment4 + * correctness + * concurrency + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.StdFunctionOrMacro +import semmle.code.cpp.dataflow.new.DataFlow + +class MemoryOrderEnum extends Enum { + MemoryOrderEnum() { + this.hasGlobalOrStdName("memory_order") + or + exists(TypedefType t | + t.getName() = "memory_order" and + t.getBaseType() = this + ) + } +} + +/* A member of the set of memory orders defined in the `memory_order` enum */ +class MemoryOrder extends EnumConstant { + MemoryOrder() { getDeclaringEnum() instanceof MemoryOrderEnum } + + int getIntValue() { result = getValue().toInt() } +} + +/* This is the only standardized memory order, allowed by RULE-21-25. */ +class AllowedMemoryOrder extends MemoryOrder { + AllowedMemoryOrder() { getName() = "memory_order_seq_cst" } +} + +/* An expression referring to a memory order */ +class MemoryOrderConstantAccess extends EnumConstantAccess { + MemoryOrderConstantAccess() { getTarget() instanceof MemoryOrder } + + predicate isAllowedOrder() { getTarget() instanceof AllowedMemoryOrder } +} + +/* An expression with a constant value that equals a `MemoryOrder` constant */ +class MemoryOrderConstantExpr extends Expr { + MemoryOrder ord; + + MemoryOrderConstantExpr() { + if this instanceof MemoryOrderConstantAccess + then ord = this.(MemoryOrderConstantAccess).getTarget() + else ord.getIntValue() = getValue().toInt() + } + + /* Get the name of the `MemoryOrder` this expression is valued as. */ + string getMemoryOrderString() { result = ord.getName() } +} + +module MemoryOrderFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { + // Direct usage of memory order constant + exists(MemoryOrderConstantAccess constant | + node.asExpr() = constant and + not constant.isAllowedOrder() + ) + or + // A literal with a disallowed constant integer value + exists(Literal literal | + node.asExpr() = literal and + not literal.getValue().toInt() = any(AllowedMemoryOrder a).getValue().toInt() + ) + } + + predicate isSink(DataFlow::Node node) { + exists(AtomicallySequencedCall call | call.getAMemoryOrderArgument() = node.asExpr()) + } +} + +module MemoryOrderFlow = DataFlow::Global; + +import MemoryOrderFlow::PathGraph + +/** + * If the node is a memory order constant, or shares a value with a memory order constant, then + * return the name of that constant. Otherwise, simply print the node. + */ +string describeMemoryOrderNode(DataFlow::Node node) { + if node.asExpr() instanceof MemoryOrderConstantExpr + then result = node.asExpr().(MemoryOrderConstantExpr).getMemoryOrderString() + else result = node.toString() +} + +from + Expr argument, AtomicallySequencedCall function, string value, MemoryOrderFlow::PathNode source, + MemoryOrderFlow::PathNode sink +where + not isExcluded(argument, Concurrency6Package::invalidMemoryOrderArgumentQuery()) and + MemoryOrderFlow::flowPath(source, sink) and + argument = sink.getNode().asExpr() and + value = describeMemoryOrderNode(source.getNode()) and + // Double check that we didn't find flow from something equivalent to the allowed value. + not value = any(AllowedMemoryOrder e).getName() and + function.getAMemoryOrderArgument() = argument +select argument, source, sink, "Invalid memory order '$@' in call to function '$@'.", value, value, + function, function.getName() diff --git a/c/misra/src/rules/RULE-21-26/TimedlockOnInappropriateMutexType.ql b/c/misra/src/rules/RULE-21-26/TimedlockOnInappropriateMutexType.ql new file mode 100644 index 0000000000..929eb5bd0a --- /dev/null +++ b/c/misra/src/rules/RULE-21-26/TimedlockOnInappropriateMutexType.ql @@ -0,0 +1,76 @@ +/** + * @id c/misra/timedlock-on-inappropriate-mutex-type + * @name RULE-21-26: The Standard Library function mtx_timedlock() shall only be invoked on mutexes of type mtx_timed + * @description The Standard Library function mtx_timedlock() shall only be invoked on mutex objects + * of appropriate mutex type. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-21-26 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.dataflow.new.DataFlow + +class MutexTimed extends EnumConstant { + MutexTimed() { hasName("mtx_timed") } +} + +class MutexInitCall extends FunctionCall { + Expr mutexExpr; + Expr mutexTypeExpr; + + MutexInitCall() { + getTarget().hasName("mtx_init") and + mutexExpr = getArgument(0) and + mutexTypeExpr = getArgument(1) + } + + predicate isTimedMutexType() { + exists(EnumConstantAccess baseTypeAccess | + ( + baseTypeAccess = mutexTypeExpr + or + baseTypeAccess = mutexTypeExpr.(BinaryBitwiseOperation).getAnOperand() + ) and + baseTypeAccess.getTarget() instanceof MutexTimed + ) + or + mutexTypeExpr.getValue().toInt() = any(MutexTimed m).getValue().toInt() + } + + Expr getMutexExpr() { result = mutexExpr } +} + +module MutexTimedlockFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { + exists(MutexInitCall init | + node.asDefiningArgument() = init.getMutexExpr() and not init.isTimedMutexType() + ) + } + + predicate isSink(DataFlow::Node node) { + exists(FunctionCall fc | + fc.getTarget().hasName("mtx_timedlock") and + node.asIndirectExpr() = fc.getArgument(0) + ) + } +} + +module Flow = DataFlow::Global; + +import Flow::PathGraph + +from Flow::PathNode source, Flow::PathNode sink +where + not isExcluded(sink.getNode().asExpr(), + Concurrency7Package::timedlockOnInappropriateMutexTypeQuery()) and + Flow::flowPath(source, sink) +select sink.getNode(), source, sink, + "Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'.", source.getNode(), + "initialized" diff --git a/c/misra/src/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.ql index ed3317b696..ab3ba3e328 100644 --- a/c/misra/src/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-21-3 * correctness * security + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.ql b/c/misra/src/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.ql index 22eca266b3..88ad0aa6db 100644 --- a/c/misra/src/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.ql +++ b/c/misra/src/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.ql @@ -7,40 +7,48 @@ * @problem.severity error * @tags external/misra/id/rule-21-4 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -class SetJmp extends Macro { +abstract class Jmp extends Locatable { + string name; + + Jmp() { + this.getFile().getAbsolutePath().matches("%setjmp.h") and + name = [this.(Macro).getName(), this.(Function).getName()] + } + + Locatable getAnInvocation() { + result = this.(Macro).getAnInvocation() or + result = this.(Function).getACallToThisFunction() + } + + string getName() { result = name } +} + +class SetJmp extends Jmp { SetJmp() { - this.hasName("setjmp") and - this.getFile().getAbsolutePath().matches("%setjmp.h") + name = "setjmp" and + this.(Macro).getName() = name } } -class LongJmp extends Function { +class LongJmp extends Jmp { LongJmp() { - this.hasName("longjmp") and - this.getFile().getAbsolutePath().matches("%setjmp.h") + name = "longjmp" and + [this.(Macro).getName(), this.(Function).getName()] = name } } -from Locatable use, Locatable feature, string name +from Locatable use, string name where not isExcluded(use, BannedPackage::standardHeaderFileUsedSetjmphQuery()) and - ( - exists(SetJmp setjmp | - feature = setjmp and - use = setjmp.getAnInvocation() and - name = "setjmp" - ) - or - exists(LongJmp longjmp | - feature = longjmp and - use = longjmp.getACallToThisFunction() and - name = "longjmp" - ) + exists(Jmp jmp | + use = jmp.getAnInvocation() and + name = jmp.getName() ) -select use, "Use of $@.", feature, name +select use, "Use of " + name + "." diff --git a/c/misra/src/rules/RULE-21-5/StandardHeaderFileUsedSignalh.ql b/c/misra/src/rules/RULE-21-5/StandardHeaderFileUsedSignalh.ql index edd05fd1aa..d22ee55742 100644 --- a/c/misra/src/rules/RULE-21-5/StandardHeaderFileUsedSignalh.ql +++ b/c/misra/src/rules/RULE-21-5/StandardHeaderFileUsedSignalh.ql @@ -7,6 +7,7 @@ * @problem.severity error * @tags external/misra/id/rule-21-5 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -18,4 +19,4 @@ where not isExcluded(fc, BannedPackage::standardHeaderFileUsedSignalhQuery()) and fc.getTarget() = f and f.getFile().getBaseName() = "signal.h" -select fc, "Call to banned function $@.", f, f.getName() +select fc, "Call to banned function " + f.getName() + "." diff --git a/c/misra/src/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.ql b/c/misra/src/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.ql index 008d8be1e3..6395ddc5ac 100644 --- a/c/misra/src/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.ql +++ b/c/misra/src/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-21-6 * security * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -40,7 +41,7 @@ private string wcharInputOutput() { from FunctionCall fc, Function f where - not isExcluded(fc, BannedPackage::standardHeaderFileUsedSignalhQuery()) and + not isExcluded(fc, BannedPackage::standardLibraryInputoutputFunctionsUsedQuery()) and fc.getTarget() = f and ( f.getName() = stdInputOutput() and @@ -49,4 +50,4 @@ where f.getName() = wcharInputOutput() and f.getFile().getBaseName() = "wchar.h" ) -select fc, "Call to banned function $@.", f, f.getName() +select fc, "Call to banned function " + f.getName() + "." diff --git a/c/misra/src/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.ql index 817ff2ae8c..ce781403b1 100644 --- a/c/misra/src/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.ql @@ -8,18 +8,16 @@ * @problem.severity error * @tags external/misra/id/rule-21-7 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra +import codingstandards.cpp.rules.atofatoiatolandatollused.AtofAtoiAtolAndAtollUsed -private string atoi() { result = ["atof", "atoi", "atol", "atoll"] } - -from FunctionCall fc, Function f -where - not isExcluded(fc, BannedPackage::atofAtoiAtolAndAtollOfStdlibhUsedQuery()) and - f = fc.getTarget() and - f.getName() = atoi() and - f.getFile().getBaseName() = "stdlib.h" -select fc, "Call to banned function $@.", f, f.getName() +class AtofAtoiAtolAndAtollOfStdlibhUsedQuery extends AtofAtoiAtolAndAtollUsedSharedQuery { + AtofAtoiAtolAndAtollOfStdlibhUsedQuery() { + this = BannedPackage::atofAtoiAtolAndAtollOfStdlibhUsedQuery() + } +} diff --git a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql index 104fa80a78..cbc7dd5a92 100644 --- a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-21-8 * correctness * security + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -26,4 +27,4 @@ from FunctionCall fc, BannedFunction f where not isExcluded(fc, BannedPackage::terminationFunctionsOfStdlibhUsedQuery()) and f = fc.getTarget() -select fc, "Call to banned function $@.", f, f.getName() +select fc, "Call to banned function " + f.getName() + "." diff --git a/c/misra/src/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.ql index 8a3c2802bf..7a911c1525 100644 --- a/c/misra/src/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-21-8 * correctness * security + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -23,4 +24,4 @@ from MacroInvocation mi, BannedMacro m where not isExcluded(mi, BannedPackage::terminationMacrosOfStdlibhUsedQuery()) and m.getAnInvocation() = mi -select mi, "Use of banned macro $@.", m, m.getName() +select mi, "Use of banned macro " + m.getName() + "." diff --git a/c/misra/src/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.ql index 5685c51a03..6759fa93d1 100644 --- a/c/misra/src/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-21-9 * security * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -17,8 +18,8 @@ import codingstandards.c.misra from FunctionCall fc, Function f where - not isExcluded(fc, BannedPackage::terminationFunctionsOfStdlibhUsedQuery()) and + not isExcluded(fc, BannedPackage::bsearchAndQsortOfStdlibhUsedQuery()) and f = fc.getTarget() and f.getName() = ["qsort", "bsearch"] and f.getFile().getBaseName() = "stdlib.h" -select fc, "Call to banned function $@.", f, f.getName() +select fc, "Call to banned function " + f.getName() + "." diff --git a/c/misra/src/rules/RULE-22-1/CloseFileHandleWhenNoLongerNeededMisra.ql b/c/misra/src/rules/RULE-22-1/CloseFileHandleWhenNoLongerNeededMisra.ql new file mode 100644 index 0000000000..d888d87b6c --- /dev/null +++ b/c/misra/src/rules/RULE-22-1/CloseFileHandleWhenNoLongerNeededMisra.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/close-file-handle-when-no-longer-needed-misra + * @name RULE-22-1: File handles acquired with Standard Library functions shall be explicitly closed + * @description File handles acquired with standard library functions should be released to avoid + * resource exhaustion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-1 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.closefilehandlewhennolongerneededshared.CloseFileHandleWhenNoLongerNeededShared + +class CloseFileHandleWhenNoLongerNeededMisraQuery extends CloseFileHandleWhenNoLongerNeededSharedSharedQuery +{ + CloseFileHandleWhenNoLongerNeededMisraQuery() { + this = Memory2Package::closeFileHandleWhenNoLongerNeededMisraQuery() + } +} diff --git a/c/misra/src/rules/RULE-22-1/FreeMemoryWhenNoLongerNeededMisra.ql b/c/misra/src/rules/RULE-22-1/FreeMemoryWhenNoLongerNeededMisra.ql new file mode 100644 index 0000000000..ca5853dac9 --- /dev/null +++ b/c/misra/src/rules/RULE-22-1/FreeMemoryWhenNoLongerNeededMisra.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/free-memory-when-no-longer-needed-misra + * @name RULE-22-1: Memory allocated dynamically with Standard Library functions shall be explicitly released + * @description Memory allocated dynamically with standard library functions should be freed to + * avoid memory leaks. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-1 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.freememorywhennolongerneededshared.FreeMemoryWhenNoLongerNeededShared + +class FreeMemoryWhenNoLongerNeededMisraQuery extends FreeMemoryWhenNoLongerNeededSharedSharedQuery { + FreeMemoryWhenNoLongerNeededMisraQuery() { + this = Memory2Package::freeMemoryWhenNoLongerNeededMisraQuery() + } +} diff --git a/c/misra/src/rules/RULE-22-10/OnlyTestErrnoRightAfterErrnoSettingFunction.ql b/c/misra/src/rules/RULE-22-10/OnlyTestErrnoRightAfterErrnoSettingFunction.ql index a8b1e312d2..50e5350936 100644 --- a/c/misra/src/rules/RULE-22-10/OnlyTestErrnoRightAfterErrnoSettingFunction.ql +++ b/c/misra/src/rules/RULE-22-10/OnlyTestErrnoRightAfterErrnoSettingFunction.ql @@ -9,6 +9,7 @@ * @problem.severity warning * @tags external/misra/id/rule-22-10 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -16,28 +17,25 @@ import cpp import codingstandards.c.misra import codingstandards.c.Errno -/* +/** * A call to an `ErrnoSettingFunction` */ - class ErrnoSettingFunctionCall extends FunctionCall { ErrnoSettingFunctionCall() { this.getTarget() instanceof ErrnoSettingFunction } } -/* +/** * A function call that is not part of the `errno` macro expansion */ - class MaySetErrnoCall extends FunctionCall { MaySetErrnoCall() { not inmacroexpansion(this, any(MacroInvocation ma | ma.getMacroName() = "errno")) } } -/* +/** * CFG nodes preceding a `errno` test where `errno` is not set */ - ControlFlowNode notSetPriorToErrnoTest(EqualityOperation eg) { result = eg or diff --git a/c/misra/src/rules/RULE-22-11/ThreadPreviouslyJoinedOrDetached.ql b/c/misra/src/rules/RULE-22-11/ThreadPreviouslyJoinedOrDetached.ql new file mode 100644 index 0000000000..1a6476b1a7 --- /dev/null +++ b/c/misra/src/rules/RULE-22-11/ThreadPreviouslyJoinedOrDetached.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/thread-previously-joined-or-detached + * @name RULE-22-11: A thread that was previously either joined or detached shall not be subsequently joined nor detached + * @description Joining or detaching a previously joined or detached thread can lead to undefined + * program behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-11 + * external/misra/c/2012/amendment4 + * correctness + * concurrency + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.joinordetachthreadonlyonce.JoinOrDetachThreadOnlyOnce + +class ThreadPreviouslyJoinedOrDetachedQuery extends JoinOrDetachThreadOnlyOnceSharedQuery { + ThreadPreviouslyJoinedOrDetachedQuery() { + this = Concurrency6Package::threadPreviouslyJoinedOrDetachedQuery() + } +} diff --git a/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql b/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql new file mode 100644 index 0000000000..d92b4ccea6 --- /dev/null +++ b/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql @@ -0,0 +1,54 @@ +/** + * @id c/misra/nonstandard-use-of-threading-object + * @name RULE-22-12: Standard library threading objects (mutexes, threads, etc.) shall only be accessed by the appropriate Standard Library functions + * @description Thread objects, thread synchronization objects, and thread-specific storage pointers + * shall only be accessed by the appropriate Standard Library functions. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-12 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type + +predicate isThreadingObject(Type t) { t instanceof PossiblySpecified::Type } + +predicate validUseOfStdThreadObject(Expr e) { + e.getParent() instanceof AddressOfExpr + or + exists(Call c | + c.getTarget().hasName(["tss_get", "tss_set", "tss_delete"]) and + e = c.getArgument(0) + ) +} + +predicate isStdThreadObjectPtr(Type t) { isThreadingObject(t.(PointerType).getBaseType()) } + +predicate invalidStdThreadObjectUse(Expr e) { + // Invalid use of mtx_t, etc. + isThreadingObject(e.getType()) and + not validUseOfStdThreadObject(e) + or + // Invalid cast from mtx_t* to void*, etc. + isStdThreadObjectPtr(e.getType()) and + exists(Cast cast | + cast.getExpr() = e and + not isStdThreadObjectPtr(cast.getType()) + ) +} + +from Expr e +where + not isExcluded(e, Concurrency8Package::nonstandardUseOfThreadingObjectQuery()) and + invalidStdThreadObjectUse(e) and + // Deduplicate results: (mtx = mtx) is an expression of mtx type, but don't flag the equality + // check, only flag the two `mtx` references. + not invalidStdThreadObjectUse(e.getAChild+()) +select e, "Invalid usage of standard thread object type '" + e.getType().toString() + "'." diff --git a/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql b/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql new file mode 100644 index 0000000000..066cf3c295 --- /dev/null +++ b/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql @@ -0,0 +1,31 @@ +/** + * @id c/misra/threading-object-with-invalid-storage-duration + * @name RULE-22-13: Threading objects (mutexes, threads, etc). shall have not have automatic or thread storage duration + * @description Thread objects, thread synchronization objects, and thread specific storage pointers + * shall have appropriate storage duration. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-13 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type + +from ObjectIdentity obj, StorageDuration storageDuration, Type type +where + not isExcluded(obj, Concurrency8Package::threadingObjectWithInvalidStorageDurationQuery()) and + storageDuration = obj.getStorageDuration() and + not storageDuration.isStatic() and + type = obj.getASubObjectType() and + type instanceof PossiblySpecified::Type +select obj, + "Object of type '" + obj.getType().getName() + "' has invalid storage duration type '" + + storageDuration.getStorageTypeName() + "'." diff --git a/c/misra/src/rules/RULE-22-14/MutexInitWithInvalidMutexType.ql b/c/misra/src/rules/RULE-22-14/MutexInitWithInvalidMutexType.ql new file mode 100644 index 0000000000..a122a0bec4 --- /dev/null +++ b/c/misra/src/rules/RULE-22-14/MutexInitWithInvalidMutexType.ql @@ -0,0 +1,36 @@ +/** + * @id c/misra/mutex-init-with-invalid-mutex-type + * @name RULE-22-14: Mutexes shall be initialized with a valid mutex type + * @description Mutexes shall be initialized with a valid mutex type. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-14 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency + +predicate isBaseMutexType(EnumConstantAccess access) { + access.getTarget().hasName(["mtx_plain", "mtx_timed"]) +} + +predicate isValidMutexType(Expr expr) { + isBaseMutexType(expr) + or + exists(BinaryBitwiseOperation binOr | binOr = expr | + isBaseMutexType(binOr.getLeftOperand()) and + binOr.getRightOperand().(EnumConstantAccess).getTarget().hasName("mtx_recursive") + ) +} + +from C11MutexSource init +where + not isExcluded(init, Concurrency8Package::mutexInitWithInvalidMutexTypeQuery()) and + not isValidMutexType(init.getMutexTypeExpr()) +select init, "Mutex initialized with incorrect type expression." diff --git a/c/misra/src/rules/RULE-22-14/MutexInitializedInsideThread.ql b/c/misra/src/rules/RULE-22-14/MutexInitializedInsideThread.ql new file mode 100644 index 0000000000..497fdaf14d --- /dev/null +++ b/c/misra/src/rules/RULE-22-14/MutexInitializedInsideThread.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/mutex-initialized-inside-thread + * @name RULE-22-14: Thread synchronization objects shall be initialized deterministically + * @description Mutex and condition objects initialized inside of threads may result in + * indeterministic state. + * @kind problem + * @precision high + * @problem.severity recommendation + * @tags external/misra/id/rule-22-14 + * readability + * maintainability + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency + +from C11MutexSource mutexCreate, ThreadedFunction thread +where + not isExcluded(mutexCreate, Concurrency8Package::mutexInitializedInsideThreadQuery()) and + thread.calls*(mutexCreate.getEnclosingFunction()) +select mutexCreate, "Mutex initialization reachable from threaded function '$@'.", thread, + thread.getName() diff --git a/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql b/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql new file mode 100644 index 0000000000..f78c25f981 --- /dev/null +++ b/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql @@ -0,0 +1,78 @@ +/** + * @id c/misra/mutex-not-initialized-before-use + * @name RULE-22-14: Thread synchronization objects shall be initialized before being accessed + * @description Mutex and condition objects shall be initialized with the standard library functions + * before using them. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-14 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type +import codingstandards.c.initialization.GlobalInitializationAnalysis + +module MutexInitializationConfig implements GlobalInitializationAnalysisConfigSig { + ObjectIdentity getAnInitializedObject(Expr e) { + e.(C11MutexSource).getMutexExpr() = result.getASubobjectAddressExpr() + } + + ObjectIdentity getAUsedObject(Expr e) { + result.getASubobjectAddressExpr() = e and + ( + exists(CMutexFunctionCall mutexUse | e = mutexUse.getLockExpr()) + or + exists(CConditionOperation condOp | e = condOp.getMutexExpr()) + ) + } +} + +module ConditionInitializationConfig implements GlobalInitializationAnalysisConfigSig { + ObjectIdentity getAnInitializedObject(Expr e) { + exists(CConditionOperation condOp | + e = condOp and + condOp.isInit() and + condOp.getConditionExpr() = result.getASubobjectAddressExpr() + ) + } + + ObjectIdentity getAUsedObject(Expr e) { + result.getASubobjectAddressExpr() = e and + exists(CConditionOperation condOp | + condOp.isUse() and + e = condOp.getConditionExpr() + ) + } +} + +import GlobalInitalizationAnalysis as MutexInitAnalysis +import GlobalInitalizationAnalysis as CondInitAnalysis + +from Expr objUse, ObjectIdentity obj, Function callRoot, string typeString, string description +where + not isExcluded(objUse, Concurrency8Package::mutexNotInitializedBeforeUseQuery()) and + ( + MutexInitAnalysis::uninitializedFrom(objUse, obj, callRoot) and + typeString = "Mutex" + or + CondInitAnalysis::uninitializedFrom(objUse, obj, callRoot) and + typeString = "Condition" + ) and + ( + if + obj.getType() instanceof PossiblySpecified::Type or + obj.getType() instanceof PossiblySpecified::Type + then description = typeString + else description = typeString + " in object" + ) +select objUse, + description + " '$@' possibly used before initialization, from entry point function '$@'.", obj, + obj.toString(), callRoot, callRoot.getName() diff --git a/c/misra/src/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.ql b/c/misra/src/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.ql new file mode 100644 index 0000000000..ec4631ef1b --- /dev/null +++ b/c/misra/src/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.ql @@ -0,0 +1,113 @@ +/** + * @id c/misra/thread-resource-disposed-before-threads-joined + * @name RULE-22-15: Thread synchronization objects and thread-specific storage pointers shall not be disposed unsafely + * @description Thread synchronization objects and thread-specific storage pointers shall not be + * destroyed until after all threads accessing them have terminated. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/misra/id/rule-22-15 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.SubObjects +import codingstandards.cpp.Concurrency + +newtype TThreadKind = + TSpawned(C11ThreadCreateCall tcc) or + TMainThread() + +TThreadKind getThreadKind(FunctionCall operation) { + if + not exists(C11ThreadCreateCall tcc | + getAThreadContextAwareSuccessor(tcc.getFunction().getEntryPoint()) = operation + ) + then result = TMainThread() + else + exists(C11ThreadCreateCall tcc | + getAThreadContextAwareSuccessor(tcc.getFunction().getEntryPoint()) = operation and + result = TSpawned(tcc) + ) +} + +bindingset[tcc, thread] +predicate followsMainThreadTcc(C11ThreadCreateCall tcc, TThreadKind thread) { + thread = TMainThread() + or + exists(C11ThreadCreateCall tcc2 | + getAThreadContextAwareSuccessor(tcc) = tcc2 and + thread = TSpawned(tcc2) + ) +} + +string describeThread(TThreadKind thread) { + thread = TMainThread() and + result = "main thread" + or + exists(C11ThreadCreateCall tcc2 | + thread = TSpawned(tcc2) and + result = tcc2.getFunction().getName() + ) +} + +bindingset[alternative] +Element elementOr(TThreadKind thread, Element alternative) { + thread = TMainThread() and + result = alternative + or + exists(C11ThreadCreateCall tcc2 | + thread = TSpawned(tcc2) and + result = tcc2 + ) +} + +from + FunctionCall dispose, FunctionCall use, C11ThreadCreateCall tcc, TThreadKind disposeThread, + TThreadKind useThread, SubObject usedAndDestroyed +where + not isExcluded(dispose, Concurrency9Package::threadResourceDisposedBeforeThreadsJoinedQuery()) and + // `tcc` may be the thread that uses the resource, or the thread that disposes it. What matters + // for the query is that `tcc` is before the use and the dispose. + dispose = getAThreadContextAwareSuccessor(tcc) and + ( + // Lock and dispose of mtx_t: + exists(CMutexFunctionCall mfc, C11MutexDestroyer md | dispose = md and use = mfc | + mfc = getAThreadContextAwareSuccessor(tcc) and + mfc.getLockExpr() = usedAndDestroyed.getAnAddressOfExpr() and + md.getMutexExpr() = usedAndDestroyed.getAnAddressOfExpr() + ) + or + // Read/store and dispose of tss_t: + exists(ThreadSpecificStorageFunctionCall tssfc, TSSDeleteFunctionCall td | + dispose = td and use = tssfc + | + tssfc = getAThreadContextAwareSuccessor(tcc) and + tssfc.getKey() = usedAndDestroyed.getAnAddressOfExpr() and + td.getKey() = usedAndDestroyed.getAnAddressOfExpr() + ) + or + // Wait and dispose of cnd_t: + exists(CConditionOperation cndop, C11ConditionDestroyer cd | dispose = cd and use = cndop | + cndop = getAThreadContextAwareSuccessor(tcc) and + cndop.getConditionExpr() = usedAndDestroyed.getAnAddressOfExpr() and + cd.getConditionExpr() = usedAndDestroyed.getAnAddressOfExpr() + ) + ) and + // Dispose could be in the main thread or in a spawned thread. + disposeThread = getThreadKind(dispose) and + // Dispose could be in the main thread or in a spawned thread. + useThread = getThreadKind(use) and + // Exclude a thread that does not concurrently share the resource it disposed (unlikely). + not useThread = disposeThread and + followsMainThreadTcc(tcc, useThread) and + followsMainThreadTcc(tcc, disposeThread) and + // If there is a join between the use and the dispose, the code is compliant. + not getAThreadContextAwarePredecessor(elementOr(useThread, use), dispose) instanceof C11ThreadWait +select dispose, "Thread resource $@ disposed before joining thread $@ which uses it.", + usedAndDestroyed.getRootIdentity(), usedAndDestroyed.toString(), elementOr(useThread, use), + describeThread(useThread) diff --git a/c/misra/src/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.ql b/c/misra/src/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.ql new file mode 100644 index 0000000000..c86c9b9d9d --- /dev/null +++ b/c/misra/src/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.ql @@ -0,0 +1,52 @@ +/** + * @id c/misra/mutex-objects-not-always-unlocked + * @name RULE-22-16: All mutex objects locked by a thread shall be explicitly unlocked by the same thread + * @description Mutex not unlocked by thread on all execution paths in current thread after being + * locked. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-16 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency +import codingstandards.cpp.resources.ResourceLeakAnalysis + +module MutexLeakConfig implements ResourceLeakConfigSig { + predicate isAllocate(ControlFlowNode allocPoint, DataFlow::Node node) { + exists(MutexFunctionCall lock | + allocPoint = lock and + lock.isLock() and + node.asDefiningArgument() = lock.getLockExpr() + ) + } + + predicate isFree(ControlFlowNode node, DataFlow::Node resource) { + exists(MutexFunctionCall mfc | + node = mfc and + mfc.isUnlock() and + mfc.getLockExpr() = resource.asExpr() + ) + } +} + +string describeMutex(Expr mutexExpr) { + if mutexExpr instanceof AddressOfExpr + then result = mutexExpr.(AddressOfExpr).getOperand().toString() + else result = mutexExpr.toString() +} + +from MutexFunctionCall lockCall, string mutexDescription +where + not isExcluded(lockCall, Concurrency8Package::mutexObjectsNotAlwaysUnlockedQuery()) and + //lockCall.getLockExpr() = mutexNode.asDefiningArgument() and + exists(ResourceLeak::getALeak(lockCall)) and + mutexDescription = describeMutex(lockCall.getLockExpr()) +select lockCall, + "Mutex '" + mutexDescription + "' is locked here and may not always be subsequently unlocked." diff --git a/c/misra/src/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.ql b/c/misra/src/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.ql new file mode 100644 index 0000000000..d85183a831 --- /dev/null +++ b/c/misra/src/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.ql @@ -0,0 +1,67 @@ +/** + * @id c/misra/invalid-operation-on-unlocked-mutex + * @name RULE-22-17: No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked + * @description No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it + * has not locked before. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-17 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.SubObjects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.dominance.BehavioralSet + +/* A call to mtx_unlock() or cnd_wait() or cnd_timedwait(), which require a locked mutex */ +class RequiresLockOperation extends FunctionCall { + SubObject mutex; + + RequiresLockOperation() { + exists(CMutexFunctionCall mutexCall | this = mutexCall | + mutexCall.isUnlock() and + mutex.getAnAddressOfExpr() = mutexCall.getLockExpr() + ) + or + exists(CConditionOperation condOp | this = condOp | + mutex.getAnAddressOfExpr() = condOp.getMutexExpr() + ) + } + + SubObject getMutex() { result = mutex } +} + +/* A config to search for a dominating set that locks the mutex before the operation */ +module LockDominatingSetConfig implements DominatingSetConfigSig { + predicate isTargetBehavior(ControlFlowNode node, RequiresLockOperation op) { + exists(CMutexFunctionCall mutexCall | node = mutexCall | + mutexCall.isLock() and + mutexCall.getLockExpr() = op.getMutex().getAnAddressOfExpr() + ) + } + + predicate isBlockingBehavior(ControlFlowNode node, RequiresLockOperation op) { + // If we find a branch that explicitly unlocks the mutex, we should not look for an earlier + // call to lock that mutex. + exists(CMutexFunctionCall mutexCall | node = mutexCall | + mutexCall.isUnlock() and + mutexCall.getLockExpr() = op.getMutex().getAnAddressOfExpr() + ) + } +} + +import DominatingBehavioralSet as DominatingSet + +from RequiresLockOperation operation, SubObject mutex +where + not isExcluded(operation, Concurrency9Package::invalidOperationOnUnlockedMutexQuery()) and + mutex = operation.getMutex() and + not DominatingSet::isDominatedByBehavior(operation) +select operation, "Invalid operation on mutex '$@' not locked by the current thread.", + mutex.getRootIdentity(), mutex.toString() diff --git a/c/misra/src/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.ql b/c/misra/src/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.ql new file mode 100644 index 0000000000..17762b3eee --- /dev/null +++ b/c/misra/src/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.ql @@ -0,0 +1,37 @@ +/** + * @id c/misra/non-recursive-mutex-recursively-locked + * @name RULE-22-18: Non-recursive mutexes shall not be recursively locked + * @description Mutexes initialized with mtx_init() without mtx_recursive shall not be locked by a + * thread that has previously locked it. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-18 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.SubObjects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type + +from + LockProtectedControlFlowNode n, CMutexFunctionCall lockCall, SubObject mutex, + CMutexFunctionCall coveredByLock +where + not isExcluded(n, Concurrency9Package::nonRecursiveMutexRecursivelyLockedQuery()) and + lockCall = n and + coveredByLock = n.coveredByLock() and + not coveredByLock = lockCall and + mutex.isPrecise() and + coveredByLock.getLockExpr() = mutex.getAnAddressOfExpr() and + lockCall.getLockExpr() = mutex.getAnAddressOfExpr() and + forex(C11MutexSource init | init.getMutexExpr() = mutex.getAnAddressOfExpr() | + not init.isRecursive() + ) +select n, "Non-recursive mutex " + mutex.toString() + " locked after it is $@.", coveredByLock, + "already locked" diff --git a/c/misra/src/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.ql b/c/misra/src/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.ql new file mode 100644 index 0000000000..7e002585b6 --- /dev/null +++ b/c/misra/src/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.ql @@ -0,0 +1,60 @@ +/** + * @id c/misra/non-recursive-mutex-recursively-locked-audit + * @name RULE-22-18: (Audit) Non-recursive mutexes shall not be recursively locked + * @description Mutexes that may be initialized without mtx_recursive shall not be locked by a + * thread that may have previously locked it. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-18 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/audit + * external/misra/obligation/required + */ + +import cpp +import codeql.util.Boolean +import codingstandards.c.misra +import codingstandards.c.SubObjects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type + +predicate isTrackableMutex(CMutexFunctionCall lockCall, Boolean recursive) { + exists(SubObject mutex | + lockCall.getLockExpr() = mutex.getAnAddressOfExpr() and + mutex.isPrecise() and + forex(C11MutexSource init | init.getMutexExpr() = mutex.getAnAddressOfExpr() | + if init.isRecursive() then recursive = true else recursive = false + ) + ) +} + +predicate definitelyDifferentMutexes(CMutexFunctionCall lockCall, CMutexFunctionCall coveredByLock) { + exists(SubObject a, SubObject b | + lockCall.getLockExpr() = a.getAnAddressOfExpr() and + coveredByLock.getLockExpr() = b.getAnAddressOfExpr() and + not a = b + ) +} + +from LockProtectedControlFlowNode n, CMutexFunctionCall lockCall, CMutexFunctionCall coveredByLock +where + not isExcluded(n, Concurrency9Package::nonRecursiveMutexRecursivelyLockedAuditQuery()) and + lockCall = n and + coveredByLock = n.coveredByLock() and + not coveredByLock = lockCall and + // If mutexes are provably different objects, they do not need to be audited + not definitelyDifferentMutexes(lockCall, coveredByLock) and + ( + // If either mutex is not trackable, it should be audited + not isTrackableMutex(lockCall, _) or + not isTrackableMutex(coveredByLock, _) + ) and + not ( + // If either mutex is definitely recursive, it does not need to be audited + isTrackableMutex(lockCall, true) or + isTrackableMutex(coveredByLock, true) + ) +select n, "Mutex locked after it was already $@.", coveredByLock, "previously locked" diff --git a/c/misra/src/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.ql b/c/misra/src/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.ql new file mode 100644 index 0000000000..0d5aa5399f --- /dev/null +++ b/c/misra/src/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.ql @@ -0,0 +1,69 @@ +/** + * @id c/misra/condition-variable-used-with-multiple-mutexes + * @name RULE-22-19: A condition variable shall be associated with at most one mutex object + * @description Standard library functions cnd_wait() and cnd_timedwait() shall specify the same + * mutex object for each condition object in all calls. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-19 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.SubObjects +import codingstandards.cpp.Concurrency + +bindingset[cond, mutex] +int countMutexesForConditionVariable(SubObject cond, SubObject mutex) { + result = + count(CConditionOperation call | + call.getConditionExpr() = cond.getAnAddressOfExpr() and + call.getMutexExpr() = mutex.getAnAddressOfExpr() + ) +} + +bindingset[cond, mutex] +predicate conditionVariableUsesMutex(SubObject cond, SubObject mutex) { + countMutexesForConditionVariable(cond, mutex) > 0 +} + +bindingset[cond, n] +SubObject nthMutexForConditionVariable(SubObject cond, int n) { + result = + rank[n](SubObject mutex | + conditionVariableUsesMutex(cond, mutex) + | + mutex order by countMutexesForConditionVariable(cond, mutex), mutex.toString() + ) +} + +bindingset[cond, mutex] +CConditionOperation firstCallForConditionMutex(SubObject cond, SubObject mutex) { + result = + rank[1](CConditionOperation call | + call.getConditionExpr() = cond.getAnAddressOfExpr() and + call.getMutexExpr() = mutex.getAnAddressOfExpr() + | + call order by call.getFile().getAbsolutePath(), call.getLocation().getStartLine() + ) +} + +from + SubObject cond, CConditionOperation useOne, SubObject mutexOne, CConditionOperation useTwo, + SubObject mutexTwo +where + not isExcluded(cond.getRootIdentity(), + Concurrency9Package::conditionVariableUsedWithMultipleMutexesQuery()) and + mutexOne = nthMutexForConditionVariable(cond, 1) and + mutexTwo = nthMutexForConditionVariable(cond, 2) and + useOne = firstCallForConditionMutex(cond, mutexOne) and + useTwo = firstCallForConditionMutex(cond, mutexOne) +select useOne, + "Condition variable $@ associated with multiple mutexes, operation uses mutex $@ while $@ uses other mutex $@.", + cond.getRootIdentity(), cond.toString(), mutexOne.getRootIdentity(), mutexOne.toString(), useTwo, + "another operation", mutexTwo.getRootIdentity(), mutexTwo.toString() diff --git a/c/misra/src/rules/RULE-22-2/OnlyFreeMemoryAllocatedDynamicallyMisra.ql b/c/misra/src/rules/RULE-22-2/OnlyFreeMemoryAllocatedDynamicallyMisra.ql new file mode 100644 index 0000000000..cdbe8e2c16 --- /dev/null +++ b/c/misra/src/rules/RULE-22-2/OnlyFreeMemoryAllocatedDynamicallyMisra.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/only-free-memory-allocated-dynamically-misra + * @name RULE-22-2: A block of memory shall only be freed if it was allocated by means of a Standard Library function + * @description Freeing memory that is not allocated dynamically can lead to heap corruption and + * undefined behavior. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-2 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.onlyfreememoryallocateddynamicallyshared.OnlyFreeMemoryAllocatedDynamicallyShared + +class OnlyFreeMemoryAllocatedDynamicallyMisraQuery extends OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery +{ + OnlyFreeMemoryAllocatedDynamicallyMisraQuery() { + this = Memory2Package::onlyFreeMemoryAllocatedDynamicallyMisraQuery() + } +} diff --git a/c/misra/src/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.ql b/c/misra/src/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.ql new file mode 100644 index 0000000000..1edf4aa9c3 --- /dev/null +++ b/c/misra/src/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.ql @@ -0,0 +1,44 @@ +/** + * @id c/misra/thread-storage-not-initialized-before-use + * @name RULE-22-20: Thread-specific storage pointers shall be created before being accessed + * @description Thread specific storage pointers shall be initialized with the standard library + * functions before using them. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-22-20 + * correctness + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Objects +import codingstandards.cpp.Concurrency +import codingstandards.cpp.Type +import codingstandards.c.initialization.GlobalInitializationAnalysis + +module ThreadStoreInitializationConfig implements GlobalInitializationAnalysisConfigSig { + ObjectIdentity getAnInitializedObject(Expr e) { + e.(TSSCreateFunctionCall).getKey() = result.getASubobjectAddressExpr() + } + + ObjectIdentity getAUsedObject(Expr e) { + result.getASubobjectAddressExpr() = e and + exists(ThreadSpecificStorageFunctionCall use | + not use instanceof TSSCreateFunctionCall and e = use.getKey() + ) + } +} + +import GlobalInitalizationAnalysis as InitAnalysis + +from Expr objUse, ObjectIdentity obj, Function callRoot +where + not isExcluded(objUse, Concurrency9Package::threadStorageNotInitializedBeforeUseQuery()) and + InitAnalysis::uninitializedFrom(objUse, obj, callRoot) +select objUse, + "Thread specific storage pointer '$@' used before initialization from entry point function '$@'.", + obj, obj.toString(), callRoot, callRoot.getName() diff --git a/c/misra/src/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.ql b/c/misra/src/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.ql new file mode 100644 index 0000000000..3c40ea7116 --- /dev/null +++ b/c/misra/src/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.ql @@ -0,0 +1,27 @@ +/** + * @id c/misra/thread-storage-pointer-initialized-inside-thread + * @name RULE-22-20: Thread specific storage pointers shall be initialized deterministically + * @description Thread specific storage pointers initialized inside of threads may result in + * indeterministic state. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-22-20 + * readability + * maintainability + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Concurrency + +from TSSCreateFunctionCall tssCreate, ThreadedFunction thread +where + not isExcluded(tssCreate, Concurrency8Package::mutexInitializedInsideThreadQuery()) and + thread.calls*(tssCreate.getEnclosingFunction()) +select tssCreate, + "Thread specific storage object initialization reachable from threaded function '$@'.", thread, + thread.getName() diff --git a/c/misra/src/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.ql b/c/misra/src/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.ql index 877fbea9aa..642813bbab 100644 --- a/c/misra/src/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.ql +++ b/c/misra/src/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-22-3 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.ql b/c/misra/src/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.ql index c4acbf7aca..2439d4ca47 100644 --- a/c/misra/src/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.ql +++ b/c/misra/src/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.ql @@ -7,6 +7,7 @@ * @problem.severity error * @tags external/misra/id/rule-22-4 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/mandatory */ @@ -15,22 +16,22 @@ import codingstandards.c.misra import codingstandards.cpp.standardlibrary.FileAccess import semmle.code.cpp.dataflow.DataFlow -class FileDFConf extends DataFlow::Configuration { - FileDFConf() { this = "FileDFConf" } - - override predicate isSource(DataFlow::Node source) { +module FileDFConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { // source is the return value of a call to fopen source.asExpr().(FOpenCall).isReadOnlyMode() } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { // sink must be the second parameter of a FsetposCall call sink.asExpr() = any(FileWriteFunctionCall write).getFileExpr() } } -from FileDFConf dfConf, DataFlow::Node source, FileWriteFunctionCall sink +module FileDFFlow = DataFlow::Global; + +from DataFlow::Node source, FileWriteFunctionCall sink where not isExcluded(sink, IO3Package::attemptToWriteToAReadOnlyStreamQuery()) and - dfConf.hasFlow(source, DataFlow::exprNode(sink.getFileExpr())) + FileDFFlow::flow(source, DataFlow::exprNode(sink.getFileExpr())) select sink, "Attempt to write to a $@ opened as read-only.", source, "stream" diff --git a/c/misra/src/rules/RULE-22-5/PointerToAFileObjectDereferenced.ql b/c/misra/src/rules/RULE-22-5/PointerToAFileObjectDereferenced.ql index 86e0b76e21..05cc4e3433 100644 --- a/c/misra/src/rules/RULE-22-5/PointerToAFileObjectDereferenced.ql +++ b/c/misra/src/rules/RULE-22-5/PointerToAFileObjectDereferenced.ql @@ -7,6 +7,7 @@ * @problem.severity error * @tags external/misra/id/rule-22-5 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/mandatory */ diff --git a/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql b/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql index 78c5063ddd..64318dbedd 100644 --- a/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql +++ b/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql @@ -7,6 +7,7 @@ * @problem.severity error * @tags external/misra/id/rule-22-6 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/mandatory */ diff --git a/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql b/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql index 22499946a0..1da495ca28 100644 --- a/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql +++ b/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql @@ -8,25 +8,25 @@ * @problem.severity error * @tags external/misra/id/rule-22-7 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra import codingstandards.cpp.ReadErrorsAndEOF +import semmle.code.cpp.dataflow.DataFlow /** * The getchar() return value propagates directly to a check against EOF macro * type conversions are not allowed */ -class DFConf extends DataFlow::Configuration { - DFConf() { this = "DFConf" } - - override predicate isSource(DataFlow::Node source) { +module DFConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof InBandErrorReadFunctionCall } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(EOFWEOFInvocation mi, EqualityOperation eq | // one operand is the sink sink.asExpr() = eq.getAnOperand() and @@ -35,11 +35,13 @@ class DFConf extends DataFlow::Configuration { ) } - override predicate isBarrier(DataFlow::Node barrier) { + predicate isBarrier(DataFlow::Node barrier) { barrier.asExpr() = any(IntegralConversion c).getExpr() } } +module DFFlow = DataFlow::Global; + // The equality operation `eq` checks a char fetched from `read` against a macro predicate isWeakMacroCheck(EqualityOperation eq, InBandErrorReadFunctionCall read) { exists(Expr c, EOFWEOFInvocation mi | @@ -51,10 +53,10 @@ predicate isWeakMacroCheck(EqualityOperation eq, InBandErrorReadFunctionCall rea ) } -from EqualityOperation eq, InBandErrorReadFunctionCall read, DFConf dfConf +from EqualityOperation eq, InBandErrorReadFunctionCall read where not isExcluded(eq, IO3Package::eofShallBeComparedWithUnmodifiedReturnValuesQuery()) and isWeakMacroCheck(eq, read) and - not dfConf.hasFlow(DataFlow::exprNode(read), DataFlow::exprNode(eq.getAnOperand())) + not DFFlow::flow(DataFlow::exprNode(read), DataFlow::exprNode(eq.getAnOperand())) select eq, "The check is not reliable as the type of the return value of $@ is converted.", read, read.toString() diff --git a/c/misra/src/rules/RULE-22-8/ErrnoSetToZeroPriorToCall.ql b/c/misra/src/rules/RULE-22-8/ErrnoSetToZeroPriorToCall.ql index 1dee846bb1..6a39070ef0 100644 --- a/c/misra/src/rules/RULE-22-8/ErrnoSetToZeroPriorToCall.ql +++ b/c/misra/src/rules/RULE-22-8/ErrnoSetToZeroPriorToCall.ql @@ -9,6 +9,7 @@ * @problem.severity error * @tags external/misra/id/rule-22-8 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -16,18 +17,16 @@ import cpp import codingstandards.c.misra import codingstandards.c.Errno -/* +/** * A call to an `ErrnoSettingFunction` */ - class ErrnoSettingFunctionCall extends FunctionCall { ErrnoSettingFunctionCall() { this.getTarget() instanceof ErrnoSettingFunction } } -/* +/** * CFG nodes preceding a `ErrnoSettingFunctionCall` */ - ControlFlowNode notZeroedPriorToErrnoSet(ErrnoSettingFunctionCall fc) { result = fc or diff --git a/c/misra/src/rules/RULE-22-9/ErrnoSetToZeroAfterCall.ql b/c/misra/src/rules/RULE-22-9/ErrnoSetToZeroAfterCall.ql index 0a49529d1d..274bf5b2ae 100644 --- a/c/misra/src/rules/RULE-22-9/ErrnoSetToZeroAfterCall.ql +++ b/c/misra/src/rules/RULE-22-9/ErrnoSetToZeroAfterCall.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-22-9 * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -15,18 +16,9 @@ import cpp import codingstandards.c.misra import codingstandards.c.Errno -/* - * A call to an `ErrnoSettingFunction` - */ - -class InBandErrnoSettingFunctionCall extends FunctionCall { - InBandErrnoSettingFunctionCall() { this.getTarget() instanceof InBandErrnoSettingFunction } -} - -/* +/** * CFG nodes following a `ErrnoSettingFunctionCall` */ - ControlFlowNode notTestedAfterErrnoSet(InBandErrnoSettingFunctionCall fc) { result = fc or diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql new file mode 100644 index 0000000000..1a76339f50 --- /dev/null +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql @@ -0,0 +1,27 @@ +/** + * @id c/misra/generic-selection-doesnt-depend-on-macro-argument + * @name RULE-23-1: A generic selection should depend on the type of a macro argument + * @description A generic selection should depend on the type of a macro argument. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-23-1 + * correctness + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Generic + +from ParsedGenericMacro macro, string ctrlExpr +where + not isExcluded(macro, GenericsPackage::genericSelectionDoesntDependOnMacroArgumentQuery()) and + ctrlExpr = macro.getControllingExprString() and + // No parameter exists that is expanded in the controlling expression one or more times + not exists(string parameter | macro.expansionsInsideControllingExpr(parameter) > 0) +select macro, + "Generic macro " + macro.getName() + " doesn't refer to a macro parameter in controlling expr '" + + ctrlExpr + "'." diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql new file mode 100644 index 0000000000..603c44e8e1 --- /dev/null +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/generic-selection-not-expanded-from-a-macro + * @name RULE-23-1: A generic selection should only be expanded from a macro + * @description A generic selection should only be expanded from a macro. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-1 + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from C11GenericExpr generic, Expr ctrlExpr +where + not isExcluded(generic, GenericsPackage::genericSelectionNotExpandedFromAMacroQuery()) and + ctrlExpr = generic.getControllingExpr() and + not exists(MacroInvocation mi | mi.getAGeneratedElement() = generic.getExpr()) +select generic, "$@ in generic expression does not expand a macro parameter.", ctrlExpr, + "Controlling expression" diff --git a/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql new file mode 100644 index 0000000000..d7fcb13d76 --- /dev/null +++ b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql @@ -0,0 +1,88 @@ +/** + * @id c/misra/generic-selection-not-from-macro-with-side-effects + * @name RULE-23-2: A generic selection shall not contain side-effects if it is not expanded from a macro + * @description A generic selection that is not expanded from a macro shall not contain potential + * side effects in the controlling expression. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-23-2 + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Generic +import codingstandards.cpp.SideEffect +import codingstandards.cpp.sideeffect.DefaultEffects +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +class GenericWithNonMacroSideEffect extends C11GenericExpr { + SideEffect sideEffect; + + GenericWithNonMacroSideEffect() { + not exists(MacroInvocation mi | + mi.getAGeneratedElement() = getExpr() and + mi.getMacro().(GenericMacro).hasControllingExprFromMacroParameter() + ) and + sideEffect = getASideEffect(getControllingExpr()) + } + + SideEffect getASideEffect() { result = sideEffect } +} + +module GenericSideEffectConfig implements DeduplicateMacroConfigSig { + string describe(GenericWithNonMacroSideEffect e) { + result = "side effect '" + e.getASideEffect() + "'" + } +} + +module GenericSideEffectReportConfig implements MacroReportConfigSig { + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = + "Generic selection macro " + m.getName() + " contains a " + description + + ", which is not from macro invocation arguments." + } + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Generic selection in macro " + m.getName() + + " contains an invocation-dependent side effect which is not from macro invocation arguments, for example $@." + } + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(GenericWithNonMacroSideEffect element) { + // A result in an isolated expansion indicates that the side effect is not always present when + // macro is expanded, and therefore the side-effect is not in the macro definition but rather + // originates in one of the macro arguments. + none() + } + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro( + GenericWithNonMacroSideEffect element, Locatable optLoc1, string optStr1 + ) { + // Generics which are not expanded from a macro aren't applicable to this rule. + none() + } +} + +import DeduplicateMacroResults as Deduplicate +import Deduplicate::Report as Report + +from Report::ReportResult res +where + not isExcluded(res.getPrimaryElement(), + GenericsPackage::genericSelectionNotFromMacroWithSideEffectsQuery()) +select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), + res.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql b/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql new file mode 100644 index 0000000000..dc4ab081d3 --- /dev/null +++ b/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql @@ -0,0 +1,36 @@ +/** + * @id c/misra/generic-without-non-default-association + * @name RULE-23-3: A generic selection should contain at least one non-default association + * @description A generic selection should contain at least one non-default association. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-3 + * correctness + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.AlertReporting + +class InvalidGeneric extends C11GenericExpr { + InvalidGeneric() { + not exists(Type t | + t = getAnAssociationType() and + not t instanceof VoidType + ) + } +} + +from InvalidGeneric generic, Element primaryElement +where + not isExcluded(primaryElement, GenericsPackage::genericWithoutNonDefaultAssociationQuery()) and + not exists(Type t | + t = generic.getAnAssociationType() and + not t instanceof VoidType + ) and + primaryElement = MacroUnwrapper::unwrapElement(generic) +select primaryElement, "Generic selection contains no non-default association." diff --git a/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql b/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql new file mode 100644 index 0000000000..2d707548fa --- /dev/null +++ b/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql @@ -0,0 +1,111 @@ +/** + * @id c/misra/generic-association-with-unselectable-type + * @name RULE-23-4: A generic association shall list an appropriate type + * @description Generic selections undergo lvalue conversion before type comparison, leading to + * certain types being impossible to select. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-23-4 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.LvalueConversion +import codingstandards.cpp.types.Graph +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +/** + * Check if a type contains an unmatchable anonymous struct or union. + * + * Anonymous structs and unions are only equal to themselves. So any anonymous struct, or compound + * type containing an anonymous struct, is unmatchable. + * + * However, there is an exception if the anonymous struct is behind a typedef. All uses of that + * typedef will resolve to the same anonymous struct, and so the typedef is matchable. + */ +predicate containsAnonymousType(Type t) { + t.(Struct).isAnonymous() + or + not t instanceof TypedefType and + exists(Type next | typeGraph(t, next) | containsAnonymousType(next)) +} + +predicate invalidType(Type t, string reason) { + containsAnonymousType(t) and + reason = "containing an anonymous struct or union type" + or + exists(performLvalueConversion(t, reason)) +} + +class InvalidSelection extends Expr { + Type selectionType; + int idx; + C11GenericExpr generic; + string reason; + + InvalidSelection() { + this = generic.getAssociationExpr(idx) and + selectionType = generic.getAssociationType(idx) and + invalidType(selectionType, reason) + } + + Type getSelectionType() { result = selectionType } + + string getReason() { result = reason } +} + +module InvalidSelectionConfig implements DeduplicateMacroConfigSig { + string describe(InvalidSelection e) { + result = "'" + e.getSelectionType().toString() + "', due to " + e.getReason() + } +} + +import InvalidSelectionConfig + +module InvalidSelectionReportConfig implements MacroReportConfigSig { + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = "Generic in macro " + m.getName() + " has unselectable type " + description + "." + } + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Generic in macro " + m.getName() + + " has an invocation-dependent unselectable type, for example $@." + } + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(InvalidSelection element) { + result = + "Generic resulting from invocation of macro $@ contains an unselectable type " + + describe(element) + "." + } + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro(InvalidSelection element, Locatable optLoc1, string optStr1) { + result = "Generic selection uses unselectable type " + describe(element) + "'." and + optLoc1 = element and + optStr1 = "side effect" + } +} + +import DeduplicateMacroResults as Deduplicate +import Deduplicate::Report as Report + +from Report::ReportResult res +where + not isExcluded(res.getPrimaryElement(), + GenericsPackage::genericSelectionNotFromMacroWithSideEffectsQuery()) +select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), + res.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql b/c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql new file mode 100644 index 0000000000..f2961e2638 --- /dev/null +++ b/c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql @@ -0,0 +1,74 @@ +/** + * @id c/misra/dangerous-default-selection-for-pointer-in-generic + * @name RULE-23-5: A generic selection should not depend on implicit pointer type conversion + * @description Pointer types in a generic selection do not undergo pointer conversions and should + * not counterintuitively fall through to the default association. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.AlertReporting +import codingstandards.cpp.types.Compatible +import codingstandards.cpp.types.LvalueConversion +import codingstandards.cpp.types.SimpleAssignment + +predicate typesCompatible(Type t1, Type t2) { + TypeEquivalence::equalTypes(t1, t2) +} + +predicate relevantTypes(Type a, Type b) { + exists(C11GenericExpr g | + a = g.getAnAssociationType() and + b = getLvalueConverted(g.getControllingExpr().getFullyConverted().getType()) + ) +} + +predicate missesOnPointerConversion(Type provided, Type expected) { + // The provided type is not compatible with the expected type: + not typesCompatible(provided, expected) and + // But 6.5.16.1 simple assignment constraints would have been satisfied: + ( + // Check as if the controlling expr is assigned to the expected type: + SimpleAssignment::satisfiesSimplePointerAssignment(expected, provided) + or + // Since developers typically rely on the compiler to catch const/non-const assignment + // errors, don't assume a const-to-non-const generic selection miss was intentional. + SimpleAssignment::satisfiesSimplePointerAssignment(provided, expected) + ) +} + +from + C11GenericExpr generic, Expr controllingExpr, Type providedType, Type missedType, + Type lvalueConverted, Element extraElement, string extraString, string extraElementName +where + not isExcluded(generic, GenericsPackage::dangerousDefaultSelectionForPointerInGenericQuery()) and + controllingExpr = generic.getControllingExpr() and + providedType = generic.getControllingExpr().getFullyConverted().getType() and + // The controlling expression undergoes lvalue conversion: + lvalueConverted = getLvalueConverted(providedType) and + // There is no perfect match + not typesCompatible(lvalueConverted, generic.getAnAssociationType()) and + // There is a default selector. + exists(VoidType default | default = generic.getAnAssociationType()) and + missedType = generic.getAnAssociationType() and + missesOnPointerConversion(lvalueConverted, missedType) and + extraElement = MacroUnwrapper::unwrapElement(generic) and + ( + if extraElement instanceof Macro + then ( + extraString = " in generic macro $@" and extraElementName = extraElement.(Macro).getName() + ) else ( + extraString = "" and extraElementName = "" + ) + ) +select generic, + "Generic matched default selection, as controlling argument type " + lvalueConverted.toString() + + " does not undergo pointer conversion to " + missedType.toString() + extraString + ".", + extraElement, extraElementName diff --git a/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql new file mode 100644 index 0000000000..f02f92b45a --- /dev/null +++ b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql @@ -0,0 +1,64 @@ +/** + * @id c/misra/generic-expression-with-incorrect-essential-type + * @name RULE-23-6: The controlling expression of a generic selection shall have an essential type that matches its standard type + * @description The controlling expression of a generic selection shall have an essential type that + * matches its standard type. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-23-6 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.cpp.Cpp14Literal +import codingstandards.cpp.AlertReporting + +predicate allowedByException(Expr expr, Type essentialType) { + // Constant expressions + exists(expr.getValue()) and + ( + // with essentially signed or unsigned type + getEssentialTypeCategory(essentialType) = EssentiallySignedType() + or + getEssentialTypeCategory(essentialType) = EssentiallyUnsignedType() + ) and + // with lower rank than `int` + essentialType.getSize() < any(IntType t).getSize() and + // and not a character constant + not expr instanceof Cpp14Literal::CharLiteral +} + +from + C11GenericExpr generic, Expr ctrlExpr, Type ctrlType, Type ctrlEssentialType, + Element extraElement, string extraString, string extraMessage +where + not isExcluded(ctrlExpr, GenericsPackage::genericExpressionWithIncorrectEssentialTypeQuery()) and + ctrlExpr = generic.getControllingExpr() and + ctrlType = ctrlExpr.getFullyConverted().getType() and + ctrlEssentialType = getEssentialType(ctrlExpr) and + // Exclude lvalue conversion on const structs + exists(getEssentialTypeCategory(ctrlEssentialType)) and + ( + not ctrlEssentialType = ctrlType + or + getEssentialTypeCategory(ctrlEssentialType) = EssentiallyEnumType() + ) and + not allowedByException(ctrlExpr, ctrlEssentialType) and + extraElement = MacroUnwrapper::unwrapElement(generic) and + ( + if extraElement instanceof Macro + then ( + extraMessage = "macro $@ " and extraString = extraElement.(Macro).getName() + ) else ( + extraMessage = "" and extraString = "" + ) + ) +select generic, + "Controlling expression in generic " + extraMessage + "has standard type " + ctrlType.toString() + + ", which doesn't match its essential type " + ctrlEssentialType.toString() + ".", extraElement, + extraString diff --git a/c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql b/c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql new file mode 100644 index 0000000000..04952ae960 --- /dev/null +++ b/c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql @@ -0,0 +1,60 @@ +/** + * @id c/misra/invalid-generic-macro-argument-evaluation + * @name RULE-23-7: A generic selection that is expanded from a macro should evaluate its argument only once + * @description A generic selection that is expanded from a macro should evaluate its argument only + * once. + * @kind problem + * @precision medium + * @problem.severity warning + * @tags external/misra/id/rule-23-7 + * correctness + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Generic + +predicate allowedByException(string parameter, ParsedGenericMacro genericMacro) { + genericMacro.expansionsOutsideExpr(parameter) = 0 and + not genericMacro.expansionsInsideAssociation(parameter, _) > 0 and + forall(MacroInvocation mi, C11GenericExpr expr | + mi.getMacro() = genericMacro and + mi.getAGeneratedElement() = expr + | + forall(Expr assoc | assoc = expr.getAnAssociationExpr() | exists(assoc.getValue())) + ) +} + +from ParsedGenericMacro genericMacro, string parameter, string reason +where + not isExcluded(genericMacro, GenericsPackage::invalidGenericMacroArgumentEvaluationQuery()) and + parameter = genericMacro.getAParameter() and + genericMacro.expansionsInsideControllingExpr(parameter) > 0 and + ( + genericMacro.expansionsOutsideExpr(parameter) > 1 and + reason = "expanded multiple times outside the generic selection" + or + genericMacro.expansionsOutsideExpr(parameter) = 1 and + genericMacro.expansionsInsideAssociation(parameter, _) > 0 and + reason = "expanded outside the generic selection and inside the generic selection" + or + genericMacro.expansionsOutsideExpr(parameter) = 0 and + exists(int i | + genericMacro.expansionsInsideAssociation(parameter, i) > 1 and + reason = "expanded in generic selection " + i.toString() + " more than once" + ) + or + genericMacro.expansionsOutsideExpr(parameter) = 0 and + exists(int i | + genericMacro.expansionsInsideAssociation(parameter, i) = 0 and + reason = "not expanded in generic selection " + i.toString() + ) and + not allowedByException(parameter, genericMacro) + ) and + not genericMacro.getBody().matches(["%sizeof%", "%__alignof%", "%typeof%", "%offsetof%"]) +select genericMacro, + "Generic macro " + genericMacro.getName() + " may have unexpected behavior from side effects " + + "in parameter " + parameter + ", as it is " + reason + "." diff --git a/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql new file mode 100644 index 0000000000..6e443bd162 --- /dev/null +++ b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql @@ -0,0 +1,96 @@ +/** + * @id c/misra/default-generic-selection-not-first-or-last + * @name RULE-23-8: A default association shall appear as either the first or the last association of a generic + * @description A default association shall appear as either the first or the last association of a + * generic selection. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-8 + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +class GenericWithMisplacedDefault extends C11GenericExpr { + int defaultIdx; + + GenericWithMisplacedDefault() { + getAssociationType(defaultIdx) instanceof VoidType and + not defaultIdx = 0 and + not defaultIdx = max(int i | exists(getAssociationType(i))) + } + + int getDefaultIdx() { result = defaultIdx } +} + +module GenericWithMisplacedDefaultConfig implements + DeduplicateMacroConfigSig +{ + string describe(GenericWithMisplacedDefault e) { + exists(int i | i = e.getDefaultIdx() + 1 | + i = 1 and result = "1st" + or + i = 2 and result = "2nd" + or + i = 3 and result = "3rd" + or + i > 3 and result = i.toString() + "th" + ) + } +} + +import GenericWithMisplacedDefaultConfig + +module GenericMisplacedDefaultReportConfig implements + MacroReportConfigSig +{ + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = + "Generic macro " + m.getName() + " has default as " + description + + " association, which is not first or last." + } + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Generic macro " + m.getName() + + " has a default association which is not first or last, for example $@." + } + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(GenericWithMisplacedDefault element) { + result = + "Generic macro $@, in this expansion, has default as " + describe(element) + + " association, which is not first or last." + } + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro(GenericWithMisplacedDefault element, Locatable optLoc1, string optStr1) { + result = + "Generic has default as " + describe(element) + " association, which is not first or last." and + optLoc1 = element and + optStr1 = "" + } +} + +import DeduplicateMacroResults as Deduplicate +import Deduplicate::Report as Report + +from Report::ReportResult res +where + not isExcluded(res.getPrimaryElement(), + GenericsPackage::defaultGenericSelectionNotFirstOrLastQuery()) +select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), + res.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.ql b/c/misra/src/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.ql index b7d01b4eae..af05bfe4bc 100644 --- a/c/misra/src/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.ql +++ b/c/misra/src/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.ql @@ -9,31 +9,45 @@ * @tags external/misra/id/rule-3-1 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -class IllegalCCommentCharacter extends string { - IllegalCCommentCharacter(){ - this = "/*" or - this = "//" - } +/* Character sequence is banned from all comment types */ +class IllegalCommentSequence extends string { + IllegalCommentSequence() { this = "/*" } } -class IllegalCPPCommentCharacter extends string { - IllegalCPPCommentCharacter(){ - this = "/*" +/* A regexp to check for illegal C-style comments */ +class IllegalCCommentRegexp extends string { + IllegalCCommentRegexp() { + // Regexp to match "//" in C-style comments, which do not appear to be URLs. General format + // uses negative lookahead/lookbehind to match like `.*(? 0) or - exists(IllegalCPPCommentCharacter c | illegalSequence = c | comment.(CppStyleComment).getContents().indexOf(illegalSequence) > 0) + exists(IllegalCommentSequence c | illegalSequence = c | + comment.getContents().indexOf(illegalSequence) > 1 + ) + or + exists(IllegalCCommentRegexp c | illegalSequence = c.getDescription() | + comment.(CStyleComment).getContents().regexpMatch(c) + ) ) select comment, "Comment contains an illegal sequence '" + illegalSequence + "'" diff --git a/c/misra/src/rules/RULE-3-2/LineSplicingUsedInComments.ql b/c/misra/src/rules/RULE-3-2/LineSplicingUsedInComments.ql index cf6a2bb547..f1fd85b129 100644 --- a/c/misra/src/rules/RULE-3-2/LineSplicingUsedInComments.ql +++ b/c/misra/src/rules/RULE-3-2/LineSplicingUsedInComments.ql @@ -10,6 +10,7 @@ * maintainability * readability * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.ql b/c/misra/src/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.ql index a7fdf080a7..0f04a7362b 100644 --- a/c/misra/src/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.ql +++ b/c/misra/src/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.ql @@ -10,39 +10,17 @@ * maintainability * readability * correctness + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra +import codingstandards.cpp.rules.nonterminatedescapesequences.NonTerminatedEscapeSequences -bindingset[s] -predicate isOctalEscape(string s) { - s.charAt(0) = "\\" and - exists(int i | i = [0 .. 7] | i.toString() = s.charAt(1)) +class OctalAndHexadecimalEscapeSequencesNotTerminatedQuery extends NonTerminatedEscapeSequencesSharedQuery +{ + OctalAndHexadecimalEscapeSequencesNotTerminatedQuery() { + this = SyntaxPackage::octalAndHexadecimalEscapeSequencesNotTerminatedQuery() + } } - -bindingset[s] -predicate isHexEscape(string s) { s.indexOf("\\x") = 0 } - -from Literal l, string escapeKind, string s -where - not isExcluded(l, SyntaxPackage::octalAndHexadecimalEscapeSequencesNotTerminatedQuery()) and - exists(int idx, string sl | - sl = l.getValueText() and - idx = sl.indexOf("\\") and - s = sl.substring(idx, sl.length()) and - // Note: Octal representations must be 1-3 digits. There is no limitation on a - // Hex literal as long as the characters are valid. This query does not consider - // if the hex literal being constructed will overflow. - ( - isHexEscape(s) and - not s.regexpMatch("^((\\\\x[0-9A-F]+(?=[\"'\\\\])))[\\s\\S]*") and - escapeKind = "hexadecimal" - or - isOctalEscape(s) and - not s.regexpMatch("^(((\\\\[0-7]{1,3})(?=[\"'\\\\])))[\\s\\S]*") and - escapeKind = "octal" - ) - ) -select l, "Invalid " + escapeKind + " escape in string literal at '" + s + "'." diff --git a/c/misra/src/rules/RULE-4-4/SectionsOfCodeShallNotBeCommentedOut.ql b/c/misra/src/rules/RULE-4-4/SectionsOfCodeShallNotBeCommentedOut.ql deleted file mode 100644 index 52adbac29d..0000000000 --- a/c/misra/src/rules/RULE-4-4/SectionsOfCodeShallNotBeCommentedOut.ql +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @id c/misra/sections-of-code-shall-not-be-commented-out - * @name RULE-4-4: Sections of code should not be commented out - * @description Commented out code may become out of date leading to developer confusion. - * @kind problem - * @precision high - * @problem.severity warning - * @tags external/misra/id/rule-4-4 - * maintainability - * readability - * correctness - * external/misra/obligation/advisory - */ - -import cpp -import codingstandards.c.misra -import codingstandards.cpp.rules.sectionsofcodeshallnotbecommentedout.SectionsOfCodeShallNotBeCommentedOut - -class SectionsOfCodeShallNotBeCommentedOutQuery extends SectionsOfCodeShallNotBeCommentedOutSharedQuery { - SectionsOfCodeShallNotBeCommentedOutQuery() { - this = SyntaxPackage::sectionsOfCodeShallNotBeCommentedOutQuery() - } -} diff --git a/c/misra/src/rules/RULE-5-1/ExternalIdentifiersNotDistinct.ql b/c/misra/src/rules/RULE-5-1/ExternalIdentifiersNotDistinct.ql index 774bc97663..2c2c302bc0 100644 --- a/c/misra/src/rules/RULE-5-1/ExternalIdentifiersNotDistinct.ql +++ b/c/misra/src/rules/RULE-5-1/ExternalIdentifiersNotDistinct.ql @@ -9,6 +9,7 @@ * correctness * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -17,7 +18,7 @@ import codingstandards.c.misra import codingstandards.cpp.rules.notdistinctidentifier.NotDistinctIdentifier class ExternalIdentifiersNotDistinct extends NotDistinctIdentifierSharedQuery { - ExternalIdentifiersNotDistinct() { + ExternalIdentifiersNotDistinct() { this = Declarations1Package::externalIdentifiersNotDistinctQuery() } -} \ No newline at end of file +} diff --git a/c/misra/src/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.ql b/c/misra/src/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.ql new file mode 100644 index 0000000000..eb24d1c094 --- /dev/null +++ b/c/misra/src/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.ql @@ -0,0 +1,39 @@ +/** + * @id c/misra/identifiers-declared-in-the-same-scope-not-distinct + * @name RULE-5-2: Identifiers declared in the same scope and name space shall be distinct + * @description Using nondistinct identifiers results in undefined behaviour. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-5-2 + * correctness + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers + +from InterestingIdentifiers d, InterestingIdentifiers d2 +where + not isExcluded(d, Declarations5Package::identifiersDeclaredInTheSameScopeNotDistinctQuery()) and + not isExcluded(d2, Declarations5Package::identifiersDeclaredInTheSameScopeNotDistinctQuery()) and + //this rule does not apply if both are external identifiers + //that is covered by RULE-5-3 + not ( + d instanceof ExternalIdentifiers and + d2 instanceof ExternalIdentifiers + ) and + d.getNamespace() = d2.getNamespace() and + d.getParentScope() = d2.getParentScope() and + not d = d2 and + d.getLocation().getStartLine() >= d2.getLocation().getStartLine() and + //first 63 chars in the name as per C99 + d.getSignificantNameComparedToMacro() = d2.getSignificantNameComparedToMacro() and + not d.getName() = d2.getName() +select d, + "Identifer " + d.getName() + " is nondistinct in characters at or over 63 limit, compared to $@", + d2, d2.getName() diff --git a/c/misra/src/rules/RULE-5-3/IdentifierHidingC.ql b/c/misra/src/rules/RULE-5-3/IdentifierHidingC.ql index 94d56367fe..1c54b70147 100644 --- a/c/misra/src/rules/RULE-5-3/IdentifierHidingC.ql +++ b/c/misra/src/rules/RULE-5-3/IdentifierHidingC.ql @@ -10,6 +10,7 @@ * @tags external/misra/id/rule-5-3 * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -18,7 +19,5 @@ import codingstandards.c.misra import codingstandards.cpp.rules.identifierhidden.IdentifierHidden class IdentifierHidingCQuery extends IdentifierHiddenSharedQuery { - IdentifierHidingCQuery() { - this = Declarations3Package::identifierHidingCQuery() - } + IdentifierHidingCQuery() { this = Declarations3Package::identifierHidingCQuery() } } diff --git a/c/misra/src/rules/RULE-5-4/MacroIdentifierNotDistinctFromParameter.ql b/c/misra/src/rules/RULE-5-4/MacroIdentifierNotDistinctFromParameter.ql index 886e05f0ea..d8a78cb680 100644 --- a/c/misra/src/rules/RULE-5-4/MacroIdentifierNotDistinctFromParameter.ql +++ b/c/misra/src/rules/RULE-5-4/MacroIdentifierNotDistinctFromParameter.ql @@ -8,6 +8,7 @@ * @tags external/misra/id/rule-5-4 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-5-4/MacroIdentifiersNotDistinct.ql b/c/misra/src/rules/RULE-5-4/MacroIdentifiersNotDistinct.ql index 1dd0fe196e..36b946491b 100644 --- a/c/misra/src/rules/RULE-5-4/MacroIdentifiersNotDistinct.ql +++ b/c/misra/src/rules/RULE-5-4/MacroIdentifiersNotDistinct.ql @@ -9,13 +9,62 @@ * correctness * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra +import codingstandards.cpp.Macro +import codingstandards.cpp.Includes +import codingstandards.cpp.PreprocessorDirective -from Macro m, Macro m2 +/** + * Gets a top level element that this macro is expanded to, e.g. an element which does not also have + * an enclosing element in the macro. + */ +Element getATopLevelElement(MacroInvocation mi) { + result = mi.getAnExpandedElement() and + not result.getEnclosingElement() = mi.getAnExpandedElement() and + not result instanceof Conversion +} + +/** + * Gets a link target that this macro is expanded in. + */ +LinkTarget getALinkTarget(Macro m) { + exists(MacroInvocation mi, Element e | + mi = m.getAnInvocation() and + e = getATopLevelElement(mi) + | + result = e.(Expr).getEnclosingFunction().getALinkTarget() + or + result = e.(Stmt).getEnclosingFunction().getALinkTarget() + or + exists(GlobalOrNamespaceVariable g | + result = g.getALinkTarget() and + g = e.(Expr).getEnclosingDeclaration() + ) + ) +} + +/** + * Holds if the m1 and m2 are unconditionally included from a common file. + * + * Extracted out for performance reasons - otherwise the call to determine the file path for the + * message was specializing the calls to `getAnUnconditionallyIncludedFile*(..)` and causing + * slow performance. + */ +bindingset[m1, m2] +pragma[inline_late] +private predicate isIncludedUnconditionallyFromCommonFile(Macro m1, Macro m2) { + exists(File f | + getAnUnconditionallyIncludedFile*(f) = m1.getFile() and + getAnUnconditionallyIncludedFile*(f) = m2.getFile() + ) +} + +from Macro m, Macro m2, string message where not isExcluded(m, Declarations1Package::macroIdentifiersNotDistinctQuery()) and not m = m2 and @@ -24,10 +73,40 @@ where //C90 states the first 31 characters of macro identifiers are significant and is not currently considered by this rule //ie an identifier differing on the 32nd character would be indistinct for C90 but distinct for C99 //and is currently not reported by this rule - if m.getName().length() >= 64 - then m.getName().prefix(63) = m2.getName().prefix(63) - else m.getName() = m2.getName() + if m.getName().length() >= 64 and not m.getName() = m2.getName() + then ( + m.getName().prefix(63) = m2.getName().prefix(63) and + message = + "Macro identifer " + m.getName() + " is nondistinct in first 63 characters, compared to $@." + ) else ( + m.getName() = m2.getName() and + message = + "Definition of macro " + m.getName() + + " is not distinct from alternative definition of $@ in " + + m2.getLocation().getFile().getRelativePath() + "." + ) ) and //reduce double report since both macros are in alert, arbitrary ordering - m.getLocation().getStartLine() >= m2.getLocation().getStartLine() -select m, "Macro identifer " + m.getName() + " is nondistinct in first 63 characters, compared to $@.", m2, m2.getName() + m.getLocation().getStartLine() >= m2.getLocation().getStartLine() and + // Not within an #ifndef MACRO_NAME + not exists(PreprocessorIfndef ifBranch | + m.getAGuard() = ifBranch or + m2.getAGuard() = ifBranch + | + ifBranch.getHead() = m.getName() + ) and + // Must be included unconditionally from the same file, otherwise m1 may not be defined + // when m2 is defined + isIncludedUnconditionallyFromCommonFile(m, m2) and + // Macros can't be mutually exclusive + not mutuallyExclusiveBranchDirectiveMacros(m, m2) and + not mutuallyExclusiveBranchDirectiveMacros(m2, m) and + // If at least one invocation exists for at least one of the macros, then they must share a link + // target - i.e. must both be expanded in the same context + ( + (exists(m.getAnInvocation()) and exists(m2.getAnInvocation())) + implies + // Must share a link target - e.g. must both be expanded in the same context + getALinkTarget(m) = getALinkTarget(m2) + ) +select m, message, m2, m2.getName() diff --git a/c/misra/src/rules/RULE-5-5/IdentifiersNotDistinctFromMacroNames.ql b/c/misra/src/rules/RULE-5-5/IdentifiersNotDistinctFromMacroNames.ql index 2ee6ef26d2..da6b725ab5 100644 --- a/c/misra/src/rules/RULE-5-5/IdentifiersNotDistinctFromMacroNames.ql +++ b/c/misra/src/rules/RULE-5-5/IdentifiersNotDistinctFromMacroNames.ql @@ -9,12 +9,13 @@ * @tags external/misra/id/rule-5-5 * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Identifiers +import codingstandards.cpp.Identifiers from Macro m, InterestingIdentifiers i, string mName, string iName where diff --git a/c/misra/src/rules/RULE-5-6/TypedefNameNotUnique.ql b/c/misra/src/rules/RULE-5-6/TypedefNameNotUnique.ql index eebdabe956..1398df6a4d 100644 --- a/c/misra/src/rules/RULE-5-6/TypedefNameNotUnique.ql +++ b/c/misra/src/rules/RULE-5-6/TypedefNameNotUnique.ql @@ -9,12 +9,13 @@ * @tags external/misra/id/rule-5-6 * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Identifiers +import codingstandards.cpp.Identifiers from TypedefType t, InterestingIdentifiers d where diff --git a/c/misra/src/rules/RULE-5-7/TagNameNotUnique.ql b/c/misra/src/rules/RULE-5-7/TagNameNotUnique.ql index f7c005d7fa..fa6560ab49 100644 --- a/c/misra/src/rules/RULE-5-7/TagNameNotUnique.ql +++ b/c/misra/src/rules/RULE-5-7/TagNameNotUnique.ql @@ -9,12 +9,13 @@ * @tags external/misra/id/rule-5-7 * readability * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.c.Identifiers +import codingstandards.cpp.Identifiers from Struct s, InterestingIdentifiers s2 where @@ -22,7 +23,6 @@ where not isExcluded(s2, Declarations3Package::tagNameNotUniqueQuery()) and not s = s2 and s.getName() = s2.getName() and - not s.getName() = "struct " and - not s.getName() = "union " and + not s.isAnonymous() and not s.getName() = s2.(TypedefType).getBaseType().toString() select s, "Tag name is nonunique compared to $@.", s2, s2.getName() diff --git a/c/misra/src/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.ql b/c/misra/src/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.ql new file mode 100644 index 0000000000..fa1b2b1fad --- /dev/null +++ b/c/misra/src/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.ql @@ -0,0 +1,72 @@ +/** + * @id c/misra/identifiers-with-external-linkage-not-unique + * @name RULE-5-8: Identifiers that define objects or functions with external linkage shall be unique + * @description Using non-unique identifiers can lead to developer confusion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-8 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers + +/** + * Holds if the `identifierName` has conflicting declarations. + */ +predicate isExternalIdentifierNotUnique(string identifierName) { + // More than one declaration with this name + count(Declaration d | d.getName() = identifierName) > 1 and + // At least one declaration is an external identifier + exists(ExternalIdentifiers e | e.getName() = identifierName) +} + +/** + * Holds if the `Declaration` `d` is conflicting with an external identifier. + */ +predicate isConflictingDeclaration(Declaration d, string name) { + isExternalIdentifierNotUnique(name) and + d.getName() = name +} + +/** + * An external identifier which is not uniquely defined in the source code. + */ +class NotUniqueExternalIdentifier extends ExternalIdentifiers { + NotUniqueExternalIdentifier() { isExternalIdentifierNotUnique(getName()) } + + Declaration getAConflictingDeclaration() { + not result = this and + isConflictingDeclaration(result, getName()) and + // We only consider a declaration to be conflicting if it shares a link target with the external + // identifier. This avoids reporting false positives where multiple binaries or libraries are + // built in the same CodeQL database, but are not intended to be linked together. + exists(LinkTarget lt | + // External declaration can only be a function or global variable + lt = this.(Function).getALinkTarget() or + lt = this.(GlobalVariable).getALinkTarget() + | + lt = result.(Function).getALinkTarget() + or + lt = result.(GlobalVariable).getALinkTarget() + or + exists(Class c | c.getAMember() = result and c.getALinkTarget() = lt) + or + result.(LocalVariable).getFunction().getALinkTarget() = lt + or + result.(Class).getALinkTarget() = lt + ) + } +} + +from NotUniqueExternalIdentifier e, Declaration de +where + not isExcluded(de, Declarations6Package::identifiersWithExternalLinkageNotUniqueQuery()) and + not isExcluded(e, Declarations6Package::identifiersWithExternalLinkageNotUniqueQuery()) and + de = e.getAConflictingDeclaration() +select de, "Identifier conflicts with external identifier $@", e, e.getName() diff --git a/c/misra/src/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.ql b/c/misra/src/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.ql new file mode 100644 index 0000000000..fcba48f2fd --- /dev/null +++ b/c/misra/src/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.ql @@ -0,0 +1,33 @@ +/** + * @id c/misra/identifiers-with-internal-linkage-not-unique + * @name RULE-5-9: Identifiers that define objects or functions with internal linkage should be unique + * @description Using non-unique identifiers can lead to developer confusion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-9 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from Declaration d1, Declaration d2 +where + not isExcluded(d1, Declarations6Package::identifiersWithInternalLinkageNotUniqueQuery()) and + not isExcluded(d2, Declarations6Package::identifiersWithInternalLinkageNotUniqueQuery()) and + d1.isStatic() and + d1.isTopLevel() and + not d1 = d2 and + d1.getName() = d2.getName() and + // Apply an ordering based on location to enforce that (d1, d2) = (d2, d1) and we only report (d1, d2). + ( + d1.getFile().getAbsolutePath() < d2.getFile().getAbsolutePath() + or + d1.getFile().getAbsolutePath() = d2.getFile().getAbsolutePath() and + d1.getLocation().getStartLine() < d2.getLocation().getStartLine() + ) +select d2, "Identifier conflicts with identifier $@ with internal linkage.", d1, d1.getName() diff --git a/c/misra/src/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql b/c/misra/src/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql new file mode 100644 index 0000000000..078c2c48b7 --- /dev/null +++ b/c/misra/src/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/bit-fields-shall-only-be-declared-with-an-appropriate-type + * @name RULE-6-1: Bit-fields shall only be declared with an appropriate type + * @description Declaring bit-fields on types other than appropriate ones causes + * implementation-specific or undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-1 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.bitfieldshallhaveanappropriatetype.BitFieldShallHaveAnAppropriateType + +class BitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery extends BitFieldShallHaveAnAppropriateTypeSharedQuery +{ + BitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery() { + this = BitfieldTypesPackage::bitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery() + } +} diff --git a/c/misra/src/rules/RULE-6-2/SingleBitNamedBitFieldsOfASignedType.ql b/c/misra/src/rules/RULE-6-2/SingleBitNamedBitFieldsOfASignedType.ql new file mode 100644 index 0000000000..142a0b542d --- /dev/null +++ b/c/misra/src/rules/RULE-6-2/SingleBitNamedBitFieldsOfASignedType.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/single-bit-named-bit-fields-of-a-signed-type + * @name RULE-6-2: Single-bit named bit fields shall not be of a signed type + * @description Single-bit named bit fields carry no useful information and therefore should not be + * declared or used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-2 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.namedbitfieldswithsignedintegertype.NamedBitFieldsWithSignedIntegerType + +class SingleBitNamedBitFieldsOfASignedTypeQuery extends NamedBitFieldsWithSignedIntegerTypeSharedQuery +{ + SingleBitNamedBitFieldsOfASignedTypeQuery() { + this = BitfieldTypesPackage::singleBitNamedBitFieldsOfASignedTypeQuery() + } +} diff --git a/c/misra/src/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.ql b/c/misra/src/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.ql new file mode 100644 index 0000000000..4befbb9dd6 --- /dev/null +++ b/c/misra/src/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/bit-field-declared-as-member-of-a-union + * @name RULE-6-3: A bit field shall not be declared as a member of a union + * @description Type punning on a union with bit fields relies on implementation-specific alignment + * behavior. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-6-3 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from BitField field, Union u +where + not isExcluded(field, BitfieldTypes2Package::bitFieldDeclaredAsMemberOfAUnionQuery()) and + u.getAField() = field +select field, + "Union member " + field.getName() + + " is declared as a bit field which relies on implementation-specific behavior." diff --git a/c/misra/src/rules/RULE-7-1/OctalConstantsUsed.ql b/c/misra/src/rules/RULE-7-1/OctalConstantsUsed.ql index d4a6c332a7..9934e80487 100644 --- a/c/misra/src/rules/RULE-7-1/OctalConstantsUsed.ql +++ b/c/misra/src/rules/RULE-7-1/OctalConstantsUsed.ql @@ -10,6 +10,7 @@ * readability * correctness * maintainability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.ql b/c/misra/src/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.ql index b1dca9ac4a..c02e0e2aca 100644 --- a/c/misra/src/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.ql +++ b/c/misra/src/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-7-2 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ @@ -19,6 +20,13 @@ from Literal l where not isExcluded(l, SyntaxPackage::uOrUSuffixRepresentedInUnsignedTypeQuery()) and not l instanceof StringLiteral and - l.getImplicitlyConverted().getType().(IntegralType).isUnsigned() and - not exists(l.getValueText().toUpperCase().indexOf("U")) -select l, "Unsigned literal does not explicitly express sign with a 'U' or 'u' suffix." + // Determine if the extractor deduced that the literal is unsigned, based on the C rules + l.getType().(IntegralType).isUnsigned() and + // And report if the literal does not contain a 'U' or 'u' suffix, e.g. explicitly unsigned + not exists(l.getValueText().toUpperCase().indexOf("U")) and + // Exclude constants generated by macro expansions, because the suffix information is lost in this + // case, so can cause false positives. + not l.isInMacroExpansion() +select l, + "Unsigned literal " + l.getValueText() + + " does not explicitly express sign with a 'U' or 'u' suffix." diff --git a/c/misra/src/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.ql b/c/misra/src/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.ql index 311831d2b8..0b38b26eea 100644 --- a/c/misra/src/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.ql +++ b/c/misra/src/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.ql @@ -9,15 +9,16 @@ * @tags external/misra/id/rule-7-3 * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ import cpp import codingstandards.c.misra +import codingstandards.cpp.Literals -from Literal l +from IntegerLiteral l where not isExcluded(l, SyntaxPackage::lowercaseCharacterLUsedInLiteralSuffixQuery()) and - not l instanceof StringLiteral and exists(l.getValueText().indexOf("l")) select l, "Lowercase 'l' used as a literal suffix." diff --git a/c/misra/src/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.ql b/c/misra/src/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.ql new file mode 100644 index 0000000000..bc2fa5f5bf --- /dev/null +++ b/c/misra/src/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.ql @@ -0,0 +1,104 @@ +/** + * @id c/misra/string-literal-assigned-to-non-const-char + * @name RULE-7-4: A string literal shall only be assigned to a pointer to const char + * @description Assigning string literal to a variable with type other than a pointer to const char + * and modifying it causes undefined behavior . + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-4 + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +/** Pointer to Wide character type, i.e. `wchar_t*`. */ +class WideCharPointerType extends PointerType { + WideCharPointerType() { this.getBaseType() instanceof Wchar_t } + + override string getAPrimaryQlClass() { result = "WideCharPointerType" } +} + +class GenericCharPointerType extends Type { + GenericCharPointerType() { + // A wide char pointer type + this instanceof WideCharPointerType + or + // A char pointer type + this.getUnspecifiedType() instanceof CharPointerType + or + // A typedef to any such type. + // Note: wchar_t is usually a typedef, so we cannot just use getUnspecifiedType() here. + this.(TypedefType).getBaseType() instanceof GenericCharPointerType + } +} + +class NonConstCharStarType extends Type { + NonConstCharStarType() { + this instanceof GenericCharPointerType and + not this.isDeeplyConstBelow() + } +} + +/* A non-const-char* variable declared with a string literal */ +predicate declaringNonConstCharVar(Variable decl, string message) { + not decl instanceof Parameter and // exclude parameters + /* It should be declaring a char* type variable */ + decl.getType() instanceof NonConstCharStarType and + /* But it's declared to hold a string literal. */ + decl.getInitializer().getExpr() instanceof StringLiteral and + message = + decl.getType().(GenericCharPointerType) + " variable " + decl + + " is declared with a string literal." +} + +/* String literal being assigned to a non-const-char* variable */ +predicate assignmentToNonConstCharVar(Assignment assign, string message) { + /* The variable being assigned is char* */ + assign.getLValue().getType() instanceof NonConstCharStarType and + /* But the rvalue is a string literal */ + assign.getRValue() instanceof StringLiteral and + message = + assign.getLValue().getType().(GenericCharPointerType) + " variable " + assign.getLValue() + + " is assigned a string literal. " +} + +/* String literal being passed to a non-const-char* parameter */ +predicate assignmentToNonConstCharParam(FunctionCall call, string message) { + exists(int index | + /* Param at index is a char* */ + call.getTarget().getParameter(index).getType() instanceof NonConstCharStarType and + /* But a string literal is passed */ + call.getArgument(index) instanceof StringLiteral and + message = + call.getTarget().getParameter(index).getType().(GenericCharPointerType) + " parameter of " + + call.getTarget() + " is passed a string literal." + ) +} + +/* String literal being returned by a non-const-char* function */ +predicate returningNonConstCharVar(ReturnStmt return, string message) { + /* The function is declared to return a char* */ + return.getEnclosingFunction().getType() instanceof NonConstCharStarType and + /* But in reality it returns a string literal */ + return.getExpr() instanceof StringLiteral and + message = + return.getEnclosingFunction().getType().(GenericCharPointerType) + " function " + + return.getEnclosingFunction() + " is returning a string literal." +} + +from Element elem, string message +where + not isExcluded(elem, Types1Package::stringLiteralAssignedToNonConstCharQuery()) and + ( + declaringNonConstCharVar(elem, message) + or + assignmentToNonConstCharVar(elem, message) + or + assignmentToNonConstCharParam(elem, message) + or + returningNonConstCharVar(elem, message) + ) +select elem, message diff --git a/c/misra/src/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.ql b/c/misra/src/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.ql new file mode 100644 index 0000000000..1fe052aaae --- /dev/null +++ b/c/misra/src/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.ql @@ -0,0 +1,46 @@ +/** + * @id c/misra/incorrectly-sized-integer-constant-macro-argument + * @name RULE-7-5: The argument of an integer constant macro shall have an appropriate size + * @description Integer constant macros argument values should be values of a compatible size. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.IntegerConstantMacro +import codingstandards.cpp.Literals + +predicate matchesSign(IntegerConstantMacro macro, PossiblyNegativeLiteral literal) { + literal.isNegative() implies macro.isSigned() +} + +bindingset[literal] +predicate matchesSize(IntegerConstantMacro macro, PossiblyNegativeLiteral literal) { + literal.getRawValue() <= macro.maxValue() and + literal.getRawValue() >= macro.minValue() +} + +from + PossiblyNegativeLiteral literal, MacroInvocation invoke, IntegerConstantMacro macro, + string explanation +where + not isExcluded(invoke, Types2Package::incorrectlySizedIntegerConstantMacroArgumentQuery()) and + invoke.getMacro() = macro and + literal = invoke.getExpr() and + ( + not matchesSign(macro, literal) and + explanation = " cannot be negative" + or + matchesSign(macro, literal) and + // Wait for BigInt support to check 64 bit macro types. + macro.getSize() < 64 and + not matchesSize(macro, literal) and + explanation = " is outside of the allowed range " + macro.getRangeString() + ) +select literal, "Value provided to integer constant macro " + macro.getName() + explanation diff --git a/c/misra/src/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.ql b/c/misra/src/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.ql new file mode 100644 index 0000000000..84fb1a9872 --- /dev/null +++ b/c/misra/src/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.ql @@ -0,0 +1,35 @@ +/** + * @id c/misra/integer-constant-macro-argument-uses-suffix + * @name RULE-7-5: The argument of an integer constant macro shall not use literal suffixes u, l, or ul + * @description Integer constant macros should be used integer literal values with no u/l suffix. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-7-5 + * readability + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.IntegerConstantMacro +import codingstandards.cpp.Literals + +string argumentSuffix(MacroInvocation invoke) { + // Extractor strips the suffix unless we look at the unexpanded argument text. + // Unexpanded argument text can be malformed in all sorts of ways, so make + // this match relatively strict, to be safe. + result = invoke.getUnexpandedArgument(0).regexpCapture("([0-9]+|0[xX][0-9A-F]+)([uUlL]+)$", 2) +} + +from MacroInvocation invoke, PossiblyNegativeLiteral argument, string suffix +where + not isExcluded(invoke, Types2Package::integerConstantMacroArgumentUsesSuffixQuery()) and + invoke.getMacro() instanceof IntegerConstantMacro and + invoke.getExpr() = argument and + suffix = argumentSuffix(invoke) +select argument, + "Value suffix '" + suffix + "' is not allowed on provided argument to integer constant macro " + + invoke.getMacroName() + "." diff --git a/c/misra/src/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.ql b/c/misra/src/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.ql new file mode 100644 index 0000000000..4c750e32d8 --- /dev/null +++ b/c/misra/src/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.ql @@ -0,0 +1,30 @@ +/** + * @id c/misra/invalid-integer-constant-macro-argument + * @name RULE-7-5: The argument of an integer constant macro shall be a literal + * @description Integer constant macros should be given a literal value as an argument. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-7-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.IntegerConstantMacro +import codingstandards.cpp.Literals +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from MacroInvocation invoke, IntegerConstantMacro macro +where + not isExcluded(invoke, Types2Package::invalidIntegerConstantMacroArgumentQuery()) and + invoke.getMacro() = macro and + ( + not invoke.getExpr() instanceof PossiblyNegativeLiteral + or + any(MacroInvocation inner).getParentInvocation() = invoke + ) +select invoke.getExpr(), + "Argument to integer constant macro " + macro.getName() + " must be an integer literal." diff --git a/c/misra/src/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.ql b/c/misra/src/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.ql new file mode 100644 index 0000000000..e4e660c628 --- /dev/null +++ b/c/misra/src/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.ql @@ -0,0 +1,77 @@ +/** + * @id c/misra/invalid-literal-for-integer-constant-macro-argument + * @name RULE-7-5: The argument of an integer constant macro shall be a decimal, hex, or octal literal + * @description Integer constant macro arguments should be a decimal, hex, or octal literal. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.IntegerConstantMacro +import codingstandards.cpp.Literals + +/** + * Floating point literals are not allowed. Neither are char or string + * literals, although those are not `NumericLiteral`s and therefore detected in + * `InvalidIntegerConstantMacroArgument.ql`. + */ +predicate validLiteralType(PossiblyNegativeLiteral literal) { + literal.getBaseLiteral() instanceof Cpp14Literal::DecimalLiteral or + literal.getBaseLiteral() instanceof Cpp14Literal::OctalLiteral or + literal.getBaseLiteral() instanceof Cpp14Literal::HexLiteral or + // Ignore cases where the AST/extractor don't give us enough information: + literal.getBaseLiteral() instanceof Cpp14Literal::UnrecognizedNumericLiteral +} + +/** + * Clang accepts `xINTsize_C(0b01)`, and expands the argument into a decimal + * literal. Binary literals are not standard c nor are they allowed by rule 7-5. + * Detect this pattern before macro expansion. + */ +predicate seemsBinaryLiteral(MacroInvocation invoke) { + invoke.getUnexpandedArgument(0).regexpMatch("-?0[bB][01]+") +} + +/** + * Extractor converts `xINTsize_C('a')` to a decimal literal. Therefore, detect + * this pattern before macro expansion. + */ +predicate seemsCharLiteral(MacroInvocation invoke) { + invoke.getUnexpandedArgument(0).regexpMatch("-?'\\\\?.'") +} + +string explainIncorrectArgument(MacroInvocation invoke) { + if seemsBinaryLiteral(invoke) + then result = "binary literal" + else + if seemsCharLiteral(invoke) + then result = "char literal" + else + exists(PossiblyNegativeLiteral literal | + literal = invoke.getExpr() and + if literal.getBaseLiteral() instanceof Cpp14Literal::FloatingLiteral + then result = "floating point literal" + else result = "invalid literal" + ) +} + +from MacroInvocation invoke, PossiblyNegativeLiteral literal +where + not isExcluded(invoke, Types2Package::invalidLiteralForIntegerConstantMacroArgumentQuery()) and + invoke.getMacro() instanceof IntegerConstantMacro and + literal = invoke.getExpr() and + ( + not validLiteralType(literal) or + seemsBinaryLiteral(invoke) or + seemsCharLiteral(invoke) + ) +select literal, + "Integer constant macro " + invoke.getMacroName() + " used with " + + explainIncorrectArgument(invoke) + + " argument, only decimal, octal, or hex integer literal allowed." diff --git a/c/misra/src/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.ql b/c/misra/src/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.ql new file mode 100644 index 0000000000..47e88196d5 --- /dev/null +++ b/c/misra/src/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/use-of-banned-small-integer-constant-macro + * @name RULE-7-6: The small integer variants of the minimum-width integer constant macros shall not be used + * @description Small integer constant macros expression are promoted to type int, which can lead to + * unexpected results. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-7-6 + * readability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.IntegerConstantMacro + +from MacroInvocation macroInvoke, IntegerConstantMacro macro +where + not isExcluded(macroInvoke, Types2Package::useOfBannedSmallIntegerConstantMacroQuery()) and + macroInvoke.getMacro() = macro and + macro.isSmall() +select macroInvoke, "Usage of small integer constant macro " + macro.getName() + " is not allowed." diff --git a/c/misra/src/rules/RULE-8-1/ExplicitlyDeclareTypes.ql b/c/misra/src/rules/RULE-8-1/ExplicitlyDeclareTypes.ql index bfcbac4435..6484372f5b 100644 --- a/c/misra/src/rules/RULE-8-1/ExplicitlyDeclareTypes.ql +++ b/c/misra/src/rules/RULE-8-1/ExplicitlyDeclareTypes.ql @@ -8,6 +8,7 @@ * @tags external/misra/id/rule-8-1 * correctness * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.ql b/c/misra/src/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.ql new file mode 100644 index 0000000000..250c00ca2e --- /dev/null +++ b/c/misra/src/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/inline-function-not-declared-static-storage + * @name RULE-8-10: An inline function shall be declared with the static storage class + * @description Declaring an inline function with external linkage can lead to undefined or + * incorrect program behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-10 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers + +from FunctionDeclarationEntry f +where + not isExcluded(f, Declarations6Package::inlineFunctionNotDeclaredStaticStorageQuery()) and + f.getFunction() instanceof InterestingIdentifiers and + f.getFunction().isInline() and + not f.hasSpecifier("static") +select f, "Inline function not explicitly declared static." diff --git a/c/misra/src/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.ql b/c/misra/src/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.ql new file mode 100644 index 0000000000..d14e236755 --- /dev/null +++ b/c/misra/src/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.ql @@ -0,0 +1,29 @@ +/** + * @id c/misra/array-external-linkage-size-explicitly-specified + * @name RULE-8-11: When an array with external linkage is declared, its size should be explicitly specified + * @description Declaring an array without an explicit size disallows the compiler and static + * checkers from doing array bounds analysis and can lead to less readable, unsafe + * code. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-11 + * correctness + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers + +from VariableDeclarationEntry v, ArrayType t +where + not isExcluded(v, Declarations6Package::arrayExternalLinkageSizeExplicitlySpecifiedQuery()) and + v.getDeclaration() instanceof ExternalIdentifiers and + v.getType() = t and + not exists(t.getSize()) and + //this rule applies to non-defining declarations only + not v.isDefinition() +select v, "Array declared without explicit size." diff --git a/c/misra/src/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.ql b/c/misra/src/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.ql new file mode 100644 index 0000000000..6ebabc3810 --- /dev/null +++ b/c/misra/src/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/value-implicit-enumeration-constant-not-unique + * @name RULE-8-12: Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique + * @description Using an implicitly specified enumeration constant that is not unique (with respect + * to an explicitly specified constant) can lead to unexpected program behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-12 + * correctness + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.nonuniqueenumerationconstant.NonUniqueEnumerationConstant + +class ValueImplicitEnumerationConstantNotUniqueQuery extends NonUniqueEnumerationConstantSharedQuery +{ + ValueImplicitEnumerationConstantNotUniqueQuery() { + this = Declarations7Package::valueImplicitEnumerationConstantNotUniqueQuery() + } +} diff --git a/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql b/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql index f04721883b..6a2c123907 100644 --- a/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql +++ b/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql @@ -10,42 +10,62 @@ * correctness * maintainability * readability + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/advisory */ import cpp import codingstandards.c.misra +import codingstandards.cpp.types.Pointers import codingstandards.cpp.SideEffect +import codingstandards.cpp.alertreporting.HoldsForAllCopies -class PointerOrArrayType extends DerivedType { - PointerOrArrayType() { - this.stripTopLevelSpecifiers() instanceof PointerType or - this.stripTopLevelSpecifiers() instanceof ArrayType +class NonConstPointerVariableCandidate extends Variable { + NonConstPointerVariableCandidate() { + // Ignore parameters in functions without bodies + (this instanceof Parameter implies exists(this.(Parameter).getFunction().getBlock())) and + // Ignore variables in functions that use ASM commands + not exists(AsmStmt a | + a.getEnclosingFunction() = this.(LocalScopeVariable).getFunction() + or + // In a type declared locally + this.(Field).getDeclaringType+().getEnclosingFunction() = a.getEnclosingFunction() + ) and + exists(PointerOrArrayType type | + // include only pointers which point to a const-qualified type + this.getType() = type and + not type.isDeeplyConstBelow() + ) and + // exclude pointers passed as arguments to functions which take a + // parameter that points to a non-const-qualified type + not exists(FunctionCall fc, int i | + fc.getArgument(i) = this.getAnAccess() and + not fc.getTarget().getParameter(i).getType().isDeeplyConstBelow() + ) and + // exclude any pointers which have their underlying data modified + not exists(VariableEffect effect | + effect.getTarget() = this and + // but not pointers that are only themselves modified + not effect.(AssignExpr).getLValue() = this.getAnAccess() and + not effect.(CrementOperation).getOperand() = this.getAnAccess() + ) and + // exclude pointers assigned to another pointer to a non-const-qualified type + not exists(Variable a | + a.getAnAssignedValue() = this.getAnAccess() and + not a.getType().(PointerOrArrayType).isDeeplyConstBelow() + ) } } -from Variable ptr, PointerOrArrayType type +/** + * Ensure that all copies of a variable are considered to be missing const qualification to avoid + * false positives where a variable is only used/modified in a single copy. + */ +class NonConstPointerVariable = + HoldsForAllCopies::LogicalResultElement; + +from NonConstPointerVariable ptr where - not isExcluded(ptr, Pointers1Package::pointerShouldPointToConstTypeWhenPossibleQuery()) and - // include only pointers which point to a const-qualified type - ptr.getType() = type and - not type.isDeeplyConstBelow() and - // exclude pointers passed as arguments to functions which take a - // parameter that points to a non-const-qualified type - not exists(FunctionCall fc, int i | - fc.getArgument(i) = ptr.getAnAccess() and - not fc.getTarget().getParameter(i).getType().isDeeplyConstBelow() - ) and - // exclude any pointers which have their underlying data modified - not exists(VariableEffect effect | - effect.getTarget() = ptr and - // but not pointers that are only themselves modified - not effect.(AssignExpr).getLValue() = effect.getAnAccess() and - not effect.(CrementOperation).getOperand() = effect.getAnAccess() - ) and - // exclude pointers assigned to another pointer to a non-const-qualified type - not exists(Variable a | - a.getAnAssignedValue() = ptr.getAnAccess() and - not a.getType().(PointerOrArrayType).isDeeplyConstBelow() - ) -select ptr, "$@ points to a non-const-qualified type.", ptr, ptr.getName() + not isExcluded(ptr.getAnElementInstance(), + Pointers1Package::pointerShouldPointToConstTypeWhenPossibleQuery()) +select ptr, "$@ points to a non-const-qualified type.", ptr, ptr.getAnElementInstance().getName() diff --git a/c/misra/src/rules/RULE-8-14/RestrictTypeQualifierUsed.ql b/c/misra/src/rules/RULE-8-14/RestrictTypeQualifierUsed.ql index 1969947753..cff7d0df5c 100644 --- a/c/misra/src/rules/RULE-8-14/RestrictTypeQualifierUsed.ql +++ b/c/misra/src/rules/RULE-8-14/RestrictTypeQualifierUsed.ql @@ -8,6 +8,7 @@ * @tags external/misra/id/rule-8-14 * correctness * security + * external/misra/c/2012/third-edition-first-revision * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.ql b/c/misra/src/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.ql new file mode 100644 index 0000000000..dc82f63d10 --- /dev/null +++ b/c/misra/src/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.ql @@ -0,0 +1,36 @@ +/** + * @id c/misra/redeclaration-of-object-with-unmatched-alignment + * @name RULE-8-15: Alignment should match between all declarations of an object + * @description All declarations of an object with an explicit alignment specification shall specify + * the same alignment. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-15 + * external/misra/c/2012/amendment3 + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import semmle.code.cpp.valuenumbering.HashCons + +predicate lexicallyEqual(AttributeArgument a, AttributeArgument b) { + hashCons(a.getValueConstant()) = hashCons(b.getValueConstant()) or + a.getValueType() = b.getValueType() +} + +from Attribute alignment, Attribute mismatched, string variable +where + not isExcluded(alignment, AlignmentPackage::redeclarationOfObjectWithUnmatchedAlignmentQuery()) and + alignment.hasName("_Alignas") and + mismatched.hasName("_Alignas") and + exists(Variable v | + v.getAnAttribute() = alignment and v.getAnAttribute() = mismatched and v.getName() = variable + ) and + not lexicallyEqual(alignment.getArgument(0), mismatched.getArgument(0)) +select alignment, + "Variable " + variable + " declared with lexically different _Alignof() values '$@' and '$@'.", + alignment, alignment.getArgument(0).toString(), mismatched, mismatched.getArgument(0).toString() diff --git a/c/misra/src/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.ql b/c/misra/src/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.ql new file mode 100644 index 0000000000..df9f3f2d1c --- /dev/null +++ b/c/misra/src/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.ql @@ -0,0 +1,96 @@ +/** + * @id c/misra/redeclaration-of-object-without-alignment + * @name RULE-8-15: Alignment should match between all declarations of an object + * @description An object declared with an explicit alignment shall be explicitly aligned in all + * declarations. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-15 + * external/misra/c/2012/amendment3 + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +/** + * Performance optimization; start query by joining attributes to declarations + * rather than locations. + * + * Including the entry location also speeds up search. + */ +newtype TAttributeDeclLocation = + TAttributeDeclLocationInfo(Attribute attribute, DeclarationEntry entry, Location entryLocation) { + entry.getDeclaration().(Variable).getAnAttribute() = attribute and + entryLocation = entry.getLocation() + } + +/** + * Get a DeclarationEntry along with its explicitly declared Attributes. + * + * DeclarationEntry does not have a method for getting Attributes by default, + * because an attribute declared on any DeclarationEntry affects all others, + * and attributes really belong to the declared variable rather than the + * declaration itself. + * + * In order to support this rule, we find for each attribute + * - A declaration entry which + * - corresponds to a variable associated with this attribute + * - is in the same file as this attribute + * - has identifier location after the attribute declaration + * - has no other declaration entry between this one and the attribute. + * + * This should give us a highly reliable means of finding which attributes are + * associated with which `DeclarationEntry`s. + * + * One note of caution: the location of the associated `Variable` must be + * treated with caution, as calls to `getLocation()` on a redeclared `Variable` + * can return multiple results. This class must act on `DeclarationEntry`s to + * deliver reliable results. + */ +class DeclarationEntryAttribute extends Attribute { + DeclarationEntry declarationEntry; + Location location; + Location declLocation; + File file; + TAttributeDeclLocation locInfo; + + DeclarationEntryAttribute() { + locInfo = TAttributeDeclLocationInfo(this, declarationEntry, declLocation) and + file = getFile() and + location = getLocation() and + declLocation = declarationEntry.getLocation() and + declarationEntry.getDeclaration().(Variable).getAnAttribute() = this and + declarationEntry.getFile() = file and + location.isBefore(declLocation, _) and + not exists(TAttributeDeclLocation blocInfo, DeclarationEntry betterFit, Location blocation | + blocInfo = TAttributeDeclLocationInfo(this, betterFit, blocation) and + not betterFit = declarationEntry and + blocation = betterFit.getLocation() and + betterFit.getFile() = file and + betterFit.getDeclaration() = declarationEntry.getDeclaration() and + blocation.isBefore(declLocation, _) and + location.isBefore(blocation, _) + ) + } + + DeclarationEntry getDeclarationEntry() { result = declarationEntry } +} + +from DeclarationEntry unaligned, DeclarationEntry aligned, DeclarationEntryAttribute attribute +where + not isExcluded(unaligned, AlignmentPackage::redeclarationOfObjectWithoutAlignmentQuery()) and + attribute.hasName("_Alignas") and + attribute.getDeclarationEntry() = aligned and + aligned.getDeclaration() = unaligned.getDeclaration() and + not exists(DeclarationEntryAttribute matchingAlignment | + matchingAlignment.hasName("_Alignas") and + matchingAlignment.getDeclarationEntry() = unaligned + ) +select unaligned, + "Variable " + unaligned.getName() + + " declared without explicit alignment to match $@ with alignment $@.", aligned, + "other definition", attribute, attribute.toString() diff --git a/c/misra/src/rules/RULE-8-16/AlignmentWithSizeZero.ql b/c/misra/src/rules/RULE-8-16/AlignmentWithSizeZero.ql new file mode 100644 index 0000000000..4a0cd9d50b --- /dev/null +++ b/c/misra/src/rules/RULE-8-16/AlignmentWithSizeZero.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/alignment-with-size-zero + * @name RULE-8-16: The alignment specification of zero should not appear in an object declaration + * @description A declaration shall not have an alignment of size zero. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-16 + * external/misra/c/2012/amendment3 + * readability + * maintainability + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from Attribute a, Variable v +where + not isExcluded(a, AlignmentPackage::alignmentWithSizeZeroQuery()) and + a.hasName("_Alignas") and + a.getArgument(0).getValueInt() = 0 and + v.getAnAttribute() = a +select a.getArgument(0), "Invalid alignof() size set to zero for variable $@.", v, v.getName() diff --git a/c/misra/src/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.ql b/c/misra/src/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.ql new file mode 100644 index 0000000000..f4e0d93d92 --- /dev/null +++ b/c/misra/src/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.ql @@ -0,0 +1,38 @@ +/** + * @id c/misra/more-than-one-alignment-specifier-on-declaration + * @name RULE-8-17: At most one explicit alignment specifier should appear in an object declaration + * @description While C permits the usage of multiple alignment specifiers, doing so reduces + * readability and may obscure the intent of the declaration. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-17 + * external/misra/c/2012/amendment3 + * readability + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from Variable v, Attribute first, Attribute last +where + not isExcluded(v, AlignmentPackage::moreThanOneAlignmentSpecifierOnDeclarationQuery()) and + first = v.getAnAttribute() and + last = v.getAnAttribute() and + not first = last and + first.hasName("_Alignas") and + last.hasName("_Alignas") and + // Handle double reporting: the first Attribute should really be first, and the last Attribute + // should really be last. This implies the first is before the last. This approach also ensures + // a single result for variables that have more than two alignment specifiers. + not exists(Attribute beforeFirst | + beforeFirst.getLocation().isBefore(first.getLocation(), _) and + v.getAnAttribute() = beforeFirst + ) and + not exists(Attribute afterLast | + last.getLocation().isBefore(afterLast.getLocation(), _) and + v.getAnAttribute() = afterLast + ) +select v, "Variable " + v.getName() + " contains more than one alignment specifier, $@ and $@", + first, first.toString(), last, last.toString() diff --git a/c/misra/src/rules/RULE-8-2/FunctionTypesNotInPrototypeForm.ql b/c/misra/src/rules/RULE-8-2/FunctionTypesNotInPrototypeForm.ql new file mode 100644 index 0000000000..1136dd714e --- /dev/null +++ b/c/misra/src/rules/RULE-8-2/FunctionTypesNotInPrototypeForm.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/function-types-not-in-prototype-form + * @name RULE-8-2: Function types shall be in prototype form with named parameters + * @description Omission of parameter types or names prevents the compiler from doing type checking + * when those functions are used and therefore may result in undefined behaviour. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/misra/id/rule-8-2 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.functiontypesnotinprototypeformshared.FunctionTypesNotInPrototypeFormShared + +class FunctionTypesNotInPrototypeFormQuery extends FunctionTypesNotInPrototypeFormSharedSharedQuery { + FunctionTypesNotInPrototypeFormQuery() { + this = Declarations4Package::functionTypesNotInPrototypeFormQuery() + } +} diff --git a/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql b/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql new file mode 100644 index 0000000000..fe0ae81ab1 --- /dev/null +++ b/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql @@ -0,0 +1,50 @@ +/** + * @id c/misra/declarations-of-a-function-same-name-and-type + * @name RULE-8-3: All declarations of a function shall use the same names and type qualifiers + * @description Using different types across the same declarations disallows strong type checking + * and can lead to undefined behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-3 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.Compatible + +predicate interestedInFunctions(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + not f1 = f2 and + f1.getDeclaration() = f2.getDeclaration() +} + +from FunctionDeclarationEntry f1, FunctionDeclarationEntry f2, string case, string pluralDo +where + not isExcluded(f1, Declarations4Package::declarationsOfAFunctionSameNameAndTypeQuery()) and + not isExcluded(f2, Declarations4Package::declarationsOfAFunctionSameNameAndTypeQuery()) and + not f1 = f2 and + f1.getDeclaration() = f2.getDeclaration() and + //return type check + ( + not FunctionDeclarationTypeEquivalence::equalReturnTypes(f1, + f2) and + case = "return type" and + pluralDo = "does" + or + //parameter type check + not FunctionDeclarationTypeEquivalence::equalParameterTypes(f1, + f2) and + case = "parameter types" and + pluralDo = "do" + or + //parameter name check + parameterNamesUnmatched(f1, f2) and + case = "parameter names" and + pluralDo = "do" + ) +select f1, + "The " + case + " of re-declaration of $@ " + pluralDo + + " not use the same type names as declaration $@", f1, f1.getName(), f2, f2.getName() diff --git a/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql b/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql new file mode 100644 index 0000000000..36a84b3b9c --- /dev/null +++ b/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql @@ -0,0 +1,55 @@ +/** + * @id c/misra/declarations-of-an-object-same-name-and-type + * @name RULE-8-3: All declarations of an object shall use the same names and type qualifiers + * @description Using different types across the same declarations disallows strong type checking + * and can lead to undefined behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-3 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.Compatible + +predicate relevantPair(VariableDeclarationEntry decl1, VariableDeclarationEntry decl2) { + not decl1 = decl2 and + not decl1.getVariable().getDeclaringType().isAnonymous() and + // Declarations are for the same qualified name + // Note: decl1.getVariable() = decl2.getVariable() does not work for common cases where an aliased + // type is used. + decl1.getVariable().getQualifiedName() = decl2.getVariable().getQualifiedName() and + // As we use qualified name, require that they share a common link target to ensure they are + // for the same object + ( + decl1.getVariable().(GlobalVariable).getALinkTarget() = + decl2.getVariable().(GlobalVariable).getALinkTarget() + or + decl1.getVariable().(Field).getDeclaringType().(Class).getALinkTarget() = + decl2.getVariable().(Field).getDeclaringType().(Class).getALinkTarget() + ) +} + +predicate relevantTypes(Type a, Type b) { + exists(VariableDeclarationEntry varA, VariableDeclarationEntry varB | + a = varA.getType() and + b = varB.getType() and + relevantPair(varA, varB) + ) +} + +from VariableDeclarationEntry decl1, VariableDeclarationEntry decl2 +where + not isExcluded(decl1, Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery()) and + not isExcluded(decl2, Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery()) and + relevantPair(decl1, decl2) and + not TypeEquivalence::equalTypes(decl1.getType(), + decl2.getType()) +select decl1, + "The object $@ of type " + decl1.getType().toString() + + " does not use the same type names as re-declaration $@ of type " + decl2.getType().toString(), + decl1, decl1.getName(), decl2, decl2.getName() diff --git a/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql new file mode 100644 index 0000000000..e7eba7e42a --- /dev/null +++ b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql @@ -0,0 +1,61 @@ +/** + * @id c/misra/compatible-declaration-function-defined + * @name RULE-8-4: A compatible declaration shall be visible when a function with external linkage is defined + * @description A compatible declaration shall be visible when a function with external linkage is + * defined, otherwise program behaviour may be undefined. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-4 + * readability + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers +import codingstandards.cpp.types.Compatible + +predicate interestedInFunctions(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + f1.getDeclaration() instanceof ExternalIdentifiers and + f1.isDefinition() and + f1.getDeclaration() = f2.getDeclaration() and + not f2.isDefinition() and + not f1.isFromTemplateInstantiation(_) and + not f2.isFromTemplateInstantiation(_) +} + +module FunDeclEquiv = + FunctionDeclarationTypeEquivalence; + +from FunctionDeclarationEntry f1 +where + not isExcluded(f1, Declarations4Package::compatibleDeclarationFunctionDefinedQuery()) and + f1.isDefinition() and + f1.getDeclaration() instanceof ExternalIdentifiers and + //no declaration matches exactly + ( + not exists(FunctionDeclarationEntry f2 | + not f2.isDefinition() and + f2.getDeclaration() = f1.getDeclaration() + ) + or + //or one exists that is close but incompatible in some way + exists(FunctionDeclarationEntry f2 | + interestedInFunctions(f1, f2) and + ( + //return types differ + not FunDeclEquiv::equalReturnTypes(f1, f2) + or + //parameter types differ + not FunDeclEquiv::equalParameterTypes(f1, f2) + or + //parameter names differ + parameterNamesUnmatched(f1, f2) + ) + ) + ) +select f1, "No separate compatible declaration found for this definition." diff --git a/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql new file mode 100644 index 0000000000..bed30d673c --- /dev/null +++ b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql @@ -0,0 +1,43 @@ +/** + * @id c/misra/compatible-declaration-object-defined + * @name RULE-8-4: A compatible declaration shall be visible when an object with external linkage is defined + * @description A compatible declaration shall be visible when an object with external linkage is + * defined, otherwise program behaviour may be undefined. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-4 + * readability + * maintainability + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers +import codingstandards.cpp.types.Compatible + +predicate relevantTypes(Type a, Type b) { + exists(VariableDeclarationEntry varA, VariableDeclarationEntry varB | + not varA = varB and + varA.getDeclaration() = varB.getDeclaration() and + a = varA.getType() and + b = varB.getType() + ) +} + +from VariableDeclarationEntry decl1 +where + not isExcluded(decl1, Declarations4Package::compatibleDeclarationObjectDefinedQuery()) and + decl1.isDefinition() and + decl1.getDeclaration() instanceof ExternalIdentifiers and + // no declaration matches + not exists(VariableDeclarationEntry decl2 | + not decl2.isDefinition() and + decl1.getDeclaration() = decl2.getDeclaration() and + TypeEquivalence::equalTypes(decl1.getType(), + decl2.getType()) + ) +select decl1, "No separate compatible declaration found for this definition." diff --git a/c/misra/src/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.ql b/c/misra/src/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.ql new file mode 100644 index 0000000000..9a3f1c7900 --- /dev/null +++ b/c/misra/src/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.ql @@ -0,0 +1,39 @@ +/** + * @id c/misra/external-object-or-function-not-declared-in-one-file + * @name RULE-8-5: An external object or function shall be declared once in one and only one file + * @description Declarations in multiple files can lead to unexpected program behaviour. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-8-5 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from DeclarationEntry de, DeclarationEntry otherDeclaration, string kind +where + not isExcluded(de, Declarations5Package::externalObjectOrFunctionNotDeclaredInOneFileQuery()) and + //this rule applies to non-defining declarations only + not de.isDefinition() and + not otherDeclaration.isDefinition() and + exists(Declaration d | + de.getDeclaration() = d and + otherDeclaration.getDeclaration() = d and + de.getFile() != otherDeclaration.getFile() + ) and + ( + de.getDeclaration() instanceof Function and kind = "function" + or + de.getDeclaration() instanceof Variable and + not de.getDeclaration() instanceof Parameter and + kind = "variable" + ) and + // Apply an ordering based on location to enforce that (de1, de2) = (de2, de1) and we only report (de1, de2). + de.getFile().getAbsolutePath() < otherDeclaration.getFile().getAbsolutePath() +select de, + "The " + kind + " declaration " + de.getName() + + " is declared in multiple files and has an additional $@.", otherDeclaration, "declaration" diff --git a/c/misra/src/rules/RULE-8-6/IdentifierWithExternalLinkageOneDefinition.ql b/c/misra/src/rules/RULE-8-6/IdentifierWithExternalLinkageOneDefinition.ql new file mode 100644 index 0000000000..0781eef539 --- /dev/null +++ b/c/misra/src/rules/RULE-8-6/IdentifierWithExternalLinkageOneDefinition.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/identifier-with-external-linkage-one-definition + * @name RULE-8-6: An identifier with external linkage shall have exactly one definition + * @description An identifier with multiple definitions in different translation units leads to + * undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-8-6 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.identifierwithexternallinkageonedefinitionshared.IdentifierWithExternalLinkageOneDefinitionShared + +class IdentifierWithExternalLinkageShallHaveOneDefinitionQuery extends IdentifierWithExternalLinkageOneDefinitionSharedSharedQuery +{ + IdentifierWithExternalLinkageShallHaveOneDefinitionQuery() { + this = Declarations4Package::identifierWithExternalLinkageOneDefinitionQuery() + } +} diff --git a/c/misra/src/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.ql b/c/misra/src/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.ql new file mode 100644 index 0000000000..9cdd6532a9 --- /dev/null +++ b/c/misra/src/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.ql @@ -0,0 +1,61 @@ +/** + * @id c/misra/should-not-be-defined-with-external-linkage + * @name RULE-8-7: Functions and objects should not be defined with external linkage if they are referenced in only one + * @description Declarations with external linkage that are referenced in only one translation unit + * can indicate an intention to only have those identifiers accessible in that + * translation unit and accidental future accesses in other translation units can lead + * to confusing program behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-7 + * correctness + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Identifiers +import codingstandards.cpp.Scope + +ExternalIdentifiers getExternalIdentifierTarget(NameQualifiableElement nqe) { + result = nqe.(Access).getTarget() + or + result = nqe.(FunctionCall).getTarget() +} + +/** + * A reference to an external identifier, either as an `Access` or a `FunctionCall`. + */ +class ExternalIdentifierReference extends NameQualifiableElement { + ExternalIdentifierReference() { exists(getExternalIdentifierTarget(this)) } + + ExternalIdentifiers getExternalIdentifierTarget() { result = getExternalIdentifierTarget(this) } +} + +predicate isReferencedInTranslationUnit( + ExternalIdentifiers e, ExternalIdentifierReference r, TranslationUnit t +) { + r.getExternalIdentifierTarget() = e and + // Used within the translation unit or an included header + r.getFile() = t.getAUserFile() +} + +from ExternalIdentifiers e, ExternalIdentifierReference a1, TranslationUnit t1 +where + not isExcluded(e, Declarations6Package::shouldNotBeDefinedWithExternalLinkageQuery()) and + // Only report external identifiers where we see the definition + e.hasDefinition() and + isReferencedInTranslationUnit(e, a1, t1) and + // Not referenced in any other translation unit + not exists(TranslationUnit t2 | + isReferencedInTranslationUnit(e, _, t2) and + not t1 = t2 + ) and + // Definition is also in the same translation unit + e.getDefinition().getFile() = t1.getAUserFile() +select e, "Declaration with external linkage is accessed in only one translation unit $@.", a1, + a1.toString() diff --git a/c/misra/src/rules/RULE-8-8/MissingStaticSpecifierFunctionRedeclarationC.ql b/c/misra/src/rules/RULE-8-8/MissingStaticSpecifierFunctionRedeclarationC.ql new file mode 100644 index 0000000000..c3a5ce897f --- /dev/null +++ b/c/misra/src/rules/RULE-8-8/MissingStaticSpecifierFunctionRedeclarationC.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/missing-static-specifier-function-redeclaration-c + * @name RULE-8-8: If a function has internal linkage then all re-declarations shall include the static storage class + * @description If a function has internal linkage then all re-declarations shall include the static + * storage class specifier to make the internal linkage explicit. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-8-8 + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.missingstaticspecifierfunctionredeclarationshared.MissingStaticSpecifierFunctionRedeclarationShared + +class MissingStaticSpecifierFunctionRedeclarationCQuery extends MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery +{ + MissingStaticSpecifierFunctionRedeclarationCQuery() { + this = Declarations5Package::missingStaticSpecifierFunctionRedeclarationCQuery() + } +} diff --git a/c/misra/src/rules/RULE-8-8/MissingStaticSpecifierObjectRedeclarationC.ql b/c/misra/src/rules/RULE-8-8/MissingStaticSpecifierObjectRedeclarationC.ql new file mode 100644 index 0000000000..877ef19d2a --- /dev/null +++ b/c/misra/src/rules/RULE-8-8/MissingStaticSpecifierObjectRedeclarationC.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/missing-static-specifier-object-redeclaration-c + * @name RULE-8-8: If an object has internal linkage then all re-declarations shall include the static storage class + * @description If an object has internal linkage then all re-declarations shall include the static + * storage class specifier to make the internal linkage explicit. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-8-8 + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.missingstaticspecifierobjectredeclarationshared.MissingStaticSpecifierObjectRedeclarationShared + +class MissingStaticSpecifierObjectRedeclarationCQuery extends MissingStaticSpecifierObjectRedeclarationSharedSharedQuery +{ + MissingStaticSpecifierObjectRedeclarationCQuery() { + this = Declarations5Package::missingStaticSpecifierObjectRedeclarationCQuery() + } +} diff --git a/c/misra/src/rules/RULE-8-9/UnnecessaryExposedIdentifierDeclarationC.ql b/c/misra/src/rules/RULE-8-9/UnnecessaryExposedIdentifierDeclarationC.ql new file mode 100644 index 0000000000..5dc697e425 --- /dev/null +++ b/c/misra/src/rules/RULE-8-9/UnnecessaryExposedIdentifierDeclarationC.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/unnecessary-exposed-identifier-declaration-c + * @name RULE-8-9: An object should be defined at block scope if its identifier only appears in a single function + * @description An identifier declared to be an object or type shall be defined in a block that + * minimizes its visibility to prevent any accidental use of the identifier. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-8-9 + * correctness + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.unnecessaryexposedidentifierdeclarationshared.UnnecessaryExposedIdentifierDeclarationShared + +class UnnecessaryExposedIdentifierDeclarationCQuery extends UnnecessaryExposedIdentifierDeclarationSharedSharedQuery +{ + UnnecessaryExposedIdentifierDeclarationCQuery() { + this = Declarations5Package::unnecessaryExposedIdentifierDeclarationCQuery() + } +} diff --git a/c/misra/src/rules/RULE-9-1/ObjectWithAutoStorageDurationReadBeforeInit.ql b/c/misra/src/rules/RULE-9-1/ObjectWithAutoStorageDurationReadBeforeInit.ql new file mode 100644 index 0000000000..f3204ef2e3 --- /dev/null +++ b/c/misra/src/rules/RULE-9-1/ObjectWithAutoStorageDurationReadBeforeInit.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/object-with-auto-storage-duration-read-before-init + * @name RULE-9-1: The value of an object with automatic storage duration shall not be read before it has been set + * @description Accessing an object before it has been initialized can lead to undefined behavior. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/misra/id/rule-9-1 + * correctness + * security + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.readofuninitializedmemory.ReadOfUninitializedMemory + +class ObjectWithAutoStorageDurationReadBeforeInitQuery extends ReadOfUninitializedMemorySharedQuery { + ObjectWithAutoStorageDurationReadBeforeInitQuery() { + this = InvalidMemory1Package::objectWithAutoStorageDurationReadBeforeInitQuery() + } +} diff --git a/c/misra/src/rules/RULE-9-2/InitializerForAggregateOrUnionNotEnclosedInBraces.ql b/c/misra/src/rules/RULE-9-2/InitializerForAggregateOrUnionNotEnclosedInBraces.ql new file mode 100644 index 0000000000..c5a9ae4814 --- /dev/null +++ b/c/misra/src/rules/RULE-9-2/InitializerForAggregateOrUnionNotEnclosedInBraces.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/initializer-for-aggregate-or-union-not-enclosed-in-braces + * @name RULE-9-2: The initializer for an aggregate or union shall be enclosed in braces + * @description Using braces in initializers of objects and subobjects improves code readability and + * clarifies intent. + * @kind problem + * @precision high + * @problem.severity recommendation + * @tags external/misra/id/rule-9-2 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.useinitializerbracestomatchaggregatetypestructure.UseInitializerBracesToMatchAggregateTypeStructure + +class InitializerForAggregateOrUnionNotEnclosedInBracesQuery extends UseInitializerBracesToMatchAggregateTypeStructureSharedQuery +{ + InitializerForAggregateOrUnionNotEnclosedInBracesQuery() { + this = Memory1Package::initializerForAggregateOrUnionNotEnclosedInBracesQuery() + } +} diff --git a/c/misra/src/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.ql b/c/misra/src/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.ql new file mode 100644 index 0000000000..d10c8315e1 --- /dev/null +++ b/c/misra/src/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.ql @@ -0,0 +1,77 @@ +/** + * @id c/misra/partially-initialized-array-with-explicit-initializers + * @name RULE-9-3: Arrays shall not be partially initialized + * @description An array object or a subobject of an array shall be explicitly initialized if any + * other object in that array is explicitly initialized. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-9-3 + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.enhancements.AggregateLiteralEnhancements + +/** + * Holds if the aggregate literal has at least one explicit initializer, and at least one + * _missing_ explicit initializer, and not _only_ designated initializers. + */ +predicate isMissingExplicitInitializers(AggregateLiteral al) { + not al.isCompilerGenerated() and + not al.isAffectedByMacro() and + // Partially initialized, but not initialized with a leading zero (which is permitted) + isPartiallyValueInitialized(al) and + not isLeadingZeroInitialized(al) +} + +// note: this query is similar to M8-5-2: MissingExplicitInitializers.ql +// but, pursuant to Rule 9.3, only covers array initializers rather than all aggregates +from AggregateLiteral al, Type aggType, Element explanationElement, string explanationDescription +where + not isExcluded(al, Memory1Package::partiallyInitializedArrayWithExplicitInitializersQuery()) and + // The aggregate literal is missing at least one explicit initializer + isMissingExplicitInitializers(al) and + // Missing array initializer + exists(int arraySize, int minIndex, int maxIndex | + // Identify the size of the array with a missing initializer + arraySize = al.getType().getUnspecifiedType().(ArrayType).getArraySize() and + // Identify the smallest index missing an initialzer + minIndex = + min(int index | + index = [0 .. arraySize - 1] and ArrayAggregateLiterals::isValueInitialized(al, index) + | + index + ) and + // Identify the largest index missing an initialzer + maxIndex = + max(int index | + index = [0 .. arraySize - 1] and ArrayAggregateLiterals::isValueInitialized(al, index) + | + index + ) and + // Ensure that the maxIndex is the last array entry. If it's not, something is up with this + // database, and so we shouldn't report it (because you can only initialize trailing array + // values) + maxIndex = (arraySize - 1) and + // Nothing useful to point to as the explanation element, so let's just set it to the parent + // array + explanationElement = al and + ( + if minIndex = maxIndex + then + // Only one element missing + explanationDescription = "the element at index " + minIndex + else + // Multiple elements missing + explanationDescription = "the elements in the index range " + minIndex + " to " + maxIndex + ) + ) +select al, + "Aggregate literal for " + getAggregateTypeDescription(al, aggType) + + " is missing an explicit initializer for $@.", aggType, aggType.getName(), explanationElement, + explanationDescription diff --git a/c/misra/src/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.ql b/c/misra/src/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.ql new file mode 100644 index 0000000000..dfe3fd8fff --- /dev/null +++ b/c/misra/src/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.ql @@ -0,0 +1,130 @@ +/** + * @id c/misra/repeated-initialization-of-aggregate-object-element + * @name RULE-9-4: An element of an object shall not be initialized more than once + * @description Repeated initialization of an element in an object can lead to side-effects or may + * signal a logic error. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-9-4 + * correctness + * maintainability + * readability + * external/misra/c/2012/third-edition-first-revision + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.enhancements.AggregateLiteralEnhancements + +/** + * Gets the `n`th parent of `e`. + * If `n` is zero, the result is `e`. + */ +Expr getNthParent(Expr e, int n) { + if n = 0 then result = e else result = getNthParent(e.getParent(), n - 1) +} + +/** + * Returns a string representation of the index of `e` relative + * to the nested array aggregate literal structure it is contained in. + */ +string getNestedArrayIndexString(Expr e) { + result = + concat(int depth | + depth = [0 .. getMaxDepth(getRootAggregate(e.getParent()))] + | + "[" + + any(int elementIndex | + exists(ArrayAggregateLiteral parent | + parent = getNthParent(e, pragma[only_bind_into](depth + 1)) and + parent.getAnElementExpr(elementIndex) = getNthParent(e, pragma[only_bind_into](depth)) + ) + | + elementIndex + ).toString() + "]" + order by + depth desc + ) +} + +/** + * Returns the number of levels of nested `ArrayAggregateLiteral`s in `al`. + * If there are no nested array aggregate literals, the max depth of the `ArrayAggregateLiteral` is `0`. + */ +language[monotonicAggregates] +int getMaxDepth(ArrayAggregateLiteral al) { + if not exists(al.getAnElementExpr(_).(ArrayAggregateLiteral)) + then result = 0 + else result = 1 + max(Expr child | child = al.getAnElementExpr(_) | getMaxDepth(child)) +} + +// internal recursive predicate for `hasMultipleInitializerExprsForSameIndex` +predicate hasMultipleInitializerExprsForSameIndexInternal( + ArrayAggregateLiteral root, Expr e1, Expr e2 +) { + root = e1 and root = e2 + or + exists(ArrayAggregateLiteral parent1, ArrayAggregateLiteral parent2, int shared_index | + hasMultipleInitializerExprsForSameIndexInternal(root, parent1, parent2) and + shared_index = [0 .. parent1.getArraySize() - 1] and + e1 = parent1.getAnElementExpr(shared_index) and + e2 = parent2.getAnElementExpr(shared_index) + ) +} + +/** + * Holds if `expr1` and `expr2` both initialize the same array element of `root`. + */ +predicate hasMultipleInitializerExprsForSameIndex(ArrayAggregateLiteral root, Expr expr1, Expr expr2) { + hasMultipleInitializerExprsForSameIndexInternal(root, expr1, expr2) and + not root = expr1 and + not root = expr2 and + not expr1 = expr2 and + ( + not expr1 instanceof ArrayAggregateLiteral + or + not expr2 instanceof ArrayAggregateLiteral + ) +} + +/** + * Holds if `expr1` and `expr2` both initialize the same field of `root`. + * + * The dbschema keyset for `aggregate_field_init` prevents referencing multiple `Expr` + * that initialize the same Field and are part of the same `ClassAggregateLiteral`. + * This predicate is therefore unable to distinguish the individual duplicate expressions. + */ +predicate hasMultipleInitializerExprsForSameField(ClassAggregateLiteral root, Field f) { + count(root.getAFieldExpr(f)) > 1 +} + +from + AggregateLiteral root, Expr e1, Expr e2, string elementDescription, string rootType, + string clarification +where + not isExcluded(e1, Memory1Package::repeatedInitializationOfAggregateObjectElementQuery()) and + exists(Initializer init | init.getExpr() = root) and + ( + hasMultipleInitializerExprsForSameIndex(root, e1, e2) and + elementDescription = getNestedArrayIndexString(e1) and + rootType = "Array aggregate literal" and + clarification = ", which is already initialized $@." + or + exists(Field f | + // we cannot distinguish between different aggregate field init expressions. + // therefore, we only report the root aggregate rather than any child init expr. + // see `hasMultipleInitializerExprsForSameField` documentation. + hasMultipleInitializerExprsForSameField(root, f) and + e1 = root and + e2 = root and + elementDescription = f.getQualifiedName() and + rootType = "Structure aggregate literal" and + clarification = "." + ) + ) and + // de-duplicate the results by excluding permutations of `e1` and `e2` + e1.getLocation().toString() <= e2.getLocation().toString() +select e1, "$@ repeats initialization of element " + elementDescription + clarification, root, + rootType, e2, "here" diff --git a/c/misra/src/rules/RULE-9-7/UninitializedAtomicObject.ql b/c/misra/src/rules/RULE-9-7/UninitializedAtomicObject.ql new file mode 100644 index 0000000000..5f7fb803d6 --- /dev/null +++ b/c/misra/src/rules/RULE-9-7/UninitializedAtomicObject.ql @@ -0,0 +1,76 @@ +/** + * @id c/misra/uninitialized-atomic-object + * @name RULE-9-7: Atomic objects shall be appropriately initialized before being accessed + * @description Atomic objects that do not have static storage duration shall be initialized with a + * value or by using 'atomic_init()'. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-9-7 + * concurrency + * external/misra/c/2012/amendment4 + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.StdFunctionOrMacro +import semmle.code.cpp.controlflow.Dominance + +class ThreadSpawningFunction extends Function { + ThreadSpawningFunction() { + this.hasName("pthread_create") + or + this.hasName("thrd_create") + or + exists(FunctionCall fc | + fc.getTarget() instanceof ThreadSpawningFunction and + fc.getEnclosingFunction() = this + ) + } +} + +class AtomicInitAddressOfExpr extends AddressOfExpr { + AtomicInitAddressOfExpr() { + // StdFunctionOrMacro arguments are not necessarily reliable, so we look for any AddressOfExpr + // that is an argument to a call to `atomic_init`. + exists(AtomicInitCall c | this = c.getAnArgument()) + } +} + +ControlFlowNode getARequiredInitializationPoint(LocalScopeVariable v) { + result = v.getParentScope().(BlockStmt).getFollowingStmt() + or + exists(DeclStmt decl | + decl.getADeclaration() = v and + result = + any(FunctionCall fc | + fc.getTarget() instanceof ThreadSpawningFunction and + fc.getEnclosingBlock().getEnclosingBlock*() = v.getParentScope() and + fc.getAPredecessor*() = decl + ) + ) +} + +from VariableDeclarationEntry decl, Variable v +where + not isExcluded(decl, Concurrency7Package::uninitializedAtomicObjectQuery()) and + v = decl.getVariable() and + v.getUnderlyingType().hasSpecifier("atomic") and + not v.isTopLevel() and + not exists(v.getInitializer()) and + exists(ControlFlowNode missingInitPoint | + missingInitPoint = getARequiredInitializationPoint(v) and + // Check for `atomic_init(&v)` + not exists(AtomicInitAddressOfExpr initialization | + initialization.getOperand().(VariableAccess).getTarget() = v and + dominates(initialization, missingInitPoint) + ) and + // Check for `unknown_func(&v)` which may call `atomic_init` on `v`. + not exists(FunctionCall fc | + fc.getAnArgument().(AddressOfExpr).getOperand().(VariableAccess).getTarget() = v and + dominates(fc, missingInitPoint) + ) + ) +select decl, + "Atomic object '" + v.getName() + "' has no initializer or corresponding use of 'atomic_init()'." diff --git a/c/misra/test/c/misra/EssentialTypes.expected b/c/misra/test/c/misra/EssentialTypes.expected new file mode 100644 index 0000000000..95976fe2ab --- /dev/null +++ b/c/misra/test/c/misra/EssentialTypes.expected @@ -0,0 +1,440 @@ +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| test.c:4:20:4:20 | 1 | signed char | signed char | essentially Signed type | +| test.c:4:20:4:20 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:5:23:5:23 | 1 | signed char | signed char | essentially Signed type | +| test.c:5:23:5:23 | (unsigned short)... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:6:17:6:18 | 1 | signed char | signed char | essentially Signed type | +| test.c:7:21:7:21 | 1 | signed char | signed char | essentially Signed type | +| test.c:7:21:7:21 | (signed short)... | signed short | signed short | essentially Signed type | +| test.c:8:13:8:16 | 1 | bool | bool | essentially Boolean type | +| test.c:8:13:8:16 | (bool)... | bool | bool | essentially Boolean type | +| test.c:10:3:10:3 | b | bool | bool | essentially Boolean type | +| test.c:10:3:10:11 | ... ? ... : ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:10:7:10:7 | u | unsigned int | unsigned int | essentially Unsigned type | +| test.c:10:11:10:11 | u | unsigned int | unsigned int | essentially Unsigned type | +| test.c:11:3:11:3 | b | bool | bool | essentially Boolean type | +| test.c:11:3:11:11 | ... ? ... : ... | signed int | signed int | essentially Signed type | +| test.c:11:7:11:7 | s | signed int | signed int | essentially Signed type | +| test.c:11:11:11:11 | s | signed int | signed int | essentially Signed type | +| test.c:12:3:12:3 | b | bool | bool | essentially Boolean type | +| test.c:12:3:12:12 | ... ? ... : ... | signed int | signed int | essentially Signed type | +| test.c:12:7:12:7 | s | signed int | signed int | essentially Signed type | +| test.c:12:11:12:12 | (int)... | int | int | essentially Signed type | +| test.c:12:11:12:12 | ss | signed short | signed short | essentially Signed type | +| test.c:13:3:13:3 | b | bool | bool | essentially Boolean type | +| test.c:13:3:13:12 | ... ? ... : ... | signed int | signed int | essentially Signed type | +| test.c:13:7:13:8 | (int)... | int | int | essentially Signed type | +| test.c:13:7:13:8 | ss | signed short | signed short | essentially Signed type | +| test.c:13:12:13:12 | s | signed int | signed int | essentially Signed type | +| test.c:14:3:14:3 | b | bool | bool | essentially Boolean type | +| test.c:14:3:14:12 | ... ? ... : ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:14:7:14:8 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:14:7:14:8 | us | unsigned short | unsigned short | essentially Unsigned type | +| test.c:14:12:14:12 | u | unsigned int | unsigned int | essentially Unsigned type | +| test.c:16:3:16:3 | b | bool | bool | essentially Boolean type | +| test.c:16:3:16:11 | ... ? ... : ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:16:7:16:7 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:16:7:16:7 | s | signed int | signed int | essentially Signed type | +| test.c:16:11:16:11 | u | unsigned int | unsigned int | essentially Unsigned type | +| test.c:26:3:26:3 | f | float | float | essentially Floating type | +| test.c:27:3:27:5 | f32 | float32_t | float32_t | essentially Floating type | +| test.c:28:3:28:6 | cf32 | float | float | essentially Floating type | +| test.c:32:3:32:3 | 1 | signed char | signed char | essentially Signed type | +| test.c:33:3:33:4 | 1 | unsigned char | unsigned char | essentially Unsigned type | +| test.c:34:3:34:5 | 1 | unsigned long | unsigned long | essentially Unsigned type | +| test.c:38:13:38:16 | 1 | bool | bool | essentially Boolean type | +| test.c:38:13:38:16 | (bool)... | bool | bool | essentially Boolean type | +| test.c:39:20:39:20 | 1 | signed char | signed char | essentially Signed type | +| test.c:39:20:39:20 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:40:23:40:23 | 1 | signed char | signed char | essentially Signed type | +| test.c:40:23:40:23 | (unsigned short)... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:41:17:41:18 | 1 | signed char | signed char | essentially Signed type | +| test.c:42:21:42:21 | 1 | signed char | signed char | essentially Signed type | +| test.c:42:21:42:21 | (signed short)... | signed short | signed short | essentially Signed type | +| test.c:44:3:44:4 | ! ... | bool | bool | essentially Boolean type | +| test.c:44:4:44:4 | b | bool | bool | essentially Boolean type | +| test.c:45:3:45:4 | ! ... | bool | bool | essentially Boolean type | +| test.c:45:4:45:4 | u | unsigned int | unsigned int | essentially Unsigned type | +| test.c:46:3:46:5 | ! ... | bool | bool | essentially Boolean type | +| test.c:46:4:46:5 | us | unsigned short | unsigned short | essentially Unsigned type | +| test.c:47:3:47:4 | ! ... | bool | bool | essentially Boolean type | +| test.c:47:4:47:4 | s | signed int | signed int | essentially Signed type | +| test.c:48:3:48:5 | ! ... | bool | bool | essentially Boolean type | +| test.c:48:4:48:5 | ss | signed short | signed short | essentially Signed type | +| test.c:50:3:50:4 | ~ ... | int | int | essentially Signed type | +| test.c:50:4:50:4 | (int)... | int | int | essentially Signed type | +| test.c:50:4:50:4 | b | bool | bool | essentially Boolean type | +| test.c:51:3:51:4 | ~ ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:51:4:51:4 | u | unsigned int | unsigned int | essentially Unsigned type | +| test.c:52:3:52:5 | ~ ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:52:4:52:5 | (int)... | int | int | essentially Signed type | +| test.c:52:4:52:5 | us | unsigned short | unsigned short | essentially Unsigned type | +| test.c:53:3:53:4 | ~ ... | signed int | signed int | essentially Signed type | +| test.c:53:4:53:4 | s | signed int | signed int | essentially Signed type | +| test.c:54:3:54:5 | ~ ... | int | int | essentially Signed type | +| test.c:54:4:54:5 | (int)... | int | int | essentially Signed type | +| test.c:54:4:54:5 | ss | signed short | signed short | essentially Signed type | +| test.c:63:30:63:32 | ((unnamed enum))... | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:63:30:63:32 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:70:3:70:5 | EC1 | signed char | signed char | essentially Signed type | +| test.c:71:3:71:5 | EC2 | E1 | E1 | essentially Enum Type | +| test.c:72:3:72:5 | EC3 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:73:3:73:5 | EC4 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:74:3:74:5 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:75:3:75:5 | EC6 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:79:3:79:5 | 97 | char | char | essentially Character type | +| test.c:80:3:80:6 | 10 | char | char | essentially Character type | +| test.c:81:3:81:6 | 0 | char | char | essentially Character type | +| test.c:87:16:87:16 | 0 | signed char | signed char | essentially Signed type | +| test.c:87:16:87:16 | (uint8_t)... | uint8_t | uint8_t | essentially Unsigned type | +| test.c:88:18:88:18 | 0 | signed char | signed char | essentially Signed type | +| test.c:88:18:88:18 | (uint16_t)... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:89:18:89:18 | 0 | signed char | signed char | essentially Signed type | +| test.c:89:18:89:18 | (uint32_t)... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:90:15:90:15 | 0 | signed char | signed char | essentially Signed type | +| test.c:90:15:90:15 | (int8_t)... | int8_t | int8_t | essentially Signed type | +| test.c:91:17:91:17 | 0 | signed char | signed char | essentially Signed type | +| test.c:91:17:91:17 | (int16_t)... | int16_t | int16_t | essentially Signed type | +| test.c:92:16:92:17 | 0 | signed char | signed char | essentially Signed type | +| test.c:94:3:94:4 | (int)... | int | int | essentially Signed type | +| test.c:94:3:94:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:94:3:94:9 | ... & ... | uint8_t | uint8_t | essentially Unsigned type | +| test.c:94:8:94:9 | (int)... | int | int | essentially Signed type | +| test.c:94:8:94:9 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:95:3:95:5 | (int)... | int | int | essentially Signed type | +| test.c:95:3:95:5 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:95:3:95:10 | ... & ... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:95:9:95:10 | (int)... | int | int | essentially Signed type | +| test.c:95:9:95:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:96:3:96:4 | (int)... | int | int | essentially Signed type | +| test.c:96:3:96:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:96:3:96:10 | ... & ... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:96:8:96:10 | (int)... | int | int | essentially Signed type | +| test.c:96:8:96:10 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:97:3:97:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:97:3:97:10 | ... & ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:97:9:97:10 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:97:9:97:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:98:3:98:4 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:98:3:98:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:98:3:98:10 | ... & ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:98:8:98:10 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:99:3:99:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:99:3:99:11 | ... & ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:99:9:99:11 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:99:9:99:11 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:100:3:100:5 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:100:3:100:5 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:100:3:100:11 | ... & ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:100:9:100:11 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:102:3:102:4 | (int)... | int | int | essentially Signed type | +| test.c:102:3:102:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:102:3:102:9 | ... \| ... | uint8_t | uint8_t | essentially Unsigned type | +| test.c:102:8:102:9 | (int)... | int | int | essentially Signed type | +| test.c:102:8:102:9 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:103:3:103:5 | (int)... | int | int | essentially Signed type | +| test.c:103:3:103:5 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:103:3:103:10 | ... \| ... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:103:9:103:10 | (int)... | int | int | essentially Signed type | +| test.c:103:9:103:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:104:3:104:4 | (int)... | int | int | essentially Signed type | +| test.c:104:3:104:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:104:3:104:10 | ... \| ... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:104:8:104:10 | (int)... | int | int | essentially Signed type | +| test.c:104:8:104:10 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:105:3:105:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:105:3:105:10 | ... \| ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:105:9:105:10 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:105:9:105:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:106:3:106:4 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:106:3:106:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:106:3:106:10 | ... \| ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:106:8:106:10 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:107:3:107:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:107:3:107:11 | ... \| ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:107:9:107:11 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:107:9:107:11 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:108:3:108:5 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:108:3:108:5 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:108:3:108:11 | ... \| ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:108:9:108:11 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:110:3:110:4 | (int)... | int | int | essentially Signed type | +| test.c:110:3:110:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:110:3:110:9 | ... ^ ... | uint8_t | uint8_t | essentially Unsigned type | +| test.c:110:8:110:9 | (int)... | int | int | essentially Signed type | +| test.c:110:8:110:9 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:111:3:111:5 | (int)... | int | int | essentially Signed type | +| test.c:111:3:111:5 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:111:3:111:10 | ... ^ ... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:111:9:111:10 | (int)... | int | int | essentially Signed type | +| test.c:111:9:111:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:112:3:112:4 | (int)... | int | int | essentially Signed type | +| test.c:112:3:112:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:112:3:112:10 | ... ^ ... | uint16_t | uint16_t | essentially Unsigned type | +| test.c:112:8:112:10 | (int)... | int | int | essentially Signed type | +| test.c:112:8:112:10 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:113:3:113:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:113:3:113:10 | ... ^ ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:113:9:113:10 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:113:9:113:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:114:3:114:4 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:114:3:114:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:114:3:114:10 | ... ^ ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:114:8:114:10 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:115:3:115:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:115:3:115:11 | ... ^ ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:115:9:115:11 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:115:9:115:11 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:116:3:116:5 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:116:3:116:5 | u16 | uint16_t | uint16_t | essentially Unsigned type | +| test.c:116:3:116:11 | ... ^ ... | uint32_t | uint32_t | essentially Unsigned type | +| test.c:116:9:116:11 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:118:3:118:4 | (int)... | int | int | essentially Signed type | +| test.c:118:3:118:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:118:3:118:9 | ... & ... | int8_t | int8_t | essentially Signed type | +| test.c:118:8:118:9 | (int)... | int | int | essentially Signed type | +| test.c:118:8:118:9 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:119:3:119:5 | (int)... | int | int | essentially Signed type | +| test.c:119:3:119:5 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:119:3:119:10 | ... & ... | int16_t | int16_t | essentially Signed type | +| test.c:119:9:119:10 | (int)... | int | int | essentially Signed type | +| test.c:119:9:119:10 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:120:3:120:4 | (int)... | int | int | essentially Signed type | +| test.c:120:3:120:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:120:3:120:10 | ... & ... | int16_t | int16_t | essentially Signed type | +| test.c:120:8:120:10 | (int)... | int | int | essentially Signed type | +| test.c:120:8:120:10 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:121:3:121:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:121:3:121:10 | ... & ... | int32_t | int32_t | essentially Signed type | +| test.c:121:9:121:10 | (int)... | int | int | essentially Signed type | +| test.c:121:9:121:10 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:122:3:122:4 | (int)... | int | int | essentially Signed type | +| test.c:122:3:122:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:122:3:122:10 | ... & ... | int32_t | int32_t | essentially Signed type | +| test.c:122:8:122:10 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:123:3:123:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:123:3:123:11 | ... & ... | int32_t | int32_t | essentially Signed type | +| test.c:123:9:123:11 | (int)... | int | int | essentially Signed type | +| test.c:123:9:123:11 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:124:3:124:5 | (int)... | int | int | essentially Signed type | +| test.c:124:3:124:5 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:124:3:124:11 | ... & ... | int32_t | int32_t | essentially Signed type | +| test.c:124:9:124:11 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:126:3:126:4 | (int)... | int | int | essentially Signed type | +| test.c:126:3:126:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:126:3:126:9 | ... \| ... | int8_t | int8_t | essentially Signed type | +| test.c:126:8:126:9 | (int)... | int | int | essentially Signed type | +| test.c:126:8:126:9 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:127:3:127:5 | (int)... | int | int | essentially Signed type | +| test.c:127:3:127:5 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:127:3:127:10 | ... \| ... | int16_t | int16_t | essentially Signed type | +| test.c:127:9:127:10 | (int)... | int | int | essentially Signed type | +| test.c:127:9:127:10 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:128:3:128:4 | (int)... | int | int | essentially Signed type | +| test.c:128:3:128:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:128:3:128:10 | ... \| ... | int16_t | int16_t | essentially Signed type | +| test.c:128:8:128:10 | (int)... | int | int | essentially Signed type | +| test.c:128:8:128:10 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:129:3:129:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:129:3:129:10 | ... \| ... | int32_t | int32_t | essentially Signed type | +| test.c:129:9:129:10 | (int)... | int | int | essentially Signed type | +| test.c:129:9:129:10 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:130:3:130:4 | (int)... | int | int | essentially Signed type | +| test.c:130:3:130:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:130:3:130:10 | ... \| ... | int32_t | int32_t | essentially Signed type | +| test.c:130:8:130:10 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:131:3:131:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:131:3:131:11 | ... \| ... | int32_t | int32_t | essentially Signed type | +| test.c:131:9:131:11 | (int)... | int | int | essentially Signed type | +| test.c:131:9:131:11 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:132:3:132:5 | (int)... | int | int | essentially Signed type | +| test.c:132:3:132:5 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:132:3:132:11 | ... \| ... | int32_t | int32_t | essentially Signed type | +| test.c:132:9:132:11 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:134:3:134:4 | (int)... | int | int | essentially Signed type | +| test.c:134:3:134:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:134:3:134:9 | ... ^ ... | int8_t | int8_t | essentially Signed type | +| test.c:134:8:134:9 | (int)... | int | int | essentially Signed type | +| test.c:134:8:134:9 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:135:3:135:5 | (int)... | int | int | essentially Signed type | +| test.c:135:3:135:5 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:135:3:135:10 | ... ^ ... | int16_t | int16_t | essentially Signed type | +| test.c:135:9:135:10 | (int)... | int | int | essentially Signed type | +| test.c:135:9:135:10 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:136:3:136:4 | (int)... | int | int | essentially Signed type | +| test.c:136:3:136:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:136:3:136:10 | ... ^ ... | int16_t | int16_t | essentially Signed type | +| test.c:136:8:136:10 | (int)... | int | int | essentially Signed type | +| test.c:136:8:136:10 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:137:3:137:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:137:3:137:10 | ... ^ ... | int32_t | int32_t | essentially Signed type | +| test.c:137:9:137:10 | (int)... | int | int | essentially Signed type | +| test.c:137:9:137:10 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:138:3:138:4 | (int)... | int | int | essentially Signed type | +| test.c:138:3:138:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:138:3:138:10 | ... ^ ... | int32_t | int32_t | essentially Signed type | +| test.c:138:8:138:10 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:139:3:139:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:139:3:139:11 | ... ^ ... | int32_t | int32_t | essentially Signed type | +| test.c:139:9:139:11 | (int)... | int | int | essentially Signed type | +| test.c:139:9:139:11 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:140:3:140:5 | (int)... | int | int | essentially Signed type | +| test.c:140:3:140:5 | s16 | int16_t | int16_t | essentially Signed type | +| test.c:140:3:140:11 | ... ^ ... | int32_t | int32_t | essentially Signed type | +| test.c:140:9:140:11 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:142:3:142:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:142:3:142:11 | ... & ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:142:9:142:11 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:142:9:142:11 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:143:3:143:5 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:143:3:143:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:143:3:143:11 | ... & ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:143:9:143:11 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:144:3:144:4 | (int)... | int | int | essentially Signed type | +| test.c:144:3:144:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:144:3:144:10 | ... & ... | int | int | essentially Signed type | +| test.c:144:8:144:10 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:145:3:145:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:145:3:145:10 | ... & ... | int | int | essentially Signed type | +| test.c:145:9:145:10 | (int)... | int | int | essentially Signed type | +| test.c:145:9:145:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:146:3:146:4 | (int)... | int | int | essentially Signed type | +| test.c:146:3:146:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:146:3:146:9 | ... & ... | int | int | essentially Signed type | +| test.c:146:8:146:9 | (int)... | int | int | essentially Signed type | +| test.c:146:8:146:9 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:147:3:147:4 | (int)... | int | int | essentially Signed type | +| test.c:147:3:147:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:147:3:147:9 | ... & ... | int | int | essentially Signed type | +| test.c:147:8:147:9 | (int)... | int | int | essentially Signed type | +| test.c:147:8:147:9 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:149:3:149:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:149:3:149:11 | ... \| ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:149:9:149:11 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:149:9:149:11 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:150:3:150:5 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:150:3:150:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:150:3:150:11 | ... \| ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:150:9:150:11 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:151:3:151:4 | (int)... | int | int | essentially Signed type | +| test.c:151:3:151:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:151:3:151:10 | ... \| ... | int | int | essentially Signed type | +| test.c:151:8:151:10 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:152:3:152:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:152:3:152:10 | ... \| ... | int | int | essentially Signed type | +| test.c:152:9:152:10 | (int)... | int | int | essentially Signed type | +| test.c:152:9:152:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:153:3:153:4 | (int)... | int | int | essentially Signed type | +| test.c:153:3:153:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:153:3:153:9 | ... \| ... | int | int | essentially Signed type | +| test.c:153:8:153:9 | (int)... | int | int | essentially Signed type | +| test.c:153:8:153:9 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:154:3:154:4 | (int)... | int | int | essentially Signed type | +| test.c:154:3:154:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:154:3:154:9 | ... \| ... | int | int | essentially Signed type | +| test.c:154:8:154:9 | (int)... | int | int | essentially Signed type | +| test.c:154:8:154:9 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:156:3:156:5 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:156:3:156:11 | ... ^ ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:156:9:156:11 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:156:9:156:11 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:157:3:157:5 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:157:3:157:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:157:3:157:11 | ... ^ ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:157:9:157:11 | u32 | uint32_t | uint32_t | essentially Unsigned type | +| test.c:158:3:158:4 | (int)... | int | int | essentially Signed type | +| test.c:158:3:158:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:158:3:158:10 | ... ^ ... | int | int | essentially Signed type | +| test.c:158:8:158:10 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:159:3:159:5 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:159:3:159:10 | ... ^ ... | int | int | essentially Signed type | +| test.c:159:9:159:10 | (int)... | int | int | essentially Signed type | +| test.c:159:9:159:10 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:160:3:160:4 | (int)... | int | int | essentially Signed type | +| test.c:160:3:160:4 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:160:3:160:9 | ... ^ ... | int | int | essentially Signed type | +| test.c:160:8:160:9 | (int)... | int | int | essentially Signed type | +| test.c:160:8:160:9 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:161:3:161:4 | (int)... | int | int | essentially Signed type | +| test.c:161:3:161:4 | s8 | int8_t | int8_t | essentially Signed type | +| test.c:161:3:161:9 | ... ^ ... | int | int | essentially Signed type | +| test.c:161:8:161:9 | (int)... | int | int | essentially Signed type | +| test.c:161:8:161:9 | u8 | uint8_t | uint8_t | essentially Unsigned type | +| test.c:165:16:165:17 | 1 | signed char | signed char | essentially Signed type | +| test.c:170:3:170:4 | 1 | unsigned char | unsigned char | essentially Unsigned type | +| test.c:170:3:170:9 | ... << ... | unsigned char | unsigned char | essentially Unsigned type | +| test.c:170:9:170:9 | 1 | signed char | signed char | essentially Signed type | +| test.c:171:3:171:6 | 256 | unsigned short | unsigned short | essentially Unsigned type | +| test.c:171:3:171:11 | ... << ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:171:11:171:11 | 1 | signed char | signed char | essentially Signed type | +| test.c:172:3:172:8 | 65536 | unsigned int | unsigned int | essentially Unsigned type | +| test.c:172:3:172:13 | ... << ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:172:13:172:13 | 1 | signed char | signed char | essentially Signed type | +| test.c:173:3:173:4 | 2 | unsigned char | unsigned char | essentially Unsigned type | +| test.c:173:3:173:9 | ... >> ... | unsigned char | unsigned char | essentially Unsigned type | +| test.c:173:9:173:9 | 1 | signed char | signed char | essentially Signed type | +| test.c:174:3:174:8 | 32768 | unsigned short | unsigned short | essentially Unsigned type | +| test.c:174:3:174:13 | ... >> ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:174:13:174:13 | 1 | signed char | signed char | essentially Signed type | +| test.c:175:3:175:13 | 2147483648 | unsigned int | unsigned int | essentially Unsigned type | +| test.c:175:3:175:18 | ... >> ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:175:18:175:18 | 1 | signed char | signed char | essentially Signed type | +| test.c:176:3:176:14 | 4294967295 | unsigned long | unsigned long | essentially Unsigned type | +| test.c:176:3:176:19 | ... << ... | unsigned long | unsigned long | essentially Unsigned type | +| test.c:176:3:176:19 | ... << ... | unsigned long | unsigned long long | essentially Unsigned type | +| test.c:176:3:176:19 | ... << ... | unsigned long long | unsigned long | essentially Unsigned type | +| test.c:176:3:176:19 | ... << ... | unsigned long long | unsigned long long | essentially Unsigned type | +| test.c:176:19:176:19 | 1 | signed char | signed char | essentially Signed type | +| test.c:181:3:181:6 | 256 | unsigned short | unsigned short | essentially Unsigned type | +| test.c:181:3:181:11 | ... >> ... | unsigned char | unsigned char | essentially Unsigned type | +| test.c:181:11:181:11 | 1 | signed char | signed char | essentially Signed type | +| test.c:182:3:182:8 | 65536 | unsigned int | unsigned int | essentially Unsigned type | +| test.c:182:3:182:13 | ... >> ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:182:13:182:13 | 1 | signed char | signed char | essentially Signed type | +| test.c:183:3:183:13 | 4294967296 | unsigned long | unsigned long | essentially Unsigned type | +| test.c:183:3:183:18 | ... >> ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:183:18:183:18 | 1 | signed char | signed char | essentially Signed type | +| test.c:184:3:184:6 | 255 | unsigned char | unsigned char | essentially Unsigned type | +| test.c:184:3:184:11 | ... << ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:184:11:184:11 | 1 | signed char | signed char | essentially Signed type | +| test.c:185:3:185:8 | 65535 | unsigned short | unsigned short | essentially Unsigned type | +| test.c:185:3:185:13 | ... << ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:185:13:185:13 | 1 | signed char | signed char | essentially Signed type | +| test.c:189:3:189:6 | 255 | unsigned char | unsigned char | essentially Unsigned type | +| test.c:189:3:189:13 | ... >> ... | unsigned char | unsigned char | essentially Unsigned type | +| test.c:189:11:189:13 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:190:3:190:8 | 65535 | unsigned short | unsigned short | essentially Unsigned type | +| test.c:190:3:190:15 | ... >> ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:190:13:190:15 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:191:3:191:13 | 4294967295 | unsigned int | unsigned int | essentially Unsigned type | +| test.c:191:3:191:20 | ... >> ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:191:18:191:20 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:192:3:192:6 | 255 | unsigned char | unsigned char | essentially Unsigned type | +| test.c:192:3:192:13 | ... << ... | unsigned char | unsigned char | essentially Unsigned type | +| test.c:192:11:192:13 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:193:3:193:8 | 65535 | unsigned short | unsigned short | essentially Unsigned type | +| test.c:193:3:193:15 | ... << ... | unsigned short | unsigned short | essentially Unsigned type | +| test.c:193:13:193:15 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:194:3:194:13 | 4294967295 | unsigned int | unsigned int | essentially Unsigned type | +| test.c:194:3:194:20 | ... << ... | unsigned int | unsigned int | essentially Unsigned type | +| test.c:194:18:194:20 | s32 | int32_t | int32_t | essentially Signed type | +| test.c:197:3:197:5 | 257 | short | short | essentially Signed type | +| test.c:197:3:197:5 | 257 | short | signed short | essentially Signed type | +| test.c:197:3:197:5 | 257 | signed short | short | essentially Signed type | +| test.c:197:3:197:5 | 257 | signed short | signed short | essentially Signed type | +| test.c:197:3:197:10 | ... >> ... | int | int | essentially Signed type | +| test.c:197:10:197:10 | 1 | signed char | signed char | essentially Signed type | +| test.c:198:3:198:7 | 65537 | int | int | essentially Signed type | +| test.c:198:3:198:7 | 65537 | int | signed int | essentially Signed type | +| test.c:198:3:198:7 | 65537 | signed int | int | essentially Signed type | +| test.c:198:3:198:7 | 65537 | signed int | signed int | essentially Signed type | +| test.c:198:3:198:12 | ... >> ... | int | int | essentially Signed type | +| test.c:198:12:198:12 | 1 | signed char | signed char | essentially Signed type | +| test.c:199:3:199:12 | 4294967297 | long | long | essentially Signed type | +| test.c:199:3:199:17 | ... >> ... | long | long | essentially Signed type | +| test.c:199:17:199:17 | 1 | signed char | signed char | essentially Signed type | diff --git a/c/misra/test/c/misra/EssentialTypes.ql b/c/misra/test/c/misra/EssentialTypes.ql new file mode 100644 index 0000000000..f8fe785727 --- /dev/null +++ b/c/misra/test/c/misra/EssentialTypes.ql @@ -0,0 +1,5 @@ +import codingstandards.c.misra.EssentialTypes + +from Expr e, Type et +where et = getEssentialType(e) +select e, et.getName(), getEssentialTypeBeforeConversions(e).getName(), getEssentialTypeCategory(et) diff --git a/c/misra/test/c/misra/test.c b/c/misra/test/c/misra/test.c new file mode 100644 index 0000000000..36a3eb0b10 --- /dev/null +++ b/c/misra/test/c/misra/test.c @@ -0,0 +1,200 @@ +#include + +void testConditional() { + unsigned int u = 1; + unsigned short us = 1; + signed int s = 1; + signed short ss = 1; + _Bool b = true; + + b ? u : u; // unsigned int + b ? s : s; // signed int + b ? s : ss; // signed int + b ? ss : s; // signed int + b ? us : u; // unsigned int + + b ? s : u; // unsigned int +} + +void testCategoriesForComplexTypes() { + typedef float float32_t; + typedef const float cfloat32_t; + const float f; + const float32_t f32; + cfloat32_t cf32; + + f; // Should be essentially Floating type + f32; // Should be essentially Floating type + cf32; // Should be essentially Floating type +} + +void testConstants() { + 1; // Essentially signed char + 1U; // Essentially unsigned char + 1UL; // Essentially unsigned long +} + +void testUnary() { + _Bool b = true; + unsigned int u = 1; + unsigned short us = 1; + signed int s = 1; + signed short ss = 1; + + !b; // Should be boolean + !u; // Should be boolean + !us; // Should be boolean + !s; // Should be boolean + !ss; // Should be boolean + + ~b; // Should be essentially signed + ~u; // Should be essentially unsigned + ~us; // Should be essentially unsigned + ~s; // Should be essentially signed + ~ss; // Should be essentially signed +} + +enum { EC1 }; +enum E1 { EC2 }; +typedef enum { EC3 } E2; + +enum { EC4 } g; + +enum { EC5 } test() { return EC5; } + +struct S1 { + enum { EC6 } m; +}; + +void testEnums() { + EC1; // Should be essentially signed + EC2; // Should be essentially enum + EC3; // Should be essentially enum + EC4; // Should be essentially enum + EC5; // Should be essentially enum + EC6; // Should be essentially enum +} + +void testControlChar() { + 'a'; // Essentially char + '\n'; // Essentially char + '\0'; // Essentially char +} + +#include +// clang-format off +void testBitwise() { // Clang format disabled to avoid confusion with variable declarations + uint8_t u8 = 0; + uint16_t u16 = 0; + uint32_t u32 = 0; + int8_t s8 = 0; + int16_t s16 = 0; + int32_t s32 = 0; + + u8 & u8; // Essentially unsigned, char + u16 & u8; // Essentially unsigned, short + u8 & u16; // Essentially unsigned, short + u32 & u8; // Essentially unsigned, int + u8 & u32; // Essentially unsigned, int + u32 & u16; // Essentially unsigned, int + u16 & u32; // Essentially unsigned, int + + u8 | u8; // Essentially unsigned, char + u16 | u8; // Essentially unsigned, short + u8 | u16; // Essentially unsigned, short + u32 | u8; // Essentially unsigned, int + u8 | u32; // Essentially unsigned, int + u32 | u16; // Essentially unsigned, int + u16 | u32; // Essentially unsigned, int + + u8 ^ u8; // Essentially unsigned, char + u16 ^ u8; // Essentially unsigned, short + u8 ^ u16; // Essentially unsigned, short + u32 ^ u8; // Essentially unsigned, int + u8 ^ u32; // Essentially unsigned, int + u32 ^ u16; // Essentially unsigned, int + u16 ^ u32; // Essentially unsigned, int + + s8 & s8; // Essentially signed, char + s16 & s8; // Essentially signed, short + s8 & s16; // Essentially signed, short + s32 & s8; // Essentially signed, int + s8 & s32; // Essentially signed, int + s32 & s16; // Essentially signed, int + s16 & s32; // Essentially signed, int + + s8 | s8; // Essentially signed, char + s16 | s8; // Essentially signed, short + s8 | s16; // Essentially signed, short + s32 | s8; // Essentially signed, int + s8 | s32; // Essentially signed, int + s32 | s16; // Essentially signed, int + s16 | s32; // Essentially signed, int + + s8 ^ s8; // Essentially signed, char + s16 ^ s8; // Essentially signed, short + s8 ^ s16; // Essentially signed, short + s32 ^ s8; // Essentially signed, int + s8 ^ s32; // Essentially signed, int + s32 ^ s16; // Essentially signed, int + s16 ^ s32; // Essentially signed, int + + u32 & s32; // Essentially unsigned, int + s32 & u32; // Essentially unsigned, int + u8 & s32; // Essentially signed, int + s32 & u8; // Essentially signed, int + u8 & s8; // Essentially signed, int + s8 & u8; // Essentially signed, int + + u32 | s32; // Essentially signed, int + s32 | u32; // Essentially signed, int + u8 | s32; // Essentially signed, int + s32 | u8; // Essentially signed, int + u8 | s8; // Essentially signed, int + s8 | u8; // Essentially signed, int + + u32 ^ s32; // Essentially signed, int + s32 ^ u32; // Essentially signed, int + u8 ^ s32; // Essentially signed, int + s32 ^ u8; // Essentially signed, int + u8 ^ s8; // Essentially signed, int + s8 ^ u8; // Essentially signed, int +} +// clang-format on +void testShifts() { + int32_t s32 = 1; + + // Left hand is unsigned and both are constants, so UTLR + // In these cases the UTLR is the same as the essential type of + // the left operand + 1U << 1; // Essentially unsigned char + 256U << 1; // Essentially unsigned short + 65536U << 1; // Essentially unsigned int + 2U >> 1; // Essentially unsigned char + 32768U >> 1; // Essentially unsigned short - 2^15 >> 1 = 2^14 + 2147483648U >> 1; // Essentially unsigned int - 2^31 >> 1 = 2^30 + 4294967295LU << 1; // Essentially unsigned long + + // Left hand is unsigned and both are constants, so UTLR + // In these cases the UTLR is not the same as the essential type of + // the left operand + 256U >> 1; // Essentially unsigned char + 65536U >> 1; // Essentially unsigned short + 4294967296U >> 1; // Essentially unsigned int + 255U << 1; // Essentially unsigned short + 65535U << 1; // Essentially unsigned int + + // Left hand is unsigned, but left isn't a constant, so essential type of left + // operand + 255U >> s32; // Essentially unsigned char + 65535U >> s32; // Essentially unsigned short + 4294967295U >> s32; // Essentially unsigned int + 255U << s32; // Essentially unsigned char + 65535U << s32; // Essentially unsigned short + 4294967295U << s32; // Essentially unsigned int + + // Left hand operand signed int, so result is standard type + 257 >> 1; // Essentially signed int + 65537 >> 1; // Essentially signed int + 4294967297 >> 1; // Essentially signed long +} \ No newline at end of file diff --git a/c/misra/test/codeql-pack.lock.yml b/c/misra/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/c/misra/test/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/misra/test/qlpack.yml b/c/misra/test/qlpack.yml index 2dc188b90c..08e5f579a3 100644 --- a/c/misra/test/qlpack.yml +++ b/c/misra/test/qlpack.yml @@ -1,4 +1,6 @@ -name: misra-c-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: misra-c-coding-standards -extractor: cpp \ No newline at end of file +name: codeql/misra-c-coding-standards-tests +version: 2.49.0-dev +extractor: cpp +license: MIT +dependencies: + codeql/misra-c-coding-standards: '*' diff --git a/c/misra/test/rules/RULE-4-10/PrecautionIncludeGuardsNotProvided.testref b/c/misra/test/rules/DIR-4-10/PrecautionIncludeGuardsNotProvided.testref similarity index 100% rename from c/misra/test/rules/RULE-4-10/PrecautionIncludeGuardsNotProvided.testref rename to c/misra/test/rules/DIR-4-10/PrecautionIncludeGuardsNotProvided.testref diff --git a/c/misra/test/rules/DIR-4-11/CheckMathLibraryFunctionParameters.testref b/c/misra/test/rules/DIR-4-11/CheckMathLibraryFunctionParameters.testref new file mode 100644 index 0000000000..50cf3fcb51 --- /dev/null +++ b/c/misra/test/rules/DIR-4-11/CheckMathLibraryFunctionParameters.testref @@ -0,0 +1 @@ +c/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.expected b/c/misra/test/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.expected new file mode 100644 index 0000000000..d5d5892975 --- /dev/null +++ b/c/misra/test/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.expected @@ -0,0 +1,18 @@ +| test.c:32:5:32:7 | call to sin | Call to periodic trigonometric function sin with maximum argument absolute value of 31.4, which exceeds the recommended maximum of pi. | +| test.c:33:5:33:7 | call to cos | Call to periodic trigonometric function cos with maximum argument absolute value of 31.4, which exceeds the recommended maximum of pi. | +| test.c:34:5:34:7 | call to tan | Call to periodic trigonometric function tan with maximum argument absolute value of 31.4, which exceeds the recommended maximum of pi. | +| test.c:38:5:38:7 | call to sin | Call to periodic trigonometric function sin with maximum argument absolute value of 31.4, which exceeds the recommended maximum of pi. | +| test.c:39:5:39:7 | call to cos | Call to periodic trigonometric function cos with maximum argument absolute value of 31.4, which exceeds the recommended maximum of pi. | +| test.c:40:5:40:7 | call to tan | Call to periodic trigonometric function tan with maximum argument absolute value of 31.4, which exceeds the recommended maximum of pi. | +| test.c:49:5:49:7 | call to sin | Call to periodic trigonometric function sin with maximum argument absolute value of 314, which exceeds the recommended maximum of pi. | +| test.c:50:5:50:7 | call to cos | Call to periodic trigonometric function cos with maximum argument absolute value of 314, which exceeds the recommended maximum of pi. | +| test.c:51:5:51:7 | call to tan | Call to periodic trigonometric function tan with maximum argument absolute value of 314, which exceeds the recommended maximum of pi. | +| test.c:52:5:52:7 | call to sin | Call to periodic trigonometric function sin with maximum argument absolute value of 314, which exceeds the recommended maximum of 10 * pi. | +| test.c:53:5:53:7 | call to cos | Call to periodic trigonometric function cos with maximum argument absolute value of 314, which exceeds the recommended maximum of 10 * pi. | +| test.c:54:5:54:7 | call to tan | Call to periodic trigonometric function tan with maximum argument absolute value of 314, which exceeds the recommended maximum of 10 * pi. | +| test.c:55:5:55:7 | call to sin | Call to periodic trigonometric function sin with maximum argument absolute value of 314, which exceeds the recommended maximum of pi. | +| test.c:56:5:56:7 | call to cos | Call to periodic trigonometric function cos with maximum argument absolute value of 314, which exceeds the recommended maximum of pi. | +| test.c:57:5:57:7 | call to tan | Call to periodic trigonometric function tan with maximum argument absolute value of 314, which exceeds the recommended maximum of pi. | +| test.c:58:5:58:7 | call to sin | Call to periodic trigonometric function sin with maximum argument absolute value of 314, which exceeds the recommended maximum of 10 * pi. | +| test.c:59:5:59:7 | call to cos | Call to periodic trigonometric function cos with maximum argument absolute value of 314, which exceeds the recommended maximum of 10 * pi. | +| test.c:60:5:60:7 | call to tan | Call to periodic trigonometric function tan with maximum argument absolute value of 314, which exceeds the recommended maximum of 10 * pi. | diff --git a/c/misra/test/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.qlref b/c/misra/test/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.qlref new file mode 100644 index 0000000000..f7bd11b44d --- /dev/null +++ b/c/misra/test/rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.qlref @@ -0,0 +1 @@ +rules/DIR-4-11/LowPrecisionPeriodicTrigonometricFunctionCall.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-11/test.c b/c/misra/test/rules/DIR-4-11/test.c new file mode 100644 index 0000000000..dac34860ff --- /dev/null +++ b/c/misra/test/rules/DIR-4-11/test.c @@ -0,0 +1,62 @@ +#include +void f(int x) { + float f1 = 0.0f; + double d1 = 0.0f; + sin(f1); // COMPLIANT + cos(f1); // COMPLIANT + tan(f1); // COMPLIANT + sin(d1); // COMPLIANT + cos(d1); // COMPLIANT + tan(d1); // COMPLIANT + + if (x < 10) { + f1 += 3.14; + d1 += 3.14; + sin(f1); // COMPLIANT + cos(f1); // COMPLIANT + tan(f1); // COMPLIANT + sin(d1); // COMPLIANT + cos(d1); // COMPLIANT + tan(d1); // COMPLIANT + sin(-f1); // COMPLIANT + cos(-f1); // COMPLIANT + tan(-f1); // COMPLIANT + sin(-d1); // COMPLIANT + cos(-d1); // COMPLIANT + tan(-d1); // COMPLIANT + } + + if (x < 20) { + f1 = 3.14 * 10; + d1 = 3.14 * 10; + sin(f1); // NON-COMPLIANT + cos(f1); // NON-COMPLIANT + tan(f1); // NON-COMPLIANT + sin(d1); // COMPLIANT + cos(d1); // COMPLIANT + tan(d1); // COMPLIANT + sin(-f1); // NON-COMPLIANT + cos(-f1); // NON-COMPLIANT + tan(-f1); // NON-COMPLIANT + sin(-d1); // COMPLIANT + cos(-d1); // COMPLIANT + tan(-d1); // COMPLIANT + } + + if (x < 30) { + f1 = 3.14 * 100; + d1 = 3.14 * 100; + sin(f1); // NON-COMPLIANT + cos(f1); // NON-COMPLIANT + tan(f1); // NON-COMPLIANT + sin(d1); // NON-COMPLIANT + cos(d1); // NON-COMPLIANT + tan(d1); // NON-COMPLIANT + sin(-f1); // NON-COMPLIANT + cos(-f1); // NON-COMPLIANT + tan(-f1); // NON-COMPLIANT + sin(-d1); // NON-COMPLIANT + cos(-d1); // NON-COMPLIANT + tan(-d1); // NON-COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.expected b/c/misra/test/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.expected similarity index 100% rename from c/misra/test/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.expected rename to c/misra/test/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.expected diff --git a/c/misra/test/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.qlref b/c/misra/test/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.qlref new file mode 100644 index 0000000000..b0cf5247b5 --- /dev/null +++ b/c/misra/test/rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.qlref @@ -0,0 +1 @@ +rules/DIR-4-12/StdLibDynamicMemoryAllocationUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-12/test.c b/c/misra/test/rules/DIR-4-12/test.c similarity index 100% rename from c/misra/test/rules/RULE-4-12/test.c rename to c/misra/test/rules/DIR-4-12/test.c diff --git a/c/misra/test/rules/DIR-4-15/PossibleMisuseOfUndetectedInfinity.testref b/c/misra/test/rules/DIR-4-15/PossibleMisuseOfUndetectedInfinity.testref new file mode 100644 index 0000000000..176855a83d --- /dev/null +++ b/c/misra/test/rules/DIR-4-15/PossibleMisuseOfUndetectedInfinity.testref @@ -0,0 +1 @@ +c/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-15/PossibleMisuseOfUndetectedNaN.testref b/c/misra/test/rules/DIR-4-15/PossibleMisuseOfUndetectedNaN.testref new file mode 100644 index 0000000000..7cd2a4d431 --- /dev/null +++ b/c/misra/test/rules/DIR-4-15/PossibleMisuseOfUndetectedNaN.testref @@ -0,0 +1 @@ +c/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-2/UsageOfAssemblyLanguageShouldBeDocumented.testref b/c/misra/test/rules/DIR-4-2/UsageOfAssemblyLanguageShouldBeDocumented.testref new file mode 100644 index 0000000000..3b0dc2fe5a --- /dev/null +++ b/c/misra/test/rules/DIR-4-2/UsageOfAssemblyLanguageShouldBeDocumented.testref @@ -0,0 +1 @@ +c/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-4/SectionsOfCodeShallNotBeCommentedOut.testref b/c/misra/test/rules/DIR-4-4/SectionsOfCodeShallNotBeCommentedOut.testref new file mode 100644 index 0000000000..4460b5ed53 --- /dev/null +++ b/c/misra/test/rules/DIR-4-4/SectionsOfCodeShallNotBeCommentedOut.testref @@ -0,0 +1 @@ +c/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.testref b/c/misra/test/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.testref index dffdbb26b8..2dc788dd11 100644 --- a/c/misra/test/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.testref +++ b/c/misra/test/rules/DIR-4-5/IdentifiersInTheSameNameSpaceUnambiguous.testref @@ -1 +1 @@ -cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql \ No newline at end of file +c/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected b/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected new file mode 100644 index 0000000000..49e0b1c34c --- /dev/null +++ b/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected @@ -0,0 +1,24 @@ +| test.c:14:5:14:10 | int4_t | The typedef type int4_t does not have its indicated size. | +| test.c:16:5:16:11 | uint4_t | The typedef type uint4_t does not have its indicated size. | +| test.c:19:25:19:33 | float64_t | The typedef name float64_t does not indicate a complex type. | +| test.c:22:15:22:24 | cfloat32_t | The typedef type cfloat32_t is not a complex type. | +| test.c:24:25:24:35 | cfloat128_t | The typedef type cfloat128_t does not have its indicated real size. | +| test.c:31:5:31:26 | _astronomical_number_t | The type _astronomical_number_t is not an alias to a fixed-width numeric type. | +| test.c:38:15:38:16 | c2 | The type signed char is not a fixed-width numeric type. | +| test.c:39:17:39:18 | c3 | The type unsigned char is not a fixed-width numeric type. | +| test.c:42:9:42:10 | s1 | The type short is not a fixed-width numeric type. | +| test.c:43:16:43:17 | s2 | The type signed short is not a fixed-width numeric type. | +| test.c:44:18:44:19 | s3 | The type unsigned short is not a fixed-width numeric type. | +| test.c:47:7:47:8 | i1 | The type int is not a fixed-width numeric type. | +| test.c:48:14:48:15 | i2 | The type signed int is not a fixed-width numeric type. | +| test.c:49:16:49:17 | i3 | The type unsigned int is not a fixed-width numeric type. | +| test.c:52:8:52:9 | l1 | The type long is not a fixed-width numeric type. | +| test.c:53:15:53:16 | l2 | The type signed long is not a fixed-width numeric type. | +| test.c:54:17:54:18 | l3 | The type unsigned long is not a fixed-width numeric type. | +| test.c:57:13:57:15 | ll1 | The type long long is not a fixed-width numeric type. | +| test.c:58:20:58:22 | ll2 | The type signed long long is not a fixed-width numeric type. | +| test.c:59:22:59:24 | ll3 | The type unsigned long long is not a fixed-width numeric type. | +| test.c:62:9:62:10 | f1 | The type float is not a fixed-width numeric type. | +| test.c:65:10:65:11 | d1 | The type double is not a fixed-width numeric type. | +| test.c:68:15:68:17 | ld1 | The type long double is not a fixed-width numeric type. | +| test.c:71:18:71:20 | cf1 | The type _Complex float is not a fixed-width numeric type. | diff --git a/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.qlref b/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.qlref new file mode 100644 index 0000000000..283e88209c --- /dev/null +++ b/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.qlref @@ -0,0 +1 @@ +rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-6/test.c b/c/misra/test/rules/DIR-4-6/test.c new file mode 100644 index 0000000000..0fc79faa2e --- /dev/null +++ b/c/misra/test/rules/DIR-4-6/test.c @@ -0,0 +1,73 @@ +typedef signed char int8_t; // COMPLIANT: exception, typedefs are permitted +typedef unsigned char uint8_t; // COMPLIANT: exception, typedefs are permitted + +typedef signed short int16_t; // COMPLIANT: exception, typedefs are permitted +typedef unsigned short uint16_t; // COMPLIANT: exception, typedefs are permitted + +typedef signed int int32_t; // COMPLIANT: exception, typedefs are permitted +typedef unsigned int uint32_t; // COMPLIANT: exception, typedefs are permitted + +typedef signed long int64_t; // COMPLIANT: exception, typedefs are permitted +typedef unsigned long uint64_t; // COMPLIANT: exception, typedefs are permitted + +typedef signed long long + int4_t; // NON_COMPLIANT: typedef does not have its indicated size +typedef unsigned long long + uint4_t; // NON_COMPLIANT: typedef does not have its indicated size + +typedef float float32_t; // COMPLIANT: exception, typedefs are permitted +typedef double _Complex float64_t; // NON-COMPLIANT: not complex floating type +typedef long double float128_t; // COMPLIANT: exception, typedefs are permitted + +typedef float cfloat32_t; // NON-COMPLIANT: not a complex floating type +typedef double _Complex cfloat64_t; // COMPLIANT: correct complex floating type +typedef double _Complex cfloat128_t; // NON-COMPLIANT: incorrect complex size + +typedef int8_t + astronomical_number_t; // COMPLIANT: aliasing a fixed-width numeric typedef +typedef uint8_t u_astronomical_number_t; // COMPLIANT: aliasing a fixed-width + // numeric typedef +typedef int + _astronomical_number_t; // NON_COMPLIANT: aliasing a basic numeric type + +int // COMPLIANT: exception, main's return type can be plain int +main(int argc, // COMPLIANT: exception, argc's type can be plain int + char *argv[]) { // COMPLIANT: char is not a basic numeric type + + char c1 = 1; // COMPLIANT: char is not a basic numeric type + signed char c2 = 1; // NON_COMPLIANT: use typedef int8_t + unsigned char c3 = 1; // NON_COMPLIANT: use typedef uint8_t + int8_t c4 = 1; // COMPLIANT: typedef used instead + + short s1 = 1; // NON_COMPLIANT: short is a basic numeric type + signed short s2 = 1; // NON_COMPLIANT: use typedef int16_t + unsigned short s3 = 1; // NON_COMPLIANT: use typedef uint16_t + int16_t s4 = 1; // COMPLIANT: typedef used instead + + int i1 = 1; // NON_COMPLIANT: int is a basic numeric type + signed int i2 = 1; // NON_COMPLIANT: use typedef int32_t + unsigned int i3 = 1; // NON_COMPLIANT: use typedef uint32_t + int32_t i4 = 1; // COMPLIANT: typedef used instead + + long l1 = 1; // NON_COMPLIANT: int is a basic numeric type + signed long l2 = 1; // NON_COMPLIANT: use typedef int64_t + unsigned long l3 = 1; // NON_COMPLIANT: use typedef uint64_t + int64_t l4 = 1; // COMPLIANT: typedef used instead + + long long ll1 = 1; // NON_COMPLIANT: int is a basic numeric type + signed long long ll2 = 1; // NON_COMPLIANT: use typedef int64_t + unsigned long long ll3 = 1; // NON_COMPLIANT: use typedef uint64_t + int64_t ll4 = 1; // COMPLIANT: typedef used instead + + float f1 = 1; // NON_COMPLIANT: float is a basic numeric type, use a typedef + float32_t f2 = 1; // COMPLIANT: typedef used instead + + double d1 = 1; // NON_COMPLIANT: int is a basic numeric type + float64_t d2 = 1; // COMPLIANT: typedef used instead + + long double ld1 = 1; // NON_COMPLIANT: int is a basic numeric type + float128_t ld2 = 1; // COMPLIANT: typedef used instead + + float _Complex cf1 = 1; // NON_COMPLIANT: complex basic numeric type + cfloat64_t cf2 = 1; // COMPLIANT: typedef used instead +} \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-7/FunctionErrorInformationUntested.testref b/c/misra/test/rules/DIR-4-7/FunctionErrorInformationUntested.testref new file mode 100644 index 0000000000..51bd5fbefb --- /dev/null +++ b/c/misra/test/rules/DIR-4-7/FunctionErrorInformationUntested.testref @@ -0,0 +1 @@ +c/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.expected b/c/misra/test/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.expected similarity index 100% rename from c/misra/test/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.expected rename to c/misra/test/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.expected diff --git a/c/misra/test/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.qlref b/c/misra/test/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.qlref new file mode 100644 index 0000000000..dfa03268cf --- /dev/null +++ b/c/misra/test/rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.qlref @@ -0,0 +1 @@ +rules/DIR-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-8/test.c b/c/misra/test/rules/DIR-4-8/test.c similarity index 100% rename from c/misra/test/rules/RULE-4-8/test.c rename to c/misra/test/rules/DIR-4-8/test.c diff --git a/c/misra/test/rules/RULE-4-8/test.h b/c/misra/test/rules/DIR-4-8/test.h similarity index 100% rename from c/misra/test/rules/RULE-4-8/test.h rename to c/misra/test/rules/DIR-4-8/test.h diff --git a/c/misra/test/rules/RULE-4-8/test_2.c b/c/misra/test/rules/DIR-4-8/test_2.c similarity index 100% rename from c/misra/test/rules/RULE-4-8/test_2.c rename to c/misra/test/rules/DIR-4-8/test_2.c diff --git a/c/misra/test/rules/RULE-4-8/test_shared.h b/c/misra/test/rules/DIR-4-8/test_shared.h similarity index 100% rename from c/misra/test/rules/RULE-4-8/test_shared.h rename to c/misra/test/rules/DIR-4-8/test_shared.h diff --git a/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.expected b/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.expected new file mode 100644 index 0000000000..22d614a183 --- /dev/null +++ b/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.expected @@ -0,0 +1,2 @@ +| test.c:6:1:6:25 | #define MACRO4(x) (x + 1) | Macro used instead of a function. | +| test.c:11:1:11:48 | #define MACRO9() printf_custom("output = %d", 7) | Macro used instead of a function. | diff --git a/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.qlref b/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.qlref new file mode 100644 index 0000000000..831e6e3101 --- /dev/null +++ b/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.qlref @@ -0,0 +1 @@ +rules/DIR-4-9/FunctionOverFunctionLikeMacro.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.testref b/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.testref new file mode 100644 index 0000000000..fb033c44e4 --- /dev/null +++ b/c/misra/test/rules/DIR-4-9/FunctionOverFunctionLikeMacro.testref @@ -0,0 +1 @@ +c/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-9/test.c b/c/misra/test/rules/DIR-4-9/test.c new file mode 100644 index 0000000000..304c4bd004 --- /dev/null +++ b/c/misra/test/rules/DIR-4-9/test.c @@ -0,0 +1,41 @@ +#include + +#define MACRO(OP, L, R) ((L)OP(R)) // COMPLIANT +#define MACRO2(L, R) (L + R) // COMPLIANT +#define MACRO3(L, R) (L " " R " " L) // COMPLIANT +#define MACRO4(x) (x + 1) // NON_COMPLIANT +#define MACRO5(L, LR) (LR + 1) // COMPLIANT +#define MACRO6(x) printf_custom("output = %d", test##x) // COMPLIANT +#define MACRO7(x) #x // COMPLIANT +#define MACRO8(x) "NOP" // COMPLIANT +#define MACRO9() printf_custom("output = %d", 7) // NON_COMPLIANT +#define MACRO10(x) // COMPLIANT +#define MACRO11(x) _Generic((x), int : 1, default : 0) // COMPLIANT +#define MY_ASSERT(X) assert(X) // NON_COMPLIANT[FALSE_NEGATIVE] + +const char a1[MACRO2(1, 1) + 6]; +extern printf_custom(); +int test1; + +void f() { + int i = MACRO(+, 1, 1); + int i2 = MACRO2(7, 10); + + static int i3 = MACRO2(1, 1); + + char *i4 = MACRO3("prefix", "suffix"); + + int i5 = MACRO4(1); + + int i6 = MACRO4(MACRO2(1, 1)); + + int i7 = MACRO5(1, 1); + + MACRO6(1); + + char *i10 = MACRO7("prefix"); + + asm(MACRO8(1)); + + MY_ASSERT(1); +} \ No newline at end of file diff --git a/c/misra/test/rules/DIR-5-1/PossibleDataRaceBetweenThreads.expected b/c/misra/test/rules/DIR-5-1/PossibleDataRaceBetweenThreads.expected new file mode 100644 index 0000000000..e1c0e9389d --- /dev/null +++ b/c/misra/test/rules/DIR-5-1/PossibleDataRaceBetweenThreads.expected @@ -0,0 +1,24 @@ +| test.c:31:3:31:8 | ... = ... | Threaded write to object $@ from thread function $@ is not synchronized with $@ from thread function $@. | test.c:11:5:11:6 | g2 | g2 | test.c:30:6:30:29 | single_thread4_writes_g2 | single_thread4_writes_g2 | test.c:27:3:27:4 | g2 | concurrent read operation | test.c:26:6:26:28 | single_thread3_reads_g2 | single_thread3_reads_g2 | +| test.c:35:3:35:8 | ... = ... | Threaded write to object $@ not synchronized from thread function $@ spawned from a loop. | test.c:12:5:12:6 | g3 | g3 | test.c:34:6:34:27 | many_thread5_writes_g3 | many_thread5_writes_g3 | test.c:35:3:35:4 | g3 | concurrent read operation | test.c:34:6:34:27 | many_thread5_writes_g3 | many_thread5_writes_g3 | +| test.c:71:3:71:11 | ... = ... | Threaded write to object $@ from thread function $@ is not synchronized with $@ from thread function $@. | test.c:68:3:68:4 | g7 | g7.m1 | test.c:70:6:70:33 | single_thread11_writes_g7_m1 | single_thread11_writes_g7_m1 | test.c:75:6:75:7 | m1 | concurrent read operation | test.c:74:6:74:33 | single_thread12_writes_g7_m1 | single_thread12_writes_g7_m1 | +| test.c:75:3:75:11 | ... = ... | Threaded write to object $@ from thread function $@ is not synchronized with $@ from thread function $@. | test.c:68:3:68:4 | g7 | g7.m1 | test.c:74:6:74:33 | single_thread12_writes_g7_m1 | single_thread12_writes_g7_m1 | test.c:71:6:71:7 | m1 | concurrent read operation | test.c:70:6:70:33 | single_thread11_writes_g7_m1 | single_thread11_writes_g7_m1 | +| test.c:79:3:79:11 | call to setlocale | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:79:3:79:11 | call to setlocale | setlocale | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:79:3:79:11 | call to setlocale | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:80:3:80:8 | call to tmpnam | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:80:3:80:8 | call to tmpnam | tmpnam | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:80:3:80:8 | call to tmpnam | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:81:3:81:6 | call to rand | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:81:3:81:6 | call to rand | rand | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:81:3:81:6 | call to rand | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:82:3:82:7 | call to srand | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:82:3:82:7 | call to srand | srand | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:82:3:82:7 | call to srand | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:83:3:83:8 | call to getenv | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:83:3:83:8 | call to getenv | getenv | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:83:3:83:8 | call to getenv | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:84:3:84:10 | call to getenv_s | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:84:3:84:10 | call to getenv_s | getenv_s | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:84:3:84:10 | call to getenv_s | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:86:3:86:10 | call to strerror | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:86:3:86:10 | call to strerror | strerror | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:86:3:86:10 | call to strerror | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:87:3:87:9 | call to asctime | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:87:3:87:9 | call to asctime | asctime | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:87:3:87:9 | call to asctime | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:88:3:88:7 | call to ctime | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:88:3:88:7 | call to ctime | ctime | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:88:3:88:7 | call to ctime | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:89:3:89:8 | call to gmtime | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:89:3:89:8 | call to gmtime | gmtime | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:89:3:89:8 | call to gmtime | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:90:3:90:11 | call to localtime | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:90:3:90:11 | call to localtime | localtime | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:90:3:90:11 | call to localtime | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:91:3:91:10 | call to mbrtoc16 | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:91:3:91:10 | call to mbrtoc16 | mbrtoc16 | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:91:3:91:10 | call to mbrtoc16 | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:92:3:92:10 | call to mbrtoc32 | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:92:3:92:10 | call to mbrtoc32 | mbrtoc32 | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:92:3:92:10 | call to mbrtoc32 | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:93:3:93:10 | call to c16rtomb | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:93:3:93:10 | call to c16rtomb | c16rtomb | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:93:3:93:10 | call to c16rtomb | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:94:3:94:10 | call to c32rtomb | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:94:3:94:10 | call to c32rtomb | c32rtomb | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:94:3:94:10 | call to c32rtomb | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:95:3:95:8 | call to mbrlen | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:95:3:95:8 | call to mbrlen | mbrlen | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:95:3:95:8 | call to mbrlen | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:96:3:96:9 | call to mbrtowc | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:96:3:96:9 | call to mbrtowc | mbrtowc | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:96:3:96:9 | call to mbrtowc | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:97:3:97:9 | call to wcrtomb | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:97:3:97:9 | call to wcrtomb | wcrtomb | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:97:3:97:9 | call to wcrtomb | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:98:3:98:11 | call to mbsrtowcs | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:98:3:98:11 | call to mbsrtowcs | mbsrtowcs | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:98:3:98:11 | call to mbsrtowcs | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | +| test.c:99:3:99:11 | call to wcsrtombs | Threaded call to non-reentrant function $@ not synchronized from thread function $@ spawned from a loop. | test.c:99:3:99:11 | call to wcsrtombs | wcsrtombs | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | test.c:99:3:99:11 | call to wcsrtombs | concurrent call to non-reentrant function | test.c:78:6:78:43 | many_thread13_calls_nonreentrant_funcs | many_thread13_calls_nonreentrant_funcs | diff --git a/c/misra/test/rules/DIR-5-1/PossibleDataRaceBetweenThreads.qlref b/c/misra/test/rules/DIR-5-1/PossibleDataRaceBetweenThreads.qlref new file mode 100644 index 0000000000..737cf79505 --- /dev/null +++ b/c/misra/test/rules/DIR-5-1/PossibleDataRaceBetweenThreads.qlref @@ -0,0 +1 @@ +rules/DIR-5-1/PossibleDataRaceBetweenThreads.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-5-1/test.c b/c/misra/test/rules/DIR-5-1/test.c new file mode 100644 index 0000000000..5f392105e6 --- /dev/null +++ b/c/misra/test/rules/DIR-5-1/test.c @@ -0,0 +1,132 @@ +#include "locale.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "threads.h" +#include "time.h" +#include "uchar.h" +#include "wchar.h" + +int g1; +int g2; +int g3; +int g4; +mtx_t g4_lock; +int g5; +mtx_t g5_lock; + +void single_thread1_reads_g1(void *p) { + g1; // COMPLIANT +} + +void many_thread2_reads_g1(void *p) { + g1; // COMPLIANT +} + +void single_thread3_reads_g2(void *p) { + g2; // COMPLIANT +} + +void single_thread4_writes_g2(void *p) { + g2 = 1; // NON-COMPLIANT +} + +void many_thread5_writes_g3(void *p) { + g3 = 1; // NON-COMPLIANT +} + +void single_thread6_reads_g4_locked(void *p) { + mtx_lock(&g4_lock); + g4; // COMPLIANT +} + +void single_thread7_writes_g4_locked(void *p) { + mtx_lock(&g4_lock); + g4 = 1; // COMPLIANT +} + +void many_thread8_writes_g5_locked(void *p) { + mtx_lock(&g5_lock); + g5 = 1; // COMPLIANT +} + +struct { + int m1; + int m2; +} g6; + +void single_thread9_writes_g6_m1(void *p) { + g6.m1 = 1; // COMPLIANT +} + +void single_thread10_writes_g6_m2(void *p) { + g6.m2 = 1; // COMPLIANT +} + +struct { + int m1; +} g7; + +void single_thread11_writes_g7_m1(void *p) { + g7.m1 = 1; // NON-COMPLIANT +} + +void single_thread12_writes_g7_m1(void *p) { + g7.m1 = 1; // NON-COMPLIANT +} + +void many_thread13_calls_nonreentrant_funcs(void *p) { + setlocale(LC_ALL, "C"); // NON-COMPLIANT + tmpnam(""); // NON-COMPLIANT + rand(); // NON-COMPLIANT + srand(0); // NON-COMPLIANT + getenv("PATH"); // NON-COMPLIANT + getenv_s(NULL, NULL, 0, NULL); // NON-COMPLIANT + strtok("a", "b"); // NON-COMPLIANT + strerror(0); // NON-COMPLIANT + asctime(NULL); // NON-COMPLIANT + ctime(NULL); // NON-COMPLIANT + gmtime(NULL); // NON-COMPLIANT + localtime(NULL); // NON-COMPLIANT + mbrtoc16(NULL, NULL, 0, NULL); // NON-COMPLIANT + mbrtoc32(NULL, NULL, 0, NULL); // NON-COMPLIANT + c16rtomb(NULL, 0, NULL); // NON-COMPLIANT + c32rtomb(NULL, 0, NULL); // NON-COMPLIANT + mbrlen(NULL, 0, NULL); // NON-COMPLIANT + mbrtowc(NULL, NULL, 0, NULL); // NON-COMPLIANT + wcrtomb(NULL, 0, NULL); // NON-COMPLIANT + mbsrtowcs(NULL, NULL, 0, NULL); // NON-COMPLIANT + wcsrtombs(NULL, NULL, 0, NULL); // NON-COMPLIANT +} + +int main(int argc, char *argv[]) { + thrd_t single_thread1; + thrd_t many_thread2; + thrd_t single_thread3; + thrd_t single_thread4; + thrd_t many_thread5; + thrd_t single_thread6; + thrd_t single_thread7; + thrd_t many_thread8; + thrd_t single_thread9; + thrd_t single_thread10; + thrd_t single_thread11; + thrd_t single_thread12; + thrd_t many_thread13; + + thrd_create(&single_thread1, single_thread1_reads_g1, NULL); + thrd_create(&single_thread3, single_thread3_reads_g2, NULL); + thrd_create(&single_thread4, single_thread4_writes_g2, NULL); + thrd_create(&single_thread6, single_thread6_reads_g4_locked, NULL); + thrd_create(&single_thread7, single_thread7_writes_g4_locked, NULL); + thrd_create(&single_thread9, single_thread9_writes_g6_m1, NULL); + thrd_create(&single_thread10, single_thread10_writes_g6_m2, NULL); + thrd_create(&single_thread11, single_thread11_writes_g7_m1, NULL); + thrd_create(&single_thread12, single_thread12_writes_g7_m1, NULL); + for (;;) { + thrd_create(&many_thread2, many_thread2_reads_g1, NULL); + thrd_create(&many_thread5, many_thread5_writes_g3, NULL); + thrd_create(&many_thread8, many_thread8_writes_g5_locked, NULL); + thrd_create(&many_thread13, many_thread13_calls_nonreentrant_funcs, NULL); + } +} \ No newline at end of file diff --git a/c/misra/test/rules/DIR-5-2/NotNoDeadlocksBetweenThreads.testref b/c/misra/test/rules/DIR-5-2/NotNoDeadlocksBetweenThreads.testref new file mode 100644 index 0000000000..4625d1a24d --- /dev/null +++ b/c/misra/test/rules/DIR-5-2/NotNoDeadlocksBetweenThreads.testref @@ -0,0 +1 @@ +c/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-5-3/BannedDynamicThreadCreation.expected b/c/misra/test/rules/DIR-5-3/BannedDynamicThreadCreation.expected new file mode 100644 index 0000000000..3bc3ab579a --- /dev/null +++ b/c/misra/test/rules/DIR-5-3/BannedDynamicThreadCreation.expected @@ -0,0 +1,16 @@ +| test.c:30:3:30:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:29:6:29:34 | make_threads_called_from_main | make_threads_called_from_main | +| test.c:31:3:31:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:29:6:29:34 | make_threads_called_from_main | make_threads_called_from_main | +| test.c:39:3:39:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:38:6:38:51 | make_threads_called_from_func_called_from_main | make_threads_called_from_func_called_from_main | +| test.c:40:3:40:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:38:6:38:51 | make_threads_called_from_func_called_from_main | make_threads_called_from_func_called_from_main | +| test.c:49:3:49:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:48:7:48:18 | pthread_func | pthread_func | +| test.c:50:3:50:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:48:7:48:18 | pthread_func | pthread_func | +| test.c:58:3:58:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:57:5:57:13 | thrd_func | thrd_func | +| test.c:59:3:59:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:57:5:57:13 | thrd_func | thrd_func | +| test.c:67:3:67:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:66:6:66:39 | make_threads_called_from_thrd_func | make_threads_called_from_thrd_func | +| test.c:68:3:68:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:66:6:66:39 | make_threads_called_from_thrd_func | make_threads_called_from_thrd_func | +| test.c:76:3:76:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:75:6:75:59 | make_threads_called_from_func_called_from_pthread_thrd | make_threads_called_from_func_called_from_pthread_thrd | +| test.c:77:3:77:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:75:6:75:59 | make_threads_called_from_func_called_from_pthread_thrd | make_threads_called_from_func_called_from_pthread_thrd | +| test.c:81:3:81:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:80:6:80:47 | make_threads_called_from_main_pthread_thrd | make_threads_called_from_main_pthread_thrd | +| test.c:82:3:82:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:80:6:80:47 | make_threads_called_from_main_pthread_thrd | make_threads_called_from_main_pthread_thrd | +| test.c:86:3:86:13 | call to thrd_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:85:6:85:38 | make_threads_not_called_by_anyone | make_threads_not_called_by_anyone | +| test.c:87:3:87:16 | call to pthread_create | Possible dynamic creation of thread outside initialization in function '$@'. | test.c:85:6:85:38 | make_threads_not_called_by_anyone | make_threads_not_called_by_anyone | diff --git a/c/misra/test/rules/DIR-5-3/BannedDynamicThreadCreation.qlref b/c/misra/test/rules/DIR-5-3/BannedDynamicThreadCreation.qlref new file mode 100644 index 0000000000..16c9614cec --- /dev/null +++ b/c/misra/test/rules/DIR-5-3/BannedDynamicThreadCreation.qlref @@ -0,0 +1 @@ +rules/DIR-5-3/BannedDynamicThreadCreation.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-5-3/ThreadCreatedByThread.expected b/c/misra/test/rules/DIR-5-3/ThreadCreatedByThread.expected new file mode 100644 index 0000000000..b8dc2bfe4b --- /dev/null +++ b/c/misra/test/rules/DIR-5-3/ThreadCreatedByThread.expected @@ -0,0 +1,14 @@ +| test.c:49:3:49:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:48:7:48:18 | pthread_func | pthread_func | test.c:19:3:19:16 | call to pthread_create | started as a thread | +| test.c:50:3:50:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:48:7:48:18 | pthread_func | pthread_func | test.c:19:3:19:16 | call to pthread_create | started as a thread | +| test.c:58:3:58:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:59:3:59:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:67:3:67:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:68:3:68:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:76:3:76:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:48:7:48:18 | pthread_func | pthread_func | test.c:19:3:19:16 | call to pthread_create | started as a thread | +| test.c:76:3:76:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:77:3:77:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:48:7:48:18 | pthread_func | pthread_func | test.c:19:3:19:16 | call to pthread_create | started as a thread | +| test.c:77:3:77:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:81:3:81:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:48:7:48:18 | pthread_func | pthread_func | test.c:19:3:19:16 | call to pthread_create | started as a thread | +| test.c:81:3:81:13 | call to thrd_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | +| test.c:82:3:82:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:48:7:48:18 | pthread_func | pthread_func | test.c:19:3:19:16 | call to pthread_create | started as a thread | +| test.c:82:3:82:16 | call to pthread_create | Thread creation call reachable from function '$@', which may also be $@. | test.c:57:5:57:13 | thrd_func | thrd_func | test.c:18:3:18:13 | call to thrd_create | started as a thread | diff --git a/c/misra/test/rules/DIR-5-3/ThreadCreatedByThread.qlref b/c/misra/test/rules/DIR-5-3/ThreadCreatedByThread.qlref new file mode 100644 index 0000000000..99cecb8311 --- /dev/null +++ b/c/misra/test/rules/DIR-5-3/ThreadCreatedByThread.qlref @@ -0,0 +1 @@ +rules/DIR-5-3/ThreadCreatedByThread.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-5-3/test.c b/c/misra/test/rules/DIR-5-3/test.c new file mode 100644 index 0000000000..16eb580276 --- /dev/null +++ b/c/misra/test/rules/DIR-5-3/test.c @@ -0,0 +1,88 @@ +#include "pthread.h" +#include "threads.h" + +thrd_t g1; // COMPLIANT +pthread_t g2; // COMPLIANT + +void *pthread_func(void *arg); +void *pthread_func_inner(void *arg); +int thrd_func(void *arg); +int thrd_func_inner(void *arg); + +void make_threads_called_from_main(void); +void func_called_from_main(void); +void make_threads_called_from_func_called_from_main(void); +void make_threads_called_from_main_pthread_thrd(void); + +int main(int argc, char *argv[]) { + thrd_create(&g1, &thrd_func, NULL); // COMPLIANT + pthread_create(&g2, NULL, &pthread_func, NULL); // COMPLIANT + + thrd_create(&g1, &thrd_func_inner, NULL); // COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // COMPLIANT + + make_threads_called_from_main(); + func_called_from_main(); + make_threads_called_from_main_pthread_thrd(); +} + +void make_threads_called_from_main() { + thrd_create(&g1, &thrd_func_inner, NULL); // COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // COMPLIANT +} + +void func_called_from_main() { + make_threads_called_from_func_called_from_main(); +} + +void make_threads_called_from_func_called_from_main() { + thrd_create(&g1, &thrd_func_inner, NULL); // COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // COMPLIANT +} + +void make_threads_called_from_pthread_func(void); +void make_threads_called_from_thrd_func(void); +void func_called_from_pthread_thrd(void); +void make_threads_called_from_func_called_from_pthread_thrd(void); + +void *pthread_func(void *arg) { + thrd_create(&g1, &thrd_func_inner, NULL); // NON-COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // NON-COMPLIANT + + make_threads_called_from_pthread_func(); + func_called_from_pthread_thrd(); + make_threads_called_from_main_pthread_thrd(); +} + +int thrd_func(void *arg) { + thrd_create(&g1, &thrd_func_inner, NULL); // NON-COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // NON-COMPLIANT + + make_threads_called_from_thrd_func(); + func_called_from_pthread_thrd(); + make_threads_called_from_main_pthread_thrd(); +} + +void make_threads_called_from_thrd_func(void) { + thrd_create(&g1, &thrd_func_inner, NULL); // NON-COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // NON-COMPLIANT +} + +void func_called_from_pthread_thrd(void) { + make_threads_called_from_func_called_from_pthread_thrd(); +} + +void make_threads_called_from_func_called_from_pthread_thrd(void) { + thrd_create(&g1, &thrd_func_inner, NULL); // NON-COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // NON-COMPLIANT +} + +void make_threads_called_from_main_pthread_thrd() { + thrd_create(&g1, &thrd_func_inner, NULL); // NON-COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // NON-COMPLIANT +} + +void make_threads_not_called_by_anyone() { + thrd_create(&g1, &thrd_func_inner, NULL); // COMPLIANT + pthread_create(&g2, NULL, &pthread_func_inner, NULL); // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.expected b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.expected new file mode 100644 index 0000000000..0e2cbb26ee --- /dev/null +++ b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.expected @@ -0,0 +1,51 @@ +| test.c:34:1:34:23 | #define A __BASE_FILE__ | Use of builtin macro '__BASE_FILE__' is a compiler extension and is not portable to other compilers. | +| test.c:35:1:35:23 | #define B __FILE_NAME__ | Use of builtin macro '__FILE_NAME__' is a compiler extension and is not portable to other compilers. | +| test.c:36:1:36:21 | #define C __COUNTER__ | Use of builtin macro '__COUNTER__' is a compiler extension and is not portable to other compilers. | +| test.c:37:1:37:27 | #define D __INCLUDE_LEVEL__ | Use of builtin macro '__INCLUDE_LEVEL__' is a compiler extension and is not portable to other compilers. | +| test.c:39:1:39:19 | #define F __clang__ | Use of builtin macro '__clang__' is a compiler extension and is not portable to other compilers. | +| test.c:40:1:40:25 | #define G __clang_major__ | Use of builtin macro '__clang_major__' is a compiler extension and is not portable to other compilers. | +| test.c:41:1:41:25 | #define H __clang_minor__ | Use of builtin macro '__clang_minor__' is a compiler extension and is not portable to other compilers. | +| test.c:42:1:42:30 | #define I __clang_patchlevel__ | Use of builtin macro '__clang_patchlevel__' is a compiler extension and is not portable to other compilers. | +| test.c:43:1:43:27 | #define J __clang_version__ | Use of builtin macro '__clang_version__' is a compiler extension and is not portable to other compilers. | +| test.c:44:1:44:36 | #define K __clang_literal_encoding__ | Use of builtin macro '__clang_literal_encoding__' is a compiler extension and is not portable to other compilers. | +| test.c:45:1:45:41 | #define L __clang_wide_literal_encoding__ | Use of builtin macro '__clang_wide_literal_encoding__' is a compiler extension and is not portable to other compilers. | +| test.c:53:33:53:43 | vector_size | Use of attribute 'vector_size' is a compiler extension and is not portable to other compilers. | +| test.c:54:33:54:47 | vector_size | Use of attribute 'vector_size' is a compiler extension and is not portable to other compilers. | +| test.c:55:37:55:51 | ext_vector_type | Use of attribute 'ext_vector_type' is a compiler extension and is not portable to other compilers. | +| test.c:56:37:56:51 | ext_vector_type | Use of attribute 'ext_vector_type' is a compiler extension and is not portable to other compilers. | +| test.c:61:3:69:4 | (statement expression) | Statement expressions are a compiler extension and are not portable to other compilers. | +| test.c:96:3:96:18 | call to __builtin_setjmp | Call to builtin function '__builtin_setjmp' is a compiler extension and is not portable to other compilers. | +| test.c:97:3:97:19 | call to __builtin_longjmp | Call to builtin function '__builtin_longjmp' is a compiler extension and is not portable to other compilers. | +| test.c:113:11:113:16 | ... ? ... : ... | Ternaries with omitted middle operands are a compiler extension and is not portable to other compilers. | +| test.c:124:12:124:12 | definition of a | 128-bit integers are a compiler extension and are not portable to other compilers. | +| test.c:128:17:128:17 | definition of a | Double-Word integers are a compiler extension and are not portable to other compilers. | +| test.c:165:8:165:15 | definition of contents | Zero length arrays are a compiler extension and are not portable to other compilers. | +| test.c:182:8:182:11 | gf19 | Empty structures are a compiler extension and are not portable to other compilers. | +| test.c:216:9:216:10 | definition of x1 | Zero length arrays are a compiler extension and are not portable to other compilers. | +| test.c:266:16:266:21 | access | Use of attribute 'access' is a compiler extension and is not portable to other compilers. | +| test.c:269:27:269:31 | alias | Use of attribute 'alias' is a compiler extension and is not portable to other compilers. | +| test.c:272:23:272:29 | aligned | Use of attribute 'aligned' is a compiler extension and is not portable to other compilers. | +| test.c:283:25:283:34 | deprecated | Use of attribute 'deprecated' is a compiler extension and is not portable to other compilers. | +| test.c:295:20:295:30 | fallthrough | Use of attribute 'fallthrough' is a compiler extension and is not portable to other compilers. | +| test.c:319:3:319:22 | alignof() | '__alignof__' is a compiler extension and is not portable to other compilers. | +| test.c:338:3:338:31 | call to __builtin_extract_return_addr | Call to builtin function '__builtin_extract_return_addr' is a compiler extension and is not portable to other compilers. | +| test.c:339:3:339:28 | call to __builtin_frob_return_addr | Call to builtin function '__builtin_frob_return_addr' is a compiler extension and is not portable to other compilers. | +| test.c:340:3:340:25 | call to __builtin_frame_address | Call to builtin function '__builtin_frame_address' is a compiler extension and is not portable to other compilers. | +| test.c:361:3:361:22 | call to __sync_fetch_and_add_4 | Call to builtin function '__sync_fetch_and_add_4' is a compiler extension and is not portable to other compilers. | +| test.c:362:3:362:22 | call to __sync_fetch_and_sub_4 | Call to builtin function '__sync_fetch_and_sub_4' is a compiler extension and is not portable to other compilers. | +| test.c:363:3:363:21 | call to __sync_fetch_and_or_4 | Call to builtin function '__sync_fetch_and_or_4' is a compiler extension and is not portable to other compilers. | +| test.c:364:3:364:22 | call to __sync_fetch_and_and_4 | Call to builtin function '__sync_fetch_and_and_4' is a compiler extension and is not portable to other compilers. | +| test.c:365:3:365:22 | call to __sync_fetch_and_xor_4 | Call to builtin function '__sync_fetch_and_xor_4' is a compiler extension and is not portable to other compilers. | +| test.c:366:3:366:23 | call to __sync_fetch_and_nand_4 | Call to builtin function '__sync_fetch_and_nand_4' is a compiler extension and is not portable to other compilers. | +| test.c:367:3:367:22 | call to __sync_add_and_fetch_4 | Call to builtin function '__sync_add_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:368:3:368:22 | call to __sync_sub_and_fetch_4 | Call to builtin function '__sync_sub_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:369:3:369:21 | call to __sync_or_and_fetch_4 | Call to builtin function '__sync_or_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:370:3:370:22 | call to __sync_and_and_fetch_4 | Call to builtin function '__sync_and_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:371:3:371:22 | call to __sync_xor_and_fetch_4 | Call to builtin function '__sync_xor_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:372:3:372:23 | call to __sync_nand_and_fetch_4 | Call to builtin function '__sync_nand_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:374:3:374:30 | call to __sync_bool_compare_and_swap_4 | Call to builtin function '__sync_bool_compare_and_swap_4' is a compiler extension and is not portable to other compilers. | +| test.c:375:3:375:29 | call to __sync_val_compare_and_swap_4 | Call to builtin function '__sync_val_compare_and_swap_4' is a compiler extension and is not portable to other compilers. | +| test.c:376:3:376:26 | call to __sync_lock_test_and_set_4 | Call to builtin function '__sync_lock_test_and_set_4' is a compiler extension and is not portable to other compilers. | +| test.c:377:3:377:21 | call to __sync_lock_release_4 | Call to builtin function '__sync_lock_release_4' is a compiler extension and is not portable to other compilers. | +| test.c:405:3:405:18 | call to __builtin_alloca | Call to builtin function '__builtin_alloca' is a compiler extension and is not portable to other compilers. | +| test.c:409:1:411:8 | #define BUILTIN __builtin_alloca( 0) | Call to builtin function '__builtin_alloca' is a compiler extension and is not portable to other compilers. | diff --git a/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.qlref b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.qlref new file mode 100644 index 0000000000..965c95be2c --- /dev/null +++ b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.qlref @@ -0,0 +1 @@ +rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-2/options b/c/misra/test/rules/RULE-1-2/options new file mode 100644 index 0000000000..ea7b68755d --- /dev/null +++ b/c/misra/test/rules/RULE-1-2/options @@ -0,0 +1 @@ +semmle-extractor-options:--clang -fhonor-infinity -std=c11 --edg --diag_error=implicit_func_decl -nostdinc -I../../../../common/test/includes/standard-library diff --git a/c/misra/test/rules/RULE-1-2/test.c b/c/misra/test/rules/RULE-1-2/test.c new file mode 100644 index 0000000000..439df3733c --- /dev/null +++ b/c/misra/test/rules/RULE-1-2/test.c @@ -0,0 +1,413 @@ +#include +#include +// Note: Clang aims to support both clang and gcc extensions. +// This test case has been designed using lists compiled from: +// - https://clang.llvm.org/docs/LanguageExtensions.html +// - https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins +#ifdef __has_builtin // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_constexpr_builtin // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_feature // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_extension // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_c_attribute // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_attribute // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_declspec_attribute // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __is_identifier // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_include // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_include_next // NON_COMPLIANT[FALSE_NEGATIVE] +#endif +#ifdef __has_warning // NON_COMPLIANT[FALSE_NEGATIVE] +#endif + +// Reference: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros +#define A __BASE_FILE__ // NON_COMPLIANT +#define B __FILE_NAME__ // NON_COMPLIANT +#define C __COUNTER__ // NON_COMPLIANT +#define D __INCLUDE_LEVEL__ // NON_COMPLIANT +#define E__TIMESTAMP__ // NON_COMPLIANT +#define F __clang__ // NON_COMPLIANT +#define G __clang_major__ // NON_COMPLIANT +#define H __clang_minor__ // NON_COMPLIANT +#define I __clang_patchlevel__ // NON_COMPLIANT +#define J __clang_version__ // NON_COMPLIANT +#define K __clang_literal_encoding__ // NON_COMPLIANT +#define L __clang_wide_literal_encoding__ // NON_COMPLIANT + +// Requires additional compiler flags to change the architecture +// typedef __attribute__((neon_vector_type(8))) int8_t int8x8_t; +// typedef __attribute__((neon_polyvector_type(16))) poly8_t poly8x16_t; + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes +typedef int int4 __attribute__((vector_size(4 * sizeof(int)))); // NON_COMPLIANT +typedef int v4si __attribute__((__vector_size__(16))); // NON_COMPLIANT +typedef float float4 __attribute__((ext_vector_type(4))); // NON_COMPLIANT +typedef float float2 __attribute__((ext_vector_type(2))); // NON_COMPLIANT + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs +void gf1() { + ({ // NON_COMPLIANT + int y = 1; + int z; + if (y > 0) + z = y; + else + z = -y; + z; + }); +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Local-Labels.html#Local-Labels +void gf2() { + // __label__ found; // NON_COMPLIANT[FALSE_NEGATIVE] -- local labels not + // supported by clang +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html#Labels-as-Values +void gf3() { + void *ptr; + // goto *ptr; // NON_COMPLIANT[FALSE_NEGATIVE] -- not supported in clang +} + +// Referfence: +// https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html#Nested-Functions +void gf4() { + // void gf4a(){ // NON_COMPLIANT[FALSE_NEGATIVE] -- not supported in clang + // + // } +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Nonlocal-Gotos.html#Nonlocal-Gotos +void gf5() { + __builtin_setjmp(0); // NON_COMPLIANT + __builtin_longjmp(0, 1); // NON_COMPLIANT +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Constructing-Calls.html#Constructing-Calls +void gf6() { + // not supported by clang + //__builtin_apply_args(); // NON_COMPLIANT[FALSE_NEGATIVE] + //__builtin_apply(0, 0, 0); // NON_COMPLIANT[FALSE_NEGATIVE] + //__builtin_return(0); // NON_COMPLIANT[FALSE_NEGATIVE] + //__builtin_va_arg_pack(); // NON_COMPLIANT[FALSE_NEGATIVE] + //__builtin_va_arg_pack_len(); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Conditionals.html#Conditionals +void gf7() { + int a = 0 ?: 0; // NON_COMPLIANT +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof +void gf8() { // not supported by qcc gcc and clang + // typeof(int *); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html#g_t_005f_005fint128 +void gf9() { + __int128 a; // NON_COMPLIANT +} +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Long-Long.html#Long-Long +void gf10() { + long long int a; // NON_COMPLIANT +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Complex.html#Complex +void gf11() { + __real__(0); // NON_COMPLIANT[FALSE_NEGATIVE] + __imag__(0); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +void gf12() {} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Floating-Types.html#Floating-Types +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Decimal-Float.html#Decimal-Float +void gf13() { + // not supported on clang + //_Decimal32 a; // NON_COMPLIANT[FALSE_NEGATIVE] + //_Decimal64 b; // NON_COMPLIANT[FALSE_NEGATIVE] + //_Decimal128 c; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Complex.html#Complex +void gf14() { + // Do not work in clang + // typedef _Complex float __attribute__((mode(TC))) _Complex128; // + // NON_COMPLIANT[FALSE_NEGATIVE] typedef _Complex float + // __attribute__((mode(XC))) _Complex80; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Hex-Floats.html#Hex-Floats +void gf15() { + float f = 0x1.fp3; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html#Zero-Length +void gf16() { + char contents[0]; // NON_COMPLIANT +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#Named-Address-Spaces +void gf17() { + // const __flash char ** p; // NON_COMPLIANT[FALSE_NEGATIVE] -- not supported + // in clang +} + +void gf18() { + // not supported by extractor - checked by looking for flags. + + // short _Fract, _Fract; // NON_COMPLIANT[FALSE_NEGATIVE] - + // long _Fract; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +struct gf19 {}; // NON_COMPLIANT + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html#Variable-Length +void gf20(int n) { + // struct S { int x[n]; }; // NON_COMPLIANT[FALSE_NEGATIVE] - will never be + // supported in clang +} +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html#Variadic-Macros +#define gf21(format, args...) \ + printf(format, args) // NON_COMPLIANT[FALSE_NEGATIVE] -- note + // the issue here is explicitly naming the arguments. +#define gf21a(format, ...) printf(format, __VA_ARGS__) // COMPLIANT + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Escaped-Newlines.html#Escaped-Newlines +#define gf22 \ + "a" \ + \ +"b" // NON_COMPLIANT[FALSE_NEGATIVE] - additional spaces after a backslash -- + // stripped by extractor +#define gf22a \ + "a" \ + "b" // COMPLIANT + +void gf24(int f, int g) { + float beat_freqs[2] = {f - g, f + g}; // NON_COMPLIANT +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html#Variable-Length +void gf25(int n) { + struct S1 { + int x1[n]; // NON_COMPLIANT + int x2[5]; // COMPLIANT + int x3[]; // COMPLIANT + }; +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Compound-Literals.html#Compound-Literals +struct gf26t { + int a; + char b[2]; +} gf26v; +void gf26(int x, int y) { + gf26v = ((struct gf26t){ + x + y, 'z', 0}); // NON_COMPLIANT[FALSE_NEGATIVE] - compound literal +} +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html#Case-Ranges +void gf28() { + int a; + + // switch(a){ + // case: 0 ... 5: // NON_COMPLIANT[FALSE_NEGATIVE] - Not supported in + // clang. + // ;; + // break; + // default: + // ;; + // break; + // } +} + +union gf29u { + int i; + double j; +}; + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Cast-to-Union.html#Cast-to-Union +void gf29() { + int x; + int y; + union gf29u z; + z = (union gf29u)x; // NON_COMPLIANT[FALSE_NEGATIVE] + z = (union gf29u)y; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function-Attributes +__attribute__((access(read_only, 1))) int +gf30(const char *); // NON_COMPLIANT -- attributes are not portable. + +extern int __attribute__((alias("var_target"))) +gf31; // NON_COMPLIANT -- attributes are not portable. + +struct __attribute__((aligned(8))) gf32 { + short f[3]; +}; // NON_COMPLIANT -- attributes are not portable. + +void gf33() { +gf33l: + __attribute__((cold, unused)); // NON_COMPLIANT + return; +} + +enum gf34 { + oldval __attribute__((deprecated)), // NON_COMPLIANT + newval +}; + +void gf35() { + int x; + // __attribute__((assume(x == 42))); // NON_COMPLIANT[FALSE_NEGATIVE] - Not + // supported in clang + + switch (x) { + case 1: + printf(""); + __attribute__((fallthrough)); // NON_COMPLIANT + case 2: + break; + } +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html#Dollar-Signs +void gf37() { + int a$1; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Character-Escapes.html#Character-Escapes +void gf38() { + const char *c = "test\e"; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +struct gf39s { + int x; + char y; +} gf39v; + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Alignment.html#Alignment +void gf39() { + __alignof__(gf39v.x); // NON_COMPLIANT +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Incomplete-Enums.html#Incomplete-Enums +// enum gf40 {}; // NON_COMPLIANT[FALSE_NEGATIVE] - not supported in clang + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html#Function-Names +void gf41() { + printf("__FUNCTION__ = %s\n", __FUNCTION__); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("__PRETTY_FUNCTION__ = %s\n", + __PRETTY_FUNCTION__); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins +void gf42() { + __builtin_extract_return_addr(0); // NON_COMPLIANT + __builtin_frob_return_addr(0); // NON_COMPLIANT + __builtin_frame_address(0); // NON_COMPLIANT +} + +struct gf43s { + int x; + char y; +} gf43v; + +void gf43() { + __builtin_offsetof(struct gf43s, x); // NON_COMPLIANT +} + +struct gf44s { + int x; + char y; +} gf44v; + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html#g_t_005f_005fsync-Builtins +void gf44() { + int i; + __sync_fetch_and_add(&i, 0); // NON_COMPLIANT + __sync_fetch_and_sub(&i, 0); // NON_COMPLIANT + __sync_fetch_and_or(&i, 0); // NON_COMPLIANT + __sync_fetch_and_and(&i, 0); // NON_COMPLIANT + __sync_fetch_and_xor(&i, 0); // NON_COMPLIANT + __sync_fetch_and_nand(&i, 0); // NON_COMPLIANT + __sync_add_and_fetch(&i, 0); // NON_COMPLIANT + __sync_sub_and_fetch(&i, 0); // NON_COMPLIANT + __sync_or_and_fetch(&i, 0); // NON_COMPLIANT + __sync_and_and_fetch(&i, 0); // NON_COMPLIANT + __sync_xor_and_fetch(&i, 0); // NON_COMPLIANT + __sync_nand_and_fetch(&i, 0); // NON_COMPLIANT + + __sync_bool_compare_and_swap(&i, 0, 0); + __sync_val_compare_and_swap(&i, 0, 0); + __sync_lock_test_and_set(&i, 0, 0); + __sync_lock_release(&i, 0); +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html#Binary-constants +void gf45() { + int i = 0b101010; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +// Reference: https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html#Thread-Local +__thread int gf46; // NON_COMPLIANT[FALSE_NEGATIVE] + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html#Unnamed-Fields +void gf47() { // NON_COMPLIANT in versions < C11. + struct { + int a; + union { + int b; + float c; + }; + int d; + } f; +} + +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins +void gf48() { + __builtin_alloca( + 0); // NON_COMPLIANT (all __builtin functions are non-compliant.) +} + +#define BUILTIN \ + __builtin_alloca( \ + 0) // NON_COMPLIANT (all __builtin functions are non-compliant.) + +void gf49() { BUILTIN; } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.expected b/c/misra/test/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.expected new file mode 100644 index 0000000000..1e57f92e4a --- /dev/null +++ b/c/misra/test/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.expected @@ -0,0 +1,8 @@ +| test.c:4:6:4:38 | ____codeql_coding_standards_main1 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:8:5:8:37 | ____codeql_coding_standards_main2 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:27:5:27:37 | ____codeql_coding_standards_main6 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:32:6:32:38 | ____codeql_coding_standards_main7 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:36:5:36:37 | ____codeql_coding_standards_main8 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:40:5:40:37 | ____codeql_coding_standards_main9 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:44:5:44:38 | ____codeql_coding_standards_main10 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | +| test.c:48:5:48:38 | ____codeql_coding_standards_main11 | main function may trigger undefined behavior because it is not in one of the formats specified by the C standard. | diff --git a/c/misra/test/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.qlref b/c/misra/test/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.qlref new file mode 100644 index 0000000000..b579db05b1 --- /dev/null +++ b/c/misra/test/rules/RULE-1-3/OccurrenceOfUndefinedBehavior.qlref @@ -0,0 +1 @@ +rules/RULE-1-3/OccurrenceOfUndefinedBehavior.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-3/test.c b/c/misra/test/rules/RULE-1-3/test.c new file mode 100644 index 0000000000..fd54959f56 --- /dev/null +++ b/c/misra/test/rules/RULE-1-3/test.c @@ -0,0 +1,50 @@ +int main(void) { // COMPLIANT +} + +void ____codeql_coding_standards_main1(void) { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main2() { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main3(int argc, char **argv) { // COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main4(int argc, char argv[][]) { // COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main5(int argc, char *argv[]) { // COMPLIANT + return 0; +} + +typedef int MY_INT; +typedef char *MY_CHAR_PTR; + +int ____codeql_coding_standards_main6(MY_INT argc, + MY_CHAR_PTR argv[]) { // COMPLIANT + return 0; +} + +void ____codeql_coding_standards_main7(char *argc, + char **argv) { // NON_COMPLIANT +} + +int ____codeql_coding_standards_main8(int argc, char *argv) { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main9() { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main10(int argc, int *argv) { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_main11(int argc, int **argv) { // NON_COMPLIANT + return 0; +} diff --git a/c/misra/test/rules/RULE-1-3/test.c.clang b/c/misra/test/rules/RULE-1-3/test.c.clang new file mode 100644 index 0000000000..380a0cb512 --- /dev/null +++ b/c/misra/test/rules/RULE-1-3/test.c.clang @@ -0,0 +1,25 @@ +// void main(void) { // COMPLIANT does not compile in clang +// } + +int ____codeql_coding_standards_m1(int argc, char **argv) { // NON_COMPLIANT + return 0; +} + +void ____codeql_coding_standards_m2(char *argc, char **argv) { // NON_COMPLIANT +} + +int ____codeql_coding_standards_m3(int argc, char *argv) { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_m4() { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_m5(int argc, int *argv) { // NON_COMPLIANT + return 0; +} + +int ____codeql_coding_standards_m6(int argc, int **argv) { // NON_COMPLIANT + return 0; +} diff --git a/c/misra/test/rules/RULE-1-4/EmergentLanguageFeaturesUsed.expected b/c/misra/test/rules/RULE-1-4/EmergentLanguageFeaturesUsed.expected new file mode 100644 index 0000000000..3f63a6c26c --- /dev/null +++ b/c/misra/test/rules/RULE-1-4/EmergentLanguageFeaturesUsed.expected @@ -0,0 +1 @@ +| test.c:7:1:7:32 | #define __STDC_WANT_LIB_EXT1__ 1 | Usage of emergent language feature. | diff --git a/c/misra/test/rules/RULE-1-4/EmergentLanguageFeaturesUsed.qlref b/c/misra/test/rules/RULE-1-4/EmergentLanguageFeaturesUsed.qlref new file mode 100644 index 0000000000..6e3737f029 --- /dev/null +++ b/c/misra/test/rules/RULE-1-4/EmergentLanguageFeaturesUsed.qlref @@ -0,0 +1 @@ +rules/RULE-1-4/EmergentLanguageFeaturesUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-4/test.c b/c/misra/test/rules/RULE-1-4/test.c new file mode 100644 index 0000000000..5bea219b54 --- /dev/null +++ b/c/misra/test/rules/RULE-1-4/test.c @@ -0,0 +1,26 @@ +#include //COMPLIANT +#include //COMPLIANT +#include //COMPLIANT +#include //COMPLIANT + +#define MACRO(x) _Generic((x), int : 0, long : 1) // COMPLIANT +#define __STDC_WANT_LIB_EXT1__ 1 // NON_COMPLIANT + +_Noreturn void f0(); // COMPLIANT + +typedef int new_type; // COMPLIANT +typedef _Atomic new_type atomic_new_type; // COMPLIANT + +void f(int p) { + int i0 = _Generic(p, int : 0, long : 1); // COMPLIANT + + _Atomic int i; // NON-COMPLIANT + + _Alignas(4) int i1; // COMPLIANT + alignas(4) int i2; // COMPLIANT + int a = _Alignof(int); // COMPLIANT + int a1 = alignof(int); // COMPLIANT + + static thread_local int i3; // COMPLIANT + static _Thread_local int i4; // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/CallToObsolescentFunctionGets.expected b/c/misra/test/rules/RULE-1-5/CallToObsolescentFunctionGets.expected new file mode 100644 index 0000000000..4c8fdc27cf --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/CallToObsolescentFunctionGets.expected @@ -0,0 +1 @@ +| test.c:37:3:37:6 | call to gets | Call to obsolescent function 'gets'. | diff --git a/c/misra/test/rules/RULE-1-5/CallToObsolescentFunctionGets.qlref b/c/misra/test/rules/RULE-1-5/CallToObsolescentFunctionGets.qlref new file mode 100644 index 0000000000..1a2ec096cf --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/CallToObsolescentFunctionGets.qlref @@ -0,0 +1 @@ +rules/RULE-1-5/CallToObsolescentFunctionGets.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/FunctionTypesNotInPrototypeFormObsolete.testref b/c/misra/test/rules/RULE-1-5/FunctionTypesNotInPrototypeFormObsolete.testref new file mode 100644 index 0000000000..1a6a69fc24 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/FunctionTypesNotInPrototypeFormObsolete.testref @@ -0,0 +1 @@ +c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.expected b/c/misra/test/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.expected new file mode 100644 index 0000000000..854b200553 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.expected @@ -0,0 +1,6 @@ +| test.c:22:1:22:14 | #define true 3 | Invalid define of boolean standard macro 'true'. | +| test.c:23:1:23:15 | #define false 3 | Invalid define of boolean standard macro 'false'. | +| test.c:24:1:24:18 | #define bool int * | Invalid define of boolean standard macro 'bool'. | +| test.c:25:1:25:11 | #undef true | Invalid undefine of boolean standard macro 'true'. | +| test.c:26:1:26:12 | #undef false | Invalid undefine of boolean standard macro 'false'. | +| test.c:27:1:27:11 | #undef bool | Invalid undefine of boolean standard macro 'bool'. | diff --git a/c/misra/test/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.qlref b/c/misra/test/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.qlref new file mode 100644 index 0000000000..5b112609cc --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.qlref @@ -0,0 +1 @@ +rules/RULE-1-5/InvalidDefineOrUndefOfStdBoolMacro.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/MissingStaticSpecifierFuncRedeclarationObsolete.testref b/c/misra/test/rules/RULE-1-5/MissingStaticSpecifierFuncRedeclarationObsolete.testref new file mode 100644 index 0000000000..7d9f2ebc04 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/MissingStaticSpecifierFuncRedeclarationObsolete.testref @@ -0,0 +1 @@ +c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/MissingStaticSpecifierObjectRedeclarationObsolete.testref b/c/misra/test/rules/RULE-1-5/MissingStaticSpecifierObjectRedeclarationObsolete.testref new file mode 100644 index 0000000000..23ed7c9fc5 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/MissingStaticSpecifierObjectRedeclarationObsolete.testref @@ -0,0 +1 @@ +c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/SizeInReallocCallIsZero.expected b/c/misra/test/rules/RULE-1-5/SizeInReallocCallIsZero.expected new file mode 100644 index 0000000000..7b05a5fc0a --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/SizeInReallocCallIsZero.expected @@ -0,0 +1 @@ +| test.c:14:3:14:9 | call to realloc | Size argument '$@' may equal zero in realloc call, resulting in obsolescent and/or implementation-defined behavior. | test.c:14:14:14:14 | 0 | 0 | diff --git a/c/misra/test/rules/RULE-1-5/SizeInReallocCallIsZero.qlref b/c/misra/test/rules/RULE-1-5/SizeInReallocCallIsZero.qlref new file mode 100644 index 0000000000..cef5e76d54 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/SizeInReallocCallIsZero.qlref @@ -0,0 +1 @@ +rules/RULE-1-5/SizeInReallocCallIsZero.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/SizeInReallocCallMayBeZero.expected b/c/misra/test/rules/RULE-1-5/SizeInReallocCallMayBeZero.expected new file mode 100644 index 0000000000..f86ad4c57c --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/SizeInReallocCallMayBeZero.expected @@ -0,0 +1 @@ +| test.c:15:3:15:9 | call to realloc | Size argument '$@' equals zero in realloc call, resulting in obsolescent and/or implementation-defined behavior. | test.c:15:14:15:15 | p0 | p0 | diff --git a/c/misra/test/rules/RULE-1-5/SizeInReallocCallMayBeZero.qlref b/c/misra/test/rules/RULE-1-5/SizeInReallocCallMayBeZero.qlref new file mode 100644 index 0000000000..1287327c5d --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/SizeInReallocCallMayBeZero.qlref @@ -0,0 +1 @@ +rules/RULE-1-5/SizeInReallocCallMayBeZero.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/UngetcCallOnStreamPositionZero.expected b/c/misra/test/rules/RULE-1-5/UngetcCallOnStreamPositionZero.expected new file mode 100644 index 0000000000..fb8d44ea19 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/UngetcCallOnStreamPositionZero.expected @@ -0,0 +1,10 @@ +edges +| test.c:39:16:39:20 | *call to fopen | test.c:39:16:39:20 | *call to fopen | provenance | | +| test.c:39:16:39:20 | *call to fopen | test.c:41:15:41:18 | *file | provenance | | +nodes +| test.c:39:16:39:20 | *call to fopen | semmle.label | *call to fopen | +| test.c:39:16:39:20 | *call to fopen | semmle.label | *call to fopen | +| test.c:41:15:41:18 | *file | semmle.label | *file | +subpaths +#select +| test.c:41:15:41:18 | *file | test.c:39:16:39:20 | *call to fopen | test.c:41:15:41:18 | *file | Obsolescent call to ungetc on file stream $@ at position zero. | test.c:39:16:39:20 | *call to fopen | *call to fopen | diff --git a/c/misra/test/rules/RULE-1-5/UngetcCallOnStreamPositionZero.qlref b/c/misra/test/rules/RULE-1-5/UngetcCallOnStreamPositionZero.qlref new file mode 100644 index 0000000000..8c28919dcb --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/UngetcCallOnStreamPositionZero.qlref @@ -0,0 +1 @@ +rules/RULE-1-5/UngetcCallOnStreamPositionZero.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.expected b/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.expected new file mode 100644 index 0000000000..edd607c52f --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.expected @@ -0,0 +1 @@ +| test.c:29:18:29:36 | ATOMIC_VAR_INIT(value) | Usage of macro ATOMIC_VAR_INIT() is declared obscelescent in C18, and discouraged in earlier C versions. | diff --git a/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.expected.gcc b/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.expected.gcc new file mode 100644 index 0000000000..cb8e72ff0f --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.expected.gcc @@ -0,0 +1 @@ +| test.c:29:18:29:36 | ATOMIC_VAR_INIT(VALUE) | Usage of macro ATOMIC_VAR_INIT() is declared obscelescent in C18, and discouraged in earlier C versions. | diff --git a/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.qlref b/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.qlref new file mode 100644 index 0000000000..9a54fdc83a --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.qlref @@ -0,0 +1 @@ +rules/RULE-1-5/UseOfObsoleteMacroAtomicVarInit.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-5/test.c b/c/misra/test/rules/RULE-1-5/test.c new file mode 100644 index 0000000000..52144bad13 --- /dev/null +++ b/c/misra/test/rules/RULE-1-5/test.c @@ -0,0 +1,48 @@ +#include "stdatomic.h" +#include "stdbool.h" +#include "stdio.h" +#include "stdlib.h" + +void f1(int p0) { + // malloc() is not obsolete, though it is banned by Rule 21.3 + int *t = malloc(10); // COMPLIANT + + // Valid usage of realloc, but all use of realloc is banned by Rule 21.3 + realloc(t, 20); // NON-COMPLIANT + + // Obsolete usage of realloc. + realloc(t, 0); // NON-COMPLIANT + realloc(t, p0); // NON-COMPLIANT +} + +extern const int g1; // COMPLIANT +const extern int g2; // NON-COMPLIANT + +#define MY_TRUE 3 // COMPLIANT +#define true 3 // NON-COMPLIANT +#define false 3 // NON-COMPLIANT +#define bool int * // NON-COMPLIANT +#undef true // NON-COMPLIANT +#undef false // NON-COMPLIANT +#undef bool // NON-COMPLIANT + +_Atomic int g3 = ATOMIC_VAR_INIT(18); // NON-COMPLIANT +_Atomic int g4 = 18; // COMPLIANT + +// `gets` was removed from C11. +extern char *gets(FILE *stream); + +// Rule 21.6 covers the below cases: +void f6(void) { + gets(stdin); // NON_COMPLIANT + + FILE *file = fopen("", 0); + // Obsolete usage of ungetc. + ungetc('c', file); // NON-COMPLIANT + + char buf[10]; + fread(buf, sizeof(buf), 10, file); + // This is not an obsolete usage of ungetc, though ungetc isn't allowed by + // 21-3. + ungetc('c', file); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.expected b/c/misra/test/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.expected new file mode 100644 index 0000000000..7a8fd1e07c --- /dev/null +++ b/c/misra/test/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.expected @@ -0,0 +1,209 @@ +| test.c:15:5:15:5 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:16:5:16:5 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:23:4:23:4 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:24:4:24:4 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:25:4:25:5 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:31:4:31:4 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:32:4:32:4 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:33:4:33:5 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:35:4:35:4 | u | Operand of essentially Unsigned type will be converted to a signed type with the signedness dependent on the implemented size of int. | +| test.c:39:7:39:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:41:7:41:8 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:47:7:47:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:49:7:49:8 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:55:3:55:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:57:3:57:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:63:3:63:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:65:3:65:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:71:3:71:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:73:3:73:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:77:3:77:4 | cf | Use of essentially Complex type in this way is a constraint violation. | +| test.c:79:3:79:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:81:3:81:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:85:3:85:4 | cf | Use of essentially Complex type in this way is a constraint violation. | +| test.c:87:5:87:5 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:89:5:89:6 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:93:5:93:6 | cf | Use of essentially Complex type in this way is a constraint violation. | +| test.c:95:5:95:5 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:97:5:97:6 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:101:5:101:6 | cf | Use of essentially Complex type in this way is a constraint violation. | +| test.c:103:7:103:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:104:7:104:7 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:105:7:105:8 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:111:7:111:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:112:7:112:7 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:113:7:113:8 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:119:3:119:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:120:3:120:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:121:3:121:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:127:3:127:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:128:3:128:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:129:3:129:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:135:3:135:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:136:3:136:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:137:3:137:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:143:7:143:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:144:7:144:7 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:145:7:145:8 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:151:7:151:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:159:7:159:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:167:8:167:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:175:8:175:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:183:3:183:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:191:3:191:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:199:3:199:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:207:3:207:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:220:3:220:3 | f | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:221:3:221:4 | cf | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:234:3:234:3 | f | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:235:3:235:4 | cf | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:248:8:248:8 | f | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:249:8:249:9 | cf | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:262:8:262:8 | f | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:263:8:263:9 | cf | Floating point numbers have inherent error such that comparisons should consider precision and not exact equality. | +| test.c:272:4:272:4 | c | Operand of essentially Character type type interpreted as a Boolean value. | +| test.c:273:4:273:5 | e1 | Operand of essentially Enum Type type interpreted as a Boolean value. | +| test.c:274:4:274:4 | s | Operand of essentially Signed type type interpreted as a Boolean value. | +| test.c:275:4:275:4 | u | Operand of essentially Unsigned type type interpreted as a Boolean value. | +| test.c:276:4:276:4 | f | Operand of essentially Floating type type interpreted as a Boolean value. | +| test.c:277:4:277:5 | cf | Operand of essentially Complex Floating type type interpreted as a Boolean value. | +| test.c:280:3:280:3 | c | Operand of essentially Character type type interpreted as a Boolean value. | +| test.c:281:3:281:4 | e1 | Operand of essentially Enum Type type interpreted as a Boolean value. | +| test.c:282:3:282:3 | s | Operand of essentially Signed type type interpreted as a Boolean value. | +| test.c:283:3:283:3 | u | Operand of essentially Unsigned type type interpreted as a Boolean value. | +| test.c:284:3:284:3 | f | Operand of essentially Floating type type interpreted as a Boolean value. | +| test.c:285:3:285:4 | cf | Operand of essentially Complex Floating type type interpreted as a Boolean value. | +| test.c:288:3:288:3 | c | Operand of essentially Character type type interpreted as a Boolean value. | +| test.c:289:3:289:4 | e1 | Operand of essentially Enum Type type interpreted as a Boolean value. | +| test.c:290:3:290:3 | s | Operand of essentially Signed type type interpreted as a Boolean value. | +| test.c:291:3:291:3 | u | Operand of essentially Unsigned type type interpreted as a Boolean value. | +| test.c:292:3:292:3 | f | Operand of essentially Floating type type interpreted as a Boolean value. | +| test.c:293:3:293:4 | cf | Operand of essentially Complex Floating type type interpreted as a Boolean value. | +| test.c:296:11:296:11 | c | Operand of essentially Character type type interpreted as a Boolean value. | +| test.c:297:11:297:12 | e1 | Operand of essentially Enum Type type interpreted as a Boolean value. | +| test.c:298:11:298:11 | s | Operand of essentially Signed type type interpreted as a Boolean value. | +| test.c:299:11:299:11 | u | Operand of essentially Unsigned type type interpreted as a Boolean value. | +| test.c:300:11:300:11 | f | Operand of essentially Floating type type interpreted as a Boolean value. | +| test.c:301:11:301:12 | cf | Operand of essentially Complex Floating type type interpreted as a Boolean value. | +| test.c:304:12:304:12 | c | Operand of essentially Character type type interpreted as a Boolean value. | +| test.c:305:12:305:13 | e1 | Operand of essentially Enum Type type interpreted as a Boolean value. | +| test.c:306:12:306:12 | s | Operand of essentially Signed type type interpreted as a Boolean value. | +| test.c:307:12:307:12 | u | Operand of essentially Unsigned type type interpreted as a Boolean value. | +| test.c:308:12:308:12 | f | Operand of essentially Floating type type interpreted as a Boolean value. | +| test.c:309:12:309:13 | cf | Operand of essentially Complex Floating type type interpreted as a Boolean value. | +| test.c:311:3:311:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:312:3:312:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:313:3:313:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:314:3:314:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:319:3:319:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:320:3:320:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:321:3:321:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:322:3:322:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:327:8:327:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:328:8:328:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:329:8:329:9 | e1 | Right hand operand of shift operator is essentially Enum Type and not not essentially unsigned. | +| test.c:330:8:330:8 | s | Right hand operand of shift operator is essentially Signed type and not not essentially unsigned. | +| test.c:335:8:335:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:336:8:336:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:337:8:337:9 | e1 | Right hand operand of shift operator is essentially Enum Type and not not essentially unsigned. | +| test.c:338:8:338:8 | s | Right hand operand of shift operator is essentially Signed type and not not essentially unsigned. | +| test.c:343:3:343:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:344:3:344:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:345:3:345:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:346:3:346:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:351:3:351:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:352:3:352:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:353:3:353:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:354:3:354:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:359:3:359:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:360:3:360:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:361:3:361:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:362:3:362:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:367:6:367:6 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:368:6:368:6 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:369:6:369:7 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:370:6:370:6 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:375:7:375:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:376:7:376:7 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:377:7:377:8 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:378:7:378:7 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:383:7:383:7 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:384:7:384:7 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:385:7:385:8 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:386:7:386:7 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:391:4:391:4 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:392:4:392:4 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:393:4:393:5 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:394:4:394:4 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:400:3:400:3 | c | Operand of essentially Character type type interpreted as a Boolean value. | +| test.c:401:3:401:4 | e1 | Operand of essentially Enum Type type interpreted as a Boolean value. | +| test.c:402:3:402:3 | s | Operand of essentially Signed type type interpreted as a Boolean value. | +| test.c:403:3:403:3 | u | Operand of essentially Unsigned type type interpreted as a Boolean value. | +| test.c:404:3:404:3 | f | Operand of essentially Floating type type interpreted as a Boolean value. | +| test.c:405:3:405:4 | cf | Operand of essentially Complex Floating type type interpreted as a Boolean value. | +| test.c:415:3:415:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:417:3:417:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:423:3:423:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:425:3:425:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:431:8:431:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:433:8:433:9 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:439:8:439:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:441:8:441:9 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:447:3:447:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:448:3:448:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:449:3:449:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:455:3:455:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:456:3:456:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:457:3:457:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:463:8:463:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:464:8:464:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:465:8:465:9 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:471:8:471:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:472:8:472:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:473:8:473:9 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:479:3:479:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:480:3:480:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:481:3:481:4 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:487:8:487:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:488:8:488:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:489:8:489:9 | e1 | Operand of essentially Enum type used in arithmetic operation, but has an implementation defined integer type. | +| test.c:495:3:495:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:496:3:496:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:497:3:497:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:498:3:498:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:503:3:503:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:504:3:504:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:505:3:505:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:506:3:506:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:511:9:511:9 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:512:9:512:9 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:513:9:513:10 | e1 | Right hand operand of shift operator is essentially Enum Type and not not essentially unsigned. | +| test.c:514:9:514:9 | s | Right hand operand of shift operator is essentially Signed type and not not essentially unsigned. | +| test.c:519:9:519:9 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:520:9:520:9 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:521:9:521:10 | e1 | Right hand operand of shift operator is essentially Enum Type and not not essentially unsigned. | +| test.c:522:9:522:9 | s | Right hand operand of shift operator is essentially Signed type and not not essentially unsigned. | +| test.c:527:3:527:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:528:3:528:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:529:3:529:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:530:3:530:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:535:3:535:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:536:3:536:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:537:3:537:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:538:3:538:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:543:3:543:3 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:544:3:544:3 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:545:3:545:4 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:546:3:546:3 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:551:8:551:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:552:8:552:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:553:8:553:9 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:554:8:554:8 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:559:8:559:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:560:8:560:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:561:8:561:9 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:562:8:562:8 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | +| test.c:567:8:567:8 | b | Operand of essentially Boolean type interpreted as a numeric value. | +| test.c:568:8:568:8 | c | Operand of essentially Charater type interpreted as a numeric value. | +| test.c:569:8:569:9 | e1 | Bitwise operator applied to operand of essentially Enum Type and not essentially unsigned. | +| test.c:570:8:570:8 | s | Bitwise operator applied to operand of essentially Signed type and not essentially unsigned. | diff --git a/c/misra/test/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.qlref b/c/misra/test/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.qlref new file mode 100644 index 0000000000..f3120fd81f --- /dev/null +++ b/c/misra/test/rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.qlref @@ -0,0 +1 @@ +rules/RULE-10-1/OperandsOfAnInappropriateEssentialType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-1/PointerTypeOnLogicalOperator.expected b/c/misra/test/rules/RULE-10-1/PointerTypeOnLogicalOperator.expected new file mode 100644 index 0000000000..34d2993389 --- /dev/null +++ b/c/misra/test/rules/RULE-10-1/PointerTypeOnLogicalOperator.expected @@ -0,0 +1,5 @@ +| test.c:581:4:581:4 | p | Logical operators should not be used with pointer types. | +| test.c:583:3:583:3 | p | Logical operators should not be used with pointer types. | +| test.c:584:7:584:7 | p | Logical operators should not be used with pointer types. | +| test.c:586:3:586:3 | p | Logical operators should not be used with pointer types. | +| test.c:587:8:587:8 | p | Logical operators should not be used with pointer types. | diff --git a/c/misra/test/rules/RULE-10-1/PointerTypeOnLogicalOperator.qlref b/c/misra/test/rules/RULE-10-1/PointerTypeOnLogicalOperator.qlref new file mode 100644 index 0000000000..cec5f51a3a --- /dev/null +++ b/c/misra/test/rules/RULE-10-1/PointerTypeOnLogicalOperator.qlref @@ -0,0 +1 @@ +rules/RULE-10-1/PointerTypeOnLogicalOperator.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-1/test.c b/c/misra/test/rules/RULE-10-1/test.c new file mode 100644 index 0000000000..3b96c7151d --- /dev/null +++ b/c/misra/test/rules/RULE-10-1/test.c @@ -0,0 +1,590 @@ +#include "math.h" +#include "stdbool.h" + +void testInappropriateOperands() { + _Bool b = true; + char c = 'c'; + enum E1 { A, B, C } e1 = A; + signed int s = 100; + unsigned int u = 1; + float f = 1.0; + float _Complex cf = 1.0 + 1.0i; + + int a[20]; + + a[b]; // NON_COMPLIANT + a[c]; // NON_COMPLIANT + a[e1]; // COMPLIANT + a[s]; // COMPLIANT + a[u]; // COMPLIANT + // a[f]; // NON_COMPILABLE + // a[cf]; // NON_COMPILABLE + + +b; // NON_COMPLIANT + +c; // NON_COMPLIANT + +e1; // NON_COMPLIANT + +s; // COMPLIANT + +u; // COMPLIANT + +f; // COMPLIANT + +cf; // COMPLIANT + + -b; // NON_COMPLIANT + -c; // NON_COMPLIANT + -e1; // NON_COMPLIANT + -s; // COMPLIANT + -u; // NON_COMPLIANT + -f; // COMPLIANT + -cf; // COMPLIANT + + 1 + b; // NON_COMPLIANT + 1 + c; // COMPLIANT + 1 + e1; // NON_COMPLIANT + 1 + s; // COMPLIANT + 1 + u; // COMPLIANT + 1 + f; // COMPLIANT + 1 + cf; // COMPLIANT + + 1 - b; // NON_COMPLIANT + 1 - c; // COMPLIANT + 1 - e1; // NON_COMPLIANT + 1 - s; // COMPLIANT + 1 - u; // COMPLIANT + 1 - f; // COMPLIANT + 1 - cf; // COMPLIANT + + b + 1; // NON_COMPLIANT + c + 1; // COMPLIANT + e1 + 1; // NON_COMPLIANT + s + 1; // COMPLIANT + u + 1; // COMPLIANT + f + 1; // COMPLIANT + cf + 1; // COMPLIANT + + b - 1; // NON_COMPLIANT + c - 1; // COMPLIANT + e1 - 1; // NON_COMPLIANT + s - 1; // COMPLIANT + u - 1; // COMPLIANT + f - 1; // COMPLIANT + cf - 1; // COMPLIANT + + b++; // NON_COMPLIANT + c++; // COMPLIANT + e1++; // NON_COMPLIANT + s++; // COMPLIANT + u++; // COMPLIANT + f++; // COMPLIANT + cf++; // NON_COMPLIANT + + b--; // NON_COMPLIANT + c--; // COMPLIANT + e1--; // NON_COMPLIANT + s--; // COMPLIANT + u--; // COMPLIANT + f--; // COMPLIANT + cf--; // NON_COMPLIANT + + ++b; // NON_COMPLIANT + ++c; // COMPLIANT + ++e1; // NON_COMPLIANT + ++s; // COMPLIANT + ++u; // COMPLIANT + ++f; // COMPLIANT + ++cf; // NON_COMPLIANT + + --b; // NON_COMPLIANT + --c; // COMPLIANT + --e1; // NON_COMPLIANT + --s; // COMPLIANT + --u; // COMPLIANT + --f; // COMPLIANT + --cf; // NON_COMPLIANT + + 1 * b; // NON_COMPLIANT + 1 * c; // NON_COMPLIANT + 1 * e1; // NON_COMPLIANT + 1 * s; // COMPLIANT + 1 * u; // COMPLIANT + 1 * f; // COMPLIANT + 1 * cf; // COMPLIANT + + 1 / b; // NON_COMPLIANT + 1 / c; // NON_COMPLIANT + 1 / e1; // NON_COMPLIANT + 1 / s; // COMPLIANT + 1 / u; // COMPLIANT + 1 / f; // COMPLIANT + 1 / cf; // COMPLIANT + + b * 1; // NON_COMPLIANT + c * 1; // NON_COMPLIANT + e1 * 1; // NON_COMPLIANT + s * 1; // COMPLIANT + u * 1; // COMPLIANT + f * 1; // COMPLIANT + cf * 1; // COMPLIANT + + b / 1; // NON_COMPLIANT + c / 1; // NON_COMPLIANT + e1 / 1; // NON_COMPLIANT + s / 1; // COMPLIANT + u / 1; // COMPLIANT + f / 1; // COMPLIANT + cf / 1; // COMPLIANT + + b % 1; // NON_COMPLIANT + c % 1; // NON_COMPLIANT + e1 % 1; // NON_COMPLIANT + s % 1; // COMPLIANT + u % 1; // COMPLIANT + // f % 1; // NON_COMPILABLE + // cf % 1; // NON_COMPILABLE + + 1 % b; // NON_COMPLIANT + 1 % c; // NON_COMPLIANT + 1 % e1; // NON_COMPLIANT + 1 % s; // COMPLIANT + 1 % u; // COMPLIANT + // 1 % f; // NON_COMPILABLE + // 1 % cf; // NON_COMPILABLE + + 1 < b; // NON_COMPLIANT + 1 < c; // COMPLIANT + 1 < e1; // COMPLIANT + 1 < s; // COMPLIANT + 1 < u; // COMPLIANT + 1 < f; // COMPLIANT + // 1 < cf; // NON_COMPILABLE + + 1 > b; // NON_COMPLIANT + 1 > c; // COMPLIANT + 1 > e1; // COMPLIANT + 1 > s; // COMPLIANT + 1 > u; // COMPLIANT + 1 > f; // COMPLIANT + // 1 > cf; // NON_COMPILABLE + + 1 <= b; // NON_COMPLIANT + 1 <= c; // COMPLIANT + 1 <= e1; // COMPLIANT + 1 <= s; // COMPLIANT + 1 <= u; // COMPLIANT + 1 <= f; // COMPLIANT + // 1 <= cf; // NON_COMPILABLE + + 1 >= b; // NON_COMPLIANT + 1 >= c; // COMPLIANT + 1 >= e1; // COMPLIANT + 1 >= s; // COMPLIANT + 1 >= u; // COMPLIANT + 1 >= f; // COMPLIANT + // 1 >= cf; // NON_COMPILABLE + + b < 1; // NON_COMPLIANT + c < 1; // COMPLIANT + e1 < 1; // COMPLIANT + s < 1; // COMPLIANT + u < 1; // COMPLIANT + f < 1; // COMPLIANT + // cf < 1; // NON_COMPILABLE + + b > 1; // NON_COMPLIANT + c > 1; // COMPLIANT + e1 > 1; // COMPLIANT + s > 1; // COMPLIANT + u > 1; // COMPLIANT + f > 1; // COMPLIANT + // cf > 1; // NON_COMPILABLE + + b <= 1; // NON_COMPLIANT + c <= 1; // COMPLIANT + e1 <= 1; // COMPLIANT + s <= 1; // COMPLIANT + u <= 1; // COMPLIANT + f <= 1; // COMPLIANT + // cf <= 1; // NON_COMPILABLE + + b >= 1; // NON_COMPLIANT + c >= 1; // COMPLIANT + e1 >= 1; // COMPLIANT + s >= 1; // COMPLIANT + u >= 1; // COMPLIANT + f >= 1; // COMPLIANT + // cf >= 1; // NON_COMPILABLE + + b == 1; // COMPLIANT + c == 1; // COMPLIANT + e1 == 1; // COMPLIANT + s == 1; // COMPLIANT + u == 1; // COMPLIANT + f == 1; // NON_COMPLIANT + cf == 1; // NON_COMPLIANT + f == 0; // COMPLIANT + f == INFINITY; // COMPLIANT + f == -INFINITY; // COMPLIANT + cf == 0; // COMPLIANT + cf == INFINITY; // COMPLIANT + cf == -INFINITY; // COMPLIANT + + b != 1; // COMPLIANT + c != 1; // COMPLIANT + e1 != 1; // COMPLIANT + s != 1; // COMPLIANT + u != 1; // COMPLIANT + f != 1; // NON_COMPLIANT + cf != 1; // NON_COMPLIANT + f != 0; // COMPLIANT + f != INFINITY; // COMPLIANT + f != -INFINITY; // COMPLIANT + cf != 0; // COMPLIANT + cf != INFINITY; // COMPLIANT + cf != -INFINITY; // COMPLIANT + + 1 == b; // COMPLIANT + 1 == c; // COMPLIANT + 1 == e1; // COMPLIANT + 1 == s; // COMPLIANT + 1 == u; // COMPLIANT + 1 == f; // NON_COMPLIANT + 1 == cf; // NON_COMPLIANT + 0 == f; // COMPLIANT + INFINITY == f; // COMPLIANT + -INFINITY == f; // COMPLIANT + 0 == cf; // COMPLIANT + INFINITY == cf; // COMPLIANT + -INFINITY == cf; // COMPLIANT + + 1 != b; // COMPLIANT + 1 != c; // COMPLIANT + 1 != e1; // COMPLIANT + 1 != s; // COMPLIANT + 1 != u; // COMPLIANT + 1 != f; // NON_COMPLIANT + 1 != cf; // NON_COMPLIANT + 0 != f; // COMPLIANT + INFINITY != f; // COMPLIANT + -INFINITY != f; // COMPLIANT + 0 != cf; // COMPLIANT + INFINITY != cf; // COMPLIANT + -INFINITY != cf; // COMPLIANT + + !b; // COMPLIANT + !c; // NON_COMPLIANT + !e1; // NON_COMPLIANT + !s; // NON_COMPLIANT + !u; // NON_COMPLIANT + !f; // NON_COMPLIANT + !cf; // NON_COMPLIANT + + b && true; // COMPLIANT + c && true; // NON_COMPLIANT + e1 && true; // NON_COMPLIANT + s && true; // NON_COMPLIANT + u && true; // NON_COMPLIANT + f && true; // NON_COMPLIANT + cf && true; // NON_COMPLIANT + + b || false; // COMPLIANT + c || false; // NON_COMPLIANT + e1 || false; // NON_COMPLIANT + s || false; // NON_COMPLIANT + u || false; // NON_COMPLIANT + f || false; // NON_COMPLIANT + cf || false; // NON_COMPLIANT + + true && b; // COMPLIANT + true && c; // NON_COMPLIANT + true && e1; // NON_COMPLIANT + true && s; // NON_COMPLIANT + true && u; // NON_COMPLIANT + true && f; // NON_COMPLIANT + true && cf; // NON_COMPLIANT + + false || b; // COMPLIANT + false || c; // NON_COMPLIANT + false || e1; // NON_COMPLIANT + false || s; // NON_COMPLIANT + false || u; // NON_COMPLIANT + false || f; // NON_COMPLIANT + false || cf; // NON_COMPLIANT + + b << u; // NON_COMPLIANT + c << u; // NON_COMPLIANT + e1 << u; // NON_COMPLIANT + s << u; // NON_COMPLIANT + u << u; // COMPLIANT + // f << u; // NON_COMPILABLE + // cf << u; // NON_COMPILABLE + + b >> u; // NON_COMPLIANT + c >> u; // NON_COMPLIANT + e1 >> u; // NON_COMPLIANT + s >> u; // NON_COMPLIANT + u >> u; // COMPLIANT + // f >> u; // NON_COMPILABLE + // cf >> u; // NON_COMPILABLE + + u << b; // NON_COMPLIANT + u << c; // NON_COMPLIANT + u << e1; // NON_COMPLIANT + u << s; // NON_COMPLIANT + u << u; // COMPLIANT + // u << f; // NON_COMPILABLE + // u << cf; // NON_COMPILABLE + + u >> b; // NON_COMPLIANT + u >> c; // NON_COMPLIANT + u >> e1; // NON_COMPLIANT + u >> s; // NON_COMPLIANT + u >> u; // COMPLIANT + // u >> f; // NON_COMPILABLE + // u >> cf; // NON_COMPILABLE + + b &u; // NON_COMPLIANT + c &u; // NON_COMPLIANT + e1 &u; // NON_COMPLIANT + s &u; // NON_COMPLIANT + u &u; // COMPLIANT + // f &u; // NON_COMPILABLE + // cf &u; // NON_COMPILABLE + + b | u; // NON_COMPLIANT + c | u; // NON_COMPLIANT + e1 | u; // NON_COMPLIANT + s | u; // NON_COMPLIANT + u | u; // COMPLIANT + // f | u; // NON_COMPILABLE + // cf | u; // NON_COMPILABLE + + b ^ u; // NON_COMPLIANT + c ^ u; // NON_COMPLIANT + e1 ^ u; // NON_COMPLIANT + s ^ u; // NON_COMPLIANT + u ^ u; // COMPLIANT + // f ^ u; // NON_COMPILABLE + // cf ^ u; // NON_COMPILABLE + + u &b; // NON_COMPLIANT + u &c; // NON_COMPLIANT + u &e1; // NON_COMPLIANT + u &s; // NON_COMPLIANT + u &u; // COMPLIANT + // u &f; // NON_COMPILABLE + // u &cf; // NON_COMPILABLE + + u | b; // NON_COMPLIANT + u | c; // NON_COMPLIANT + u | e1; // NON_COMPLIANT + u | s; // NON_COMPLIANT + u | u; // COMPLIANT + // u | f; // NON_COMPILABLE + // u | cf; // NON_COMPILABLE + + u ^ b; // NON_COMPLIANT + u ^ c; // NON_COMPLIANT + u ^ e1; // NON_COMPLIANT + u ^ s; // NON_COMPLIANT + u ^ u; // COMPLIANT + // u ^ f; // NON_COMPILABLE + // u ^ cf; // NON_COMPILABLE + + ~b; // NON_COMPLIANT + ~c; // NON_COMPLIANT + ~e1; // NON_COMPLIANT + ~s; // NON_COMPLIANT + ~u; // COMPLIANT + //~f; // NON_COMPILABLE + ~cf; // NON_COMPLIANT + + b ? 1 : 2; // COMPLIANT + c ? 1 : 2; // NON_COMPLIANT + e1 ? 1 : 2; // NON_COMPLIANT + s ? 1 : 2; // NON_COMPLIANT + u ? 1 : 2; // NON_COMPLIANT + f ? 1 : 2; // NON_COMPLIANT + cf ? 1 : 2; // NON_COMPLIANT + + b ? b : b; // COMPLIANT + b ? c : c; // COMPLIANT + b ? e1 : e1; // COMPLIANT + b ? s : s; // COMPLIANT + b ? u : u; // COMPLIANT + b ? f : f; // COMPLIANT + b ? cf : cf; // COMPLIANT + + b += 1; // NON_COMPLIANT + c += 1; // COMPLIANT + e1 += 1; // NON_COMPLIANT + s += 1; // COMPLIANT + u += 1; // COMPLIANT + f += 1; // COMPLIANT + cf += 1; // COMPLIANT + + b -= 1; // NON_COMPLIANT + c -= 1; // COMPLIANT + e1 -= 1; // NON_COMPLIANT + s -= 1; // COMPLIANT + u -= 1; // COMPLIANT + f -= 1; // COMPLIANT + cf -= 1; // COMPLIANT + + u += b; // NON_COMPLIANT + u += c; // COMPLIANT + u += e1; // NON_COMPLIANT + u += s; // COMPLIANT + u += u; // COMPLIANT + u += f; // COMPLIANT + u += cf; // COMPLIANT + + u -= b; // NON_COMPLIANT + u -= c; // COMPLIANT + u -= e1; // NON_COMPLIANT + u -= s; // COMPLIANT + u -= u; // COMPLIANT + u -= f; // COMPLIANT + u -= cf; // COMPLIANT + + b *= 1; // NON_COMPLIANT + c *= 1; // NON_COMPLIANT + e1 *= 1; // NON_COMPLIANT + s *= 1; // COMPLIANT + u *= 1; // COMPLIANT + f *= 1; // COMPLIANT + cf *= 1; // COMPLIANT + + b /= 1; // NON_COMPLIANT + c /= 1; // NON_COMPLIANT + e1 /= 1; // NON_COMPLIANT + s /= 1; // COMPLIANT + u /= 1; // COMPLIANT + f /= 1; // COMPLIANT + cf /= 1; // COMPLIANT + + u *= b; // NON_COMPLIANT + u *= c; // NON_COMPLIANT + u *= e1; // NON_COMPLIANT + u *= s; // COMPLIANT + u *= u; // COMPLIANT + u *= f; // COMPLIANT + u *= cf; // COMPLIANT + + u /= b; // NON_COMPLIANT + u /= c; // NON_COMPLIANT + u /= e1; // NON_COMPLIANT + u /= s; // COMPLIANT + u /= u; // COMPLIANT + u /= f; // COMPLIANT + u /= cf; // COMPLIANT + + b %= 1; // NON_COMPLIANT + c %= 1; // NON_COMPLIANT + e1 %= 1; // NON_COMPLIANT + s %= 1; // COMPLIANT + u %= 1; // COMPLIANT + // f %= 1; // NON_COMPILABLE + // cf %= 1; // NON_COMPILABLE + + u %= b; // NON_COMPLIANT + u %= c; // NON_COMPLIANT + u %= e1; // NON_COMPLIANT + u %= s; // COMPLIANT + u %= u; // COMPLIANT + // u %= f; // NON_COMPILABLE + // u %= cf; // NON_COMPILABLE + + b <<= u; // NON_COMPLIANT + c <<= u; // NON_COMPLIANT + e1 <<= u; // NON_COMPLIANT + s <<= u; // NON_COMPLIANT + u <<= u; // COMPLIANT + // f <<= u; // NON_COMPILABLE + // cf <<= u; // NON_COMPILABLE + + b >>= u; // NON_COMPLIANT + c >>= u; // NON_COMPLIANT + e1 >>= u; // NON_COMPLIANT + s >>= u; // NON_COMPLIANT + u >>= u; // COMPLIANT + // f >>= u; // NON_COMPILABLE + // cf >>= u; // NON_COMPILABLE + + u <<= b; // NON_COMPLIANT + u <<= c; // NON_COMPLIANT + u <<= e1; // NON_COMPLIANT + u <<= s; // NON_COMPLIANT + u <<= u; // COMPLIANT + // u <<= f; // NON_COMPILABLE + // u <<= cf; // NON_COMPILABLE + + u >>= b; // NON_COMPLIANT + u >>= c; // NON_COMPLIANT + u >>= e1; // NON_COMPLIANT + u >>= s; // NON_COMPLIANT + u >>= u; // COMPLIANT + // u >>= f; // NON_COMPILABLE + // u >>= cf; // NON_COMPILABLE + + b &= u; // NON_COMPLIANT + c &= u; // NON_COMPLIANT + e1 &= u; // NON_COMPLIANT + s &= u; // NON_COMPLIANT + u &= u; // COMPLIANT + // f &= u; // NON_COMPILABLE + // cf &= u; // NON_COMPILABLE + + b ^= u; // NON_COMPLIANT + c ^= u; // NON_COMPLIANT + e1 ^= u; // NON_COMPLIANT + s ^= u; // NON_COMPLIANT + u ^= u; // COMPLIANT + // f ^= u; // NON_COMPILABLE + // cf ^= u; // NON_COMPILABLE + + b |= u; // NON_COMPLIANT + c |= u; // NON_COMPLIANT + e1 |= u; // NON_COMPLIANT + s |= u; // NON_COMPLIANT + u |= u; // COMPLIANT + // f |= u; // NON_COMPILABLE + // cf |= u; // NON_COMPILABLE + + u &= b; // NON_COMPLIANT + u &= c; // NON_COMPLIANT + u &= e1; // NON_COMPLIANT + u &= s; // NON_COMPLIANT + u &= u; // COMPLIANT + // u &= f; // NON_COMPILABLE + // u &= cf; // NON_COMPILABLE + + u ^= b; // NON_COMPLIANT + u ^= c; // NON_COMPLIANT + u ^= e1; // NON_COMPLIANT + u ^= s; // NON_COMPLIANT + u ^= u; // COMPLIANT + // u ^= f; // NON_COMPILABLE + // u ^= cf; // NON_COMPILABLE + + u |= b; // NON_COMPLIANT + u |= c; // NON_COMPLIANT + u |= e1; // NON_COMPLIANT + u |= s; // NON_COMPLIANT + u |= u; // COMPLIANT + // u |= f; // NON_COMPILABLE + // u |= cf; // NON_COMPILABLE +} + +void pointerType() { + _Bool b = true; + int *p; + + !b; // COMPLIANT + !p; // NON_COMPLIANT + b &&b; // COMPLIANT + p &&b; // NON_COMPLIANT + b &&p; // NON_COMPLIANT + b || b; // COMPLIANT + p || b; // NON_COMPLIANT + b || p; // NON_COMPLIANT + p += 1; // COMPLIANT + p -= 1; // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.expected b/c/misra/test/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.expected new file mode 100644 index 0000000000..a1d3657a1e --- /dev/null +++ b/c/misra/test/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.expected @@ -0,0 +1,19 @@ +| test.c:19:3:19:11 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:20:3:20:9 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:21:3:21:9 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:22:3:22:9 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:23:3:23:9 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:24:3:24:10 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:25:3:25:10 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:28:3:28:9 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:29:3:29:10 | ... + ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:35:3:35:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:36:3:36:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:37:3:37:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:38:3:38:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:39:3:39:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:40:3:40:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:41:3:41:10 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:42:3:42:10 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:45:3:45:9 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | +| test.c:46:3:46:10 | ... - ... | Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations | diff --git a/c/misra/test/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.qlref b/c/misra/test/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.qlref new file mode 100644 index 0000000000..774874eb62 --- /dev/null +++ b/c/misra/test/rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.qlref @@ -0,0 +1 @@ +rules/RULE-10-2/AdditionSubtractionOnEssentiallyCharType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-2/test.c b/c/misra/test/rules/RULE-10-2/test.c new file mode 100644 index 0000000000..1d86013c07 --- /dev/null +++ b/c/misra/test/rules/RULE-10-2/test.c @@ -0,0 +1,47 @@ +#include + +void testRules() { + _Bool b = true; + enum E1 { A, B, C } e1 = A; + signed int i = 100; + unsigned int u = 100; + signed short s = 100; + unsigned short us = 100; + signed long l = 100L; + unsigned long ul = 100UL; + float f = 10.0f; + + // Addition cases + i + 'a'; // COMPLIANT + 'a' + i; // COMPLIANT + u + 'a'; // COMPLIANT + 'a' + u; // COMPLIANT + 'a' + 'a'; // NON_COMPLIANT + 'a' + f; // NON_COMPLIANT + f + 'a'; // NON_COMPLIANT + 'a' + b; // NON_COMPLIANT + b + 'a'; // NON_COMPLIANT + 'a' + e1; // NON_COMPLIANT + e1 + 'a'; // NON_COMPLIANT + 'a' + s; // COMPLIANT + 'a' + us; // COMPLIANT + 'a' + l; // NON_COMPLIANT + 'a' + ul; // NON_COMPLIANT + + // Subtraction cases + 'a' - i; // COMPLIANT + 'a' - u; // COMPLIANT + 'a' - 'a'; // COMPLIANT + 'a' - f; // NON_COMPLIANT + i - 'a'; // NON_COMPLIANT + u - 'a'; // NON_COMPLIANT + f - 'a'; // NON_COMPLIANT + b - 'a'; // NON_COMPLIANT + 'a' - b; // NON_COMPLIANT + e1 - 'a'; // NON_COMPLIANT + 'a' - e1; // NON_COMPLIANT + 'a' - s; // COMPLIANT + 'a' - us; // COMPLIANT + 'a' - l; // NON_COMPLIANT + 'a' - ul; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.expected b/c/misra/test/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.expected new file mode 100644 index 0000000000..edfd93dc51 --- /dev/null +++ b/c/misra/test/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.expected @@ -0,0 +1,194 @@ +| test.c:13:7:13:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:14:7:14:7 | s | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:15:7:15:7 | u | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:16:7:16:7 | f | Assignment of essentially Floating type value to an object of essentially Boolean type. | +| test.c:17:7:17:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Boolean type. | +| test.c:19:8:19:8 | b | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:21:8:21:8 | s | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:22:8:22:8 | u | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:23:8:23:8 | f | Assignment of essentially Floating type value to an object of essentially Enum Type. | +| test.c:24:8:24:9 | cf | Assignment of essentially Complex Floating type value to an object of essentially Enum Type. | +| test.c:26:7:26:7 | b | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:27:7:27:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:29:7:29:7 | u | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:30:7:30:7 | f | Assignment of essentially Floating type value to an object of essentially Signed type. | +| test.c:31:7:31:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Signed type. | +| test.c:33:7:33:7 | b | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:34:7:34:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:35:7:35:7 | s | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:37:7:37:7 | f | Assignment of essentially Floating type value to an object of essentially Unsigned type. | +| test.c:38:7:38:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Unsigned type. | +| test.c:40:7:40:7 | b | Assignment of essentially Boolean type value to an object of essentially Floating type. | +| test.c:41:7:41:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Floating type. | +| test.c:42:7:42:7 | s | Assignment of essentially Signed type value to an object of essentially Floating type. | +| test.c:43:7:43:7 | u | Assignment of essentially Unsigned type value to an object of essentially Floating type. | +| test.c:45:7:45:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Floating type. | +| test.c:47:8:47:8 | b | Assignment of essentially Boolean type value to an object of essentially Complex Floating type. | +| test.c:48:8:48:9 | e1 | Assignment of essentially Enum Type value to an object of essentially Complex Floating type. | +| test.c:49:8:49:8 | s | Assignment of essentially Signed type value to an object of essentially Complex Floating type. | +| test.c:50:8:50:8 | u | Assignment of essentially Unsigned type value to an object of essentially Complex Floating type. | +| test.c:64:14:64:15 | e1 | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:65:14:65:14 | s | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:66:14:66:14 | u | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:67:14:67:14 | f | Assignment of essentially Floating type value to an object of essentially Boolean type. | +| test.c:68:15:68:16 | cf | Assignment of essentially Complex Floating type value to an object of essentially Boolean type. | +| test.c:70:17:70:17 | b | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:72:17:72:17 | s | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:73:17:73:17 | u | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:74:17:74:17 | f | Assignment of essentially Floating type value to an object of essentially Enum Type. | +| test.c:75:18:75:19 | cf | Assignment of essentially Complex Floating type value to an object of essentially Enum Type. | +| test.c:77:19:77:19 | b | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:78:19:78:20 | e1 | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:80:19:80:19 | u | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:81:19:81:19 | f | Assignment of essentially Floating type value to an object of essentially Signed type. | +| test.c:82:20:82:21 | cf | Assignment of essentially Complex Floating type value to an object of essentially Signed type. | +| test.c:84:21:84:21 | b | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:85:21:85:22 | e1 | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:86:21:86:21 | s | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:88:21:88:21 | f | Assignment of essentially Floating type value to an object of essentially Unsigned type. | +| test.c:89:22:89:23 | cf | Assignment of essentially Complex Floating type value to an object of essentially Unsigned type. | +| test.c:91:14:91:14 | b | Assignment of essentially Boolean type value to an object of essentially Floating type. | +| test.c:92:14:92:15 | e1 | Assignment of essentially Enum Type value to an object of essentially Floating type. | +| test.c:93:14:93:14 | s | Assignment of essentially Signed type value to an object of essentially Floating type. | +| test.c:94:14:94:14 | u | Assignment of essentially Unsigned type value to an object of essentially Floating type. | +| test.c:96:15:96:16 | cf | Assignment of essentially Complex Floating type value to an object of essentially Floating type. | +| test.c:98:24:98:24 | b | Assignment of essentially Boolean type value to an object of essentially Complex Floating type. | +| test.c:99:24:99:25 | e1 | Assignment of essentially Enum Type value to an object of essentially Complex Floating type. | +| test.c:100:24:100:24 | s | Assignment of essentially Signed type value to an object of essentially Complex Floating type. | +| test.c:101:24:101:24 | u | Assignment of essentially Unsigned type value to an object of essentially Complex Floating type. | +| test.c:107:7:107:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:108:7:108:7 | s | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:109:7:109:7 | u | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:110:7:110:7 | f | Assignment of essentially Floating type value to an object of essentially Boolean type. | +| test.c:111:7:111:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Boolean type. | +| test.c:114:7:114:7 | b | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:116:7:116:7 | s | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:117:7:117:7 | u | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:118:7:118:7 | f | Assignment of essentially Floating type value to an object of essentially Enum Type. | +| test.c:119:7:119:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Enum Type. | +| test.c:122:7:122:7 | b | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:123:7:123:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:125:7:125:7 | u | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:126:7:126:7 | f | Assignment of essentially Floating type value to an object of essentially Signed type. | +| test.c:127:7:127:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Signed type. | +| test.c:130:7:130:7 | b | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:131:7:131:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:132:7:132:7 | s | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:134:7:134:7 | f | Assignment of essentially Floating type value to an object of essentially Unsigned type. | +| test.c:135:7:135:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Unsigned type. | +| test.c:138:7:138:7 | b | Assignment of essentially Boolean type value to an object of essentially Floating type. | +| test.c:139:7:139:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Floating type. | +| test.c:140:7:140:7 | s | Assignment of essentially Signed type value to an object of essentially Floating type. | +| test.c:141:7:141:7 | u | Assignment of essentially Unsigned type value to an object of essentially Floating type. | +| test.c:143:7:143:8 | cf | Assignment of essentially Complex Floating type value to an object of essentially Floating type. | +| test.c:146:7:146:7 | b | Assignment of essentially Boolean type value to an object of essentially Complex Floating type. | +| test.c:147:7:147:8 | e1 | Assignment of essentially Enum Type value to an object of essentially Complex Floating type. | +| test.c:148:7:148:7 | s | Assignment of essentially Signed type value to an object of essentially Complex Floating type. | +| test.c:149:7:149:7 | u | Assignment of essentially Unsigned type value to an object of essentially Complex Floating type. | +| test.c:158:7:158:8 | - ... | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:159:7:159:16 | 4294967296 | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:171:8:171:8 | A | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:172:8:172:10 | 100 | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:173:23:173:25 | 200 | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:178:8:178:11 | 1 | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:180:8:180:10 | 100 | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:181:23:181:25 | 200 | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:186:8:186:11 | 1 | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:187:8:187:8 | A | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:189:23:189:25 | 200 | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:194:8:194:11 | 1 | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:195:8:195:8 | A | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:216:8:216:8 | b | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:217:8:217:8 | b | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:218:8:218:8 | b | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:219:8:219:8 | b | Assignment of essentially Boolean type value to an object of essentially Floating type. | +| test.c:220:8:220:8 | b | Assignment of essentially Boolean type value to an object of essentially Complex Floating type. | +| test.c:223:8:223:9 | e1 | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:225:8:225:9 | e1 | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:226:8:226:9 | e1 | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:227:8:227:9 | e1 | Assignment of essentially Enum Type value to an object of essentially Floating type. | +| test.c:228:8:228:9 | e1 | Assignment of essentially Enum Type value to an object of essentially Complex Floating type. | +| test.c:231:8:231:8 | s | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:232:8:232:8 | s | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:234:8:234:8 | s | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:235:8:235:8 | s | Assignment of essentially Signed type value to an object of essentially Floating type. | +| test.c:236:8:236:8 | s | Assignment of essentially Signed type value to an object of essentially Complex Floating type. | +| test.c:239:8:239:8 | u | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:240:8:240:8 | u | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:241:8:241:8 | u | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:243:8:243:8 | u | Assignment of essentially Unsigned type value to an object of essentially Floating type. | +| test.c:244:8:244:8 | u | Assignment of essentially Unsigned type value to an object of essentially Complex Floating type. | +| test.c:247:8:247:8 | f | Assignment of essentially Floating type value to an object of essentially Boolean type. | +| test.c:248:8:248:8 | f | Assignment of essentially Floating type value to an object of essentially Enum Type. | +| test.c:249:8:249:8 | f | Assignment of essentially Floating type value to an object of essentially Signed type. | +| test.c:250:8:250:8 | f | Assignment of essentially Floating type value to an object of essentially Unsigned type. | +| test.c:255:8:255:9 | cf | Assignment of essentially Complex Floating type value to an object of essentially Boolean type. | +| test.c:256:8:256:9 | cf | Assignment of essentially Complex Floating type value to an object of essentially Enum Type. | +| test.c:257:8:257:9 | cf | Assignment of essentially Complex Floating type value to an object of essentially Signed type. | +| test.c:258:8:258:9 | cf | Assignment of essentially Complex Floating type value to an object of essentially Unsigned type. | +| test.c:259:8:259:9 | cf | Assignment of essentially Complex Floating type value to an object of essentially Floating type. | +| test.c:275:12:275:13 | e1 | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:277:12:277:12 | s | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:279:12:279:12 | u | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:281:12:281:12 | f | Assignment of essentially Floating type value to an object of essentially Boolean type. | +| test.c:283:12:283:13 | cf | Assignment of essentially Complex Floating type value to an object of essentially Boolean type. | +| test.c:297:12:297:12 | b | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:301:12:301:12 | s | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:303:12:303:12 | u | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:305:12:305:12 | f | Assignment of essentially Floating type value to an object of essentially Enum Type. | +| test.c:307:12:307:13 | cf | Assignment of essentially Complex Floating type value to an object of essentially Enum Type. | +| test.c:321:12:321:12 | b | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:323:12:323:13 | e1 | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:327:12:327:12 | u | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:329:12:329:12 | f | Assignment of essentially Floating type value to an object of essentially Signed type. | +| test.c:331:12:331:13 | cf | Assignment of essentially Complex Floating type value to an object of essentially Signed type. | +| test.c:345:12:345:12 | b | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:347:12:347:13 | e1 | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:349:12:349:12 | s | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:353:12:353:12 | f | Assignment of essentially Floating type value to an object of essentially Unsigned type. | +| test.c:355:12:355:13 | cf | Assignment of essentially Complex Floating type value to an object of essentially Unsigned type. | +| test.c:369:12:369:12 | b | Assignment of essentially Boolean type value to an object of essentially Floating type. | +| test.c:371:12:371:13 | e1 | Assignment of essentially Enum Type value to an object of essentially Floating type. | +| test.c:373:12:373:12 | s | Assignment of essentially Signed type value to an object of essentially Floating type. | +| test.c:375:12:375:12 | u | Assignment of essentially Unsigned type value to an object of essentially Floating type. | +| test.c:379:12:379:13 | cf | Assignment of essentially Complex Floating type value to an object of essentially Floating type. | +| test.c:393:12:393:12 | b | Assignment of essentially Boolean type value to an object of essentially Complex Floating type. | +| test.c:395:12:395:13 | e1 | Assignment of essentially Enum Type value to an object of essentially Complex Floating type. | +| test.c:397:12:397:12 | s | Assignment of essentially Signed type value to an object of essentially Complex Floating type. | +| test.c:399:12:399:12 | u | Assignment of essentially Unsigned type value to an object of essentially Complex Floating type. | +| test.c:427:10:427:11 | e1 | Assignment of essentially Enum Type value to an object of essentially Boolean type. | +| test.c:428:10:428:10 | s | Assignment of essentially Signed type value to an object of essentially Boolean type. | +| test.c:429:10:429:10 | u | Assignment of essentially Unsigned type value to an object of essentially Boolean type. | +| test.c:430:10:430:10 | f | Assignment of essentially Floating type value to an object of essentially Boolean type. | +| test.c:431:10:431:11 | cf | Assignment of essentially Complex Floating type value to an object of essentially Boolean type. | +| test.c:433:11:433:11 | b | Assignment of essentially Boolean type value to an object of essentially Enum Type. | +| test.c:435:11:435:11 | s | Assignment of essentially Signed type value to an object of essentially Enum Type. | +| test.c:436:11:436:11 | u | Assignment of essentially Unsigned type value to an object of essentially Enum Type. | +| test.c:437:11:437:11 | f | Assignment of essentially Floating type value to an object of essentially Enum Type. | +| test.c:438:11:438:12 | cf | Assignment of essentially Complex Floating type value to an object of essentially Enum Type. | +| test.c:440:10:440:10 | b | Assignment of essentially Boolean type value to an object of essentially Signed type. | +| test.c:441:10:441:11 | e1 | Assignment of essentially Enum Type value to an object of essentially Signed type. | +| test.c:443:10:443:10 | u | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:444:10:444:10 | f | Assignment of essentially Floating type value to an object of essentially Signed type. | +| test.c:445:10:445:11 | cf | Assignment of essentially Complex Floating type value to an object of essentially Signed type. | +| test.c:447:10:447:10 | b | Assignment of essentially Boolean type value to an object of essentially Unsigned type. | +| test.c:448:10:448:11 | e1 | Assignment of essentially Enum Type value to an object of essentially Unsigned type. | +| test.c:449:10:449:10 | s | Assignment of essentially Signed type value to an object of essentially Unsigned type. | +| test.c:451:10:451:10 | f | Assignment of essentially Floating type value to an object of essentially Unsigned type. | +| test.c:452:10:452:11 | cf | Assignment of essentially Complex Floating type value to an object of essentially Unsigned type. | +| test.c:454:10:454:10 | b | Assignment of essentially Boolean type value to an object of essentially Floating type. | +| test.c:455:10:455:11 | e1 | Assignment of essentially Enum Type value to an object of essentially Floating type. | +| test.c:456:10:456:10 | s | Assignment of essentially Signed type value to an object of essentially Floating type. | +| test.c:457:10:457:10 | u | Assignment of essentially Unsigned type value to an object of essentially Floating type. | +| test.c:459:10:459:11 | cf | Assignment of essentially Complex Floating type value to an object of essentially Floating type. | +| test.c:461:11:461:11 | b | Assignment of essentially Boolean type value to an object of essentially Complex Floating type. | +| test.c:462:11:462:12 | e1 | Assignment of essentially Enum Type value to an object of essentially Complex Floating type. | +| test.c:463:11:463:11 | s | Assignment of essentially Signed type value to an object of essentially Complex Floating type. | +| test.c:464:11:464:11 | u | Assignment of essentially Unsigned type value to an object of essentially Complex Floating type. | +| test.c:473:26:473:28 | f64 | Assignment of essentially Floating type value to an object of essentially Complex Floating type. | +| test.c:490:12:490:20 | ... & ... | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:491:12:491:20 | ... \| ... | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:492:12:492:20 | ... ^ ... | Assignment of essentially Unsigned type value to an object of essentially Signed type. | +| test.c:497:20:497:27 | ... & ... | Assignment of value of essentially Signed type of size 2 bytes to an object narrower essential type of size 1 bytes. | +| test.c:502:23:502:30 | ... & ... | Assignment of value of essentially Unsigned type of size 2 bytes to an object narrower essential type of size 1 bytes. | +| test.c:505:22:505:29 | ... & ... | Assignment of essentially Signed type value to an object of essentially Unsigned type. | diff --git a/c/misra/test/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.qlref b/c/misra/test/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.qlref new file mode 100644 index 0000000000..32b138eba6 --- /dev/null +++ b/c/misra/test/rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.qlref @@ -0,0 +1 @@ +rules/RULE-10-3/AssignmentOfIncompatibleEssentialType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-3/test.c b/c/misra/test/rules/RULE-10-3/test.c new file mode 100644 index 0000000000..a5bfd3beaf --- /dev/null +++ b/c/misra/test/rules/RULE-10-3/test.c @@ -0,0 +1,506 @@ +#include +#include + +void testAssignment() { + _Bool b = true; // COMPLIANT + enum E1 { A, B, C } e1 = A; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + b = false; // COMPLIANT + b = e1; // NON_COMPLIANT + b = s; // NON_COMPLIANT + b = u; // NON_COMPLIANT + b = f; // NON_COMPLIANT + b = cf; // NON_COMPLIANT + + e1 = b; // NON_COMPLIANT + e1 = e1; // COMPLIANT + e1 = s; // NON_COMPLIANT + e1 = u; // NON_COMPLIANT + e1 = f; // NON_COMPLIANT + e1 = cf; // NON_COMPLIANT + + s = b; // NON_COMPLIANT + s = e1; // NON_COMPLIANT + s = s; // COMPLIANT + s = u; // NON_COMPLIANT + s = f; // NON_COMPLIANT + s = cf; // NON_COMPLIANT + + u = b; // NON_COMPLIANT + u = e1; // NON_COMPLIANT + u = s; // NON_COMPLIANT + u = u; // COMPLIANT + u = f; // NON_COMPLIANT + u = cf; // NON_COMPLIANT + + f = b; // NON_COMPLIANT + f = e1; // NON_COMPLIANT + f = s; // NON_COMPLIANT + f = u; // NON_COMPLIANT + f = f; // COMPLIANT + f = cf; // NON-COMPLIANT + + cf = b; // NON_COMPLIANT + cf = e1; // NON_COMPLIANT + cf = s; // NON_COMPLIANT + cf = u; // NON_COMPLIANT + cf = f; // COMPLIANT + cf = cf; // COMPLIANT +} + +void testInitializers() { + _Bool b = true; // COMPLIANT + enum E1 { A, B, C } e1 = A; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + _Bool bb = b; // COMPLIANT + _Bool be = e1; // NON_COMPLIANT + _Bool bs = s; // NON_COMPLIANT + _Bool bu = u; // NON_COMPLIANT + _Bool bf = f; // NON_COMPLIANT + _Bool bcf = cf; // NON_COMPLIANT + + enum E1 e1b = b; // NON_COMPLIANT + enum E1 e1e = e1; // COMPLIANT + enum E1 e1s = s; // NON_COMPLIANT + enum E1 e1u = u; // NON_COMPLIANT + enum E1 e1f = f; // NON_COMPLIANT + enum E1 e1cf = cf; // NON_COMPLIANT + + signed int sb = b; // NON_COMPLIANT + signed int se = e1; // NON_COMPLIANT + signed int ss = s; // COMPLIANT + signed int su = u; // NON_COMPLIANT + signed int sf = f; // NON_COMPLIANT + signed int scf = cf; // NON_COMPLIANT + + unsigned int ub = b; // NON_COMPLIANT + unsigned int ue = e1; // NON_COMPLIANT + unsigned int us = s; // NON_COMPLIANT + unsigned int uu = u; // COMPLIANT + unsigned int uf = f; // NON_COMPLIANT + unsigned int ucf = cf; // NON_COMPLIANT + + float fb = b; // NON_COMPLIANT + float fe = e1; // NON_COMPLIANT + float fs = s; // NON_COMPLIANT + float fu = u; // NON_COMPLIANT + float ff = f; // COMPLIANT + float fcf = cf; // NON-COMPLIANT + + float _Complex cfb = b; // NON_COMPLIANT + float _Complex cfe = e1; // NON_COMPLIANT + float _Complex cfs = s; // NON_COMPLIANT + float _Complex cfu = u; // NON_COMPLIANT + float _Complex cff = f; // COMPLIANT + float _Complex cfcf = cf; // COMPLIANT + + _Bool ba[6] = { + b, // COMPLIANT + e1, // NON_COMPLIANT + s, // NON_COMPLIANT + u, // NON_COMPLIANT + f, // NON_COMPLIANT + cf // NON_COMPLIANT + }; + enum E1 ea[6] = { + b, // NON_COMPLIANT + e1, // COMPLIANT + s, // NON_COMPLIANT + u, // NON_COMPLIANT + f, // NON_COMPLIANT + cf // NON_COMPLIANT + }; + signed int sa[6] = { + b, // NON_COMPLIANT + e1, // NON_COMPLIANT + s, // COMPLIANT + u, // NON_COMPLIANT + f, // NON_COMPLIANT + cf // NON_COMPLIANT + }; + unsigned int ua[6] = { + b, // NON_COMPLIANT + e1, // NON_COMPLIANT + s, // NON_COMPLIANT + u, // COMPLIANT + f, // NON_COMPLIANT + cf // NON_COMPLIANT + }; + float fa[6] = { + b, // NON_COMPLIANT + e1, // NON_COMPLIANT + s, // NON_COMPLIANT + u, // NON_COMPLIANT + f, // COMPLIANT + cf // NON_COMPLIANT + }; + float _Complex cfa[6] = { + b, // NON_COMPLIANT + e1, // NON_COMPLIANT + s, // NON_COMPLIANT + u, // NON_COMPLIANT + f, // COMPLIANT + cf // COMPLIANT + }; +} + +void testException1() { + unsigned int u = 100; // COMPLIANT - by exception 1 + u = 100; // COMPLIANT - by exception 1 + u = -1; // NON_COMPLIANT - smaller that uint, so exception doesn't apply + u = 4294967296; // NON_COMPLIANT - cannot be stored in an int, so exception + // doesn't apply +} + +void testSwitchCase() { + _Bool b = true; // COMPLIANT + enum E1 { A, B, C } e1 = A; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + switch (b) { + case true: // COMPLIANT + case A: // NON_COMPLIANT + case 100: // NON_COMPLIANT + case ((unsigned int)200): // NON_COMPLIANT + break; // case 1.0f: // NON_COMPILABLE + } + + switch (e1) { + case true: // NON_COMPLIANT + case A: // COMPLIANT + case 100: // NON_COMPLIANT + case ((unsigned int)200): // NON_COMPLIANT + break; // case 1.0f: // NON_COMPILABLE + } + + switch (s) { + case true: // NON_COMPLIANT + case A: // NON_COMPLIANT + case 100: // COMPLIANT + case ((unsigned int)200): // NON_COMPLIANT + break; // case 1.0f: // NON_COMPILABLE + } + + switch (u) { + case true: // NON_COMPLIANT + case A: // NON_COMPLIANT + case 100: // COMPLIANT - by exception 1 + case ((unsigned int)200): // COMPLIANT - by exception 1 + break; // case 1.0f: // NON_COMPILABLE + } +} + +enum EG { EGA, EGB, EGC }; + +void func(_Bool b, enum EG eg, signed int i, unsigned int u, float f, + float _Complex cf); + +void testFunctionCall() { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + func(b, // COMPLIANT + b, // NON_COMPLIANT + b, // NON_COMPLIANT + b, // NON_COMPLIANT + b, // NON_COMPLIANT + b // NON_COMPLIANT + ); + + func(e1, // NON_COMPLIANT + e1, // COMPLIANT + e1, // NON_COMPLIANT + e1, // NON_COMPLIANT + e1, // NON_COMPLIANT + e1 // NON_COMPLIANT + ); + + func(s, // NON_COMPLIANT + s, // NON_COMPLIANT + s, // COMPLIANT + s, // NON_COMPLIANT + s, // NON_COMPLIANT + s // NON_COMPLIANT + ); + + func(u, // NON_COMPLIANT + u, // NON_COMPLIANT + u, // NON_COMPLIANT + u, // COMPLIANT + u, // NON_COMPLIANT + u // NON_COMPLIANT + ); + + func(f, // NON_COMPLIANT + f, // NON_COMPLIANT + f, // NON_COMPLIANT + f, // NON_COMPLIANT + f, // COMPLIANT + f // COMPLIANT + ); + + func(cf, // NON_COMPLIANT + cf, // NON_COMPLIANT + cf, // NON_COMPLIANT + cf, // NON_COMPLIANT + cf, // NON_COMPLIANT + cf); +} + +_Bool testBoolFunctionReturn(int x) { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + switch (x) { + case 0: + return b; // COMPLIANT + case 1: + return e1; // NON_COMPLIANT + case 2: + return s; // NON_COMPLIANT + case 3: + return u; // NON_COMPLIANT + case 4: + return f; // NON_COMPLIANT + default: + return cf; // NON_COMPLIANT + } +} + +enum EG testEnumFunctionReturn(int x) { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + switch (x) { + case 0: + return b; // NON_COMPLIANT + case 1: + return e1; // COMPLIANT + case 2: + return s; // NON_COMPLIANT + case 3: + return u; // NON_COMPLIANT + case 4: + return f; // NON_COMPLIANT + default: + return cf; // NON_COMPLIANT + } +} + +signed int testSignedIntFunctionReturn(int x) { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + switch (x) { + case 0: + return b; // NON_COMPLIANT + case 1: + return e1; // NON_COMPLIANT + case 2: + return s; // COMPLIANT + case 3: + return u; // NON_COMPLIANT + case 4: + return f; // NON_COMPLIANT + default: + return cf; // NON_COMPLIANT + } +} + +unsigned int testUnsignedIntFunctionReturn(int x) { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + switch (x) { + case 0: + return b; // NON_COMPLIANT + case 1: + return e1; // NON_COMPLIANT + case 2: + return s; // NON_COMPLIANT + case 3: + return u; // COMPLIANT + case 4: + return f; // NON_COMPLIANT + default: + return cf; // NON_COMPLIANT + } +} + +float testFloatFunctionReturn(int x) { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + switch (x) { + case 0: + return b; // NON_COMPLIANT + case 1: + return e1; // NON_COMPLIANT + case 2: + return s; // NON_COMPLIANT + case 3: + return u; // NON_COMPLIANT + case 4: + return f; // COMPLIANT + default: + return cf; // NON_COMPLIANT + } +} + +float _Complex testComplexFunctionReturn(int x) { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + switch (x) { + case 0: + return b; // NON_COMPLIANT + case 1: + return e1; // NON_COMPLIANT + case 2: + return s; // NON_COMPLIANT + case 3: + return u; // NON_COMPLIANT + case 4: + return f; // COMPLIANT + default: + return cf; // COMPLIANT + } +} + +struct S1 { + _Bool b; + enum EG e1; + signed int s; + unsigned int u; + float f; + float _Complex cf; +}; + +void testStructAssignment() { + _Bool b = true; // COMPLIANT + enum EG e1 = EGA; // COMPLIANT + signed int s = 100; // COMPLIANT + unsigned int u = 100; // COMPLIANT - by exception 1 + float f = 10.0f; // COMPLIANT + float _Complex cf = 10.0f; // COMPLIANT + + struct S1 s1; + + s1.b = b; // COMPLIANT + s1.b = e1; // NON_COMPLIANT + s1.b = s; // NON_COMPLIANT + s1.b = u; // NON_COMPLIANT + s1.b = f; // NON_COMPLIANT + s1.b = cf; // NON_COMPLIANT + + s1.e1 = b; // NON_COMPLIANT + s1.e1 = e1; // COMPLIANT + s1.e1 = s; // NON_COMPLIANT + s1.e1 = u; // NON_COMPLIANT + s1.e1 = f; // NON_COMPLIANT + s1.e1 = cf; // NON_COMPLIANT + + s1.s = b; // NON_COMPLIANT + s1.s = e1; // NON_COMPLIANT + s1.s = s; // COMPLIANT + s1.s = u; // NON_COMPLIANT + s1.s = f; // NON_COMPLIANT + s1.s = cf; // NON_COMPLIANT + + s1.u = b; // NON_COMPLIANT + s1.u = e1; // NON_COMPLIANT + s1.u = s; // NON_COMPLIANT + s1.u = u; // COMPLIANT + s1.u = f; // NON_COMPLIANT + s1.u = cf; // NON_COMPLIANT + + s1.f = b; // NON_COMPLIANT + s1.f = e1; // NON_COMPLIANT + s1.f = s; // NON_COMPLIANT + s1.f = u; // NON_COMPLIANT + s1.f = f; // COMPLIANT + s1.f = cf; // NON_COMPLIANT + + s1.cf = b; // NON_COMPLIANT + s1.cf = e1; // NON_COMPLIANT + s1.cf = s; // NON_COMPLIANT + s1.cf = u; // NON_COMPLIANT + s1.cf = f; // COMPLIANT + s1.cf = cf; // COMPLIANT +} + +void testException4() { + float f32 = 10.0f; // COMPLIANT + double f64 = 10.0f; // COMPLIANT + float _Complex cf32a = f32; // COMPLIANT + float _Complex cf32b = f64; // NON_COMPLIANT + double _Complex cf64a = f32; // COMPLIANT + double _Complex cf64b = f64; // COMPLIANT + + double _Complex f64byparts_a = 10.0i; // COMPLIANT + double _Complex f64byparts_b = 10.0 * I; // COMPLIANT + double _Complex f64byparts_c = 10.0f + 10.0i; // COMPLIANT + double _Complex f64byparts_d = 10.0f + 10.0f * I; // COMPLIANT +} + +void testBinaryBitwise() { + signed int s32 = 100; // COMPLIANT - wider + signed short s16 = 0; // COMPLIANT - wider + signed char s8 = 0; // COMPLIANT - wider + unsigned int u32 = 100; // COMPLIANT - by exception 1 + unsigned char u8 = 0; // COMPLIANT - by exception 1 + unsigned short u16 = 0; // COMPLIANT - by exception 1 + int x1 = s32 & u32; // NON_COMPLIANT - integer promotion to u32 + int x2 = s32 | u32; // NON_COMPLIANT - integer promotion to u32 + int x3 = s32 ^ u32; // NON_COMPLIANT - integer promotion to u32 + int x4 = s16 & s32; // COMPLIANT + int x5 = s16 & u16; // COMPLIANT + int x6 = s16 & s8; // COMPLIANT + signed short x7 = s16 & s8; // COMPLIANT + signed char x8 = s16 & s8; // NON_COMPLIANT + signed char x9 = s8 & s8; // COMPLIANT + signed short x10 = s8 & s8; // COMPLIANT + unsigned int x11 = u16 & u8; // COMPLIANT + unsigned short x12 = u16 & u8; // COMPLIANT + unsigned char x13 = u16 & u8; // NON_COMPLIANT + unsigned char x14 = u8 & u8; // COMPLIANT + unsigned short x15 = u8 & u8; // COMPLIANT + unsigned int x16 = s16 & s8; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.expected b/c/misra/test/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.expected new file mode 100644 index 0000000000..c85f2a447e --- /dev/null +++ b/c/misra/test/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.expected @@ -0,0 +1,13 @@ +| test.c:15:3:15:9 | ... + ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Unsigned type, right operand: essentially Signed type). | +| test.c:16:3:16:9 | ... + ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Unsigned type). | +| test.c:17:3:17:10 | ... += ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Unsigned type). | +| test.c:18:3:18:9 | ... + ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Floating type, right operand: essentially Signed type). | +| test.c:19:3:19:9 | ... + ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Floating type). | +| test.c:20:3:20:10 | ... += ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Floating type). | +| test.c:21:3:21:10 | ... + ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Complex Floating type, right operand: essentially Signed type). | +| test.c:22:3:22:10 | ... + ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Complex Floating type). | +| test.c:23:3:23:11 | ... += ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Complex Floating type). | +| test.c:31:3:31:9 | ... - ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Character type). | +| test.c:32:3:32:10 | ... -= ... | The operands of this operator with usual arithmetic conversions have mismatched essential types (left operand: essentially Signed type, right operand: essentially Character type). | +| test.c:43:3:43:11 | ... < ... | The operands of this operator with usual arithmetic conversions have mismatched essentially Enum types (left operand: E1, right operand: E2). | +| test.c:44:3:44:7 | ... < ... | The operands of this operator with usual arithmetic conversions have mismatched essentially Enum types (left operand: E1, right operand: E2). | diff --git a/c/misra/test/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.qlref b/c/misra/test/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.qlref new file mode 100644 index 0000000000..3b9a9bc32c --- /dev/null +++ b/c/misra/test/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.qlref @@ -0,0 +1 @@ +rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-4/test.c b/c/misra/test/rules/RULE-10-4/test.c new file mode 100644 index 0000000000..223aacbdad --- /dev/null +++ b/c/misra/test/rules/RULE-10-4/test.c @@ -0,0 +1,54 @@ +void testOps() { + signed int s32 = 100; + signed long long s64 = 100; + unsigned int u = 100; + float f = 10.0f; + float _Complex cf = 10.0f; + char c = 'A'; + + s32 + s32; // COMPLIANT + s64 + s64; // COMPLIANT + s32 + s64; // COMPLIANT + s64 + s32; // COMPLIANT + s64 += s32; // COMPLIANT + s32 += s64; // COMPLIANT + u + s32; // NON_COMPLIANT + s32 + u; // NON_COMPLIANT + s32 += u; // NON_COMPLIANT + f + s32; // NON_COMPLIANT + s32 + f; // NON_COMPLIANT + s32 += f; // NON_COMPLIANT + cf + s32; // NON_COMPLIANT + s32 + cf; // NON_COMPLIANT + s32 += cf; // NON_COMPLIANT + + c + s32; // COMPLIANT - by exception + c += s32; // COMPLIANT - by exception + s32 + c; // COMPLIANT - by exception + s32 += c; // COMPLIANT - by exception + c - s32; // COMPLIANT - by exception + c -= s32; // COMPLIANT - by exception + s32 - c; // NON_COMPLIANT + s32 -= c; // NON_COMPLIANT + + cf + f; // COMPLIANT - by exception + f + cf; // COMPLIANT - by exception + cf *f; // COMPLIANT - by exception + f *cf; // COMPLIANT - by exception + + enum E1 { A, B, C } e1a; + enum E2 { D, E, F } e2a; + e1a < e1a; // COMPLIANT + A < A; // COMPLIANT + e1a < e2a; // NON_COMPLIANT + A < D; // NON_COMPLIANT + + enum { G }; + s32 + G; // COMPLIANT + c == '\n'; // COMPLIANT + + typedef enum { H } E3; + + E3 e3a = H; + e3a < H; // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-5/InappropriateEssentialTypeCast.expected b/c/misra/test/rules/RULE-10-5/InappropriateEssentialTypeCast.expected new file mode 100644 index 0000000000..2f4c38eb95 --- /dev/null +++ b/c/misra/test/rules/RULE-10-5/InappropriateEssentialTypeCast.expected @@ -0,0 +1,25 @@ +| test.c:10:3:10:9 | (char)... | Incompatible cast from essentially Boolean type to essentially Character type. | +| test.c:11:3:11:13 | (E1)... | Incompatible cast from essentially Boolean type to essentially Enum Type. | +| test.c:12:3:12:15 | (signed int)... | Incompatible cast from essentially Boolean type to essentially Signed type. | +| test.c:13:3:13:17 | (unsigned int)... | Incompatible cast from essentially Boolean type to essentially Unsigned type. | +| test.c:14:3:14:10 | (float)... | Incompatible cast from essentially Boolean type to essentially Floating type. | +| test.c:15:3:15:20 | (_Complex float)... | Incompatible cast from essentially Boolean type to essentially Complex Floating type. | +| test.c:18:3:18:11 | (bool)... | Incompatible cast from essentially Character type to essentially Boolean type. | +| test.c:20:3:20:13 | (E1)... | Incompatible cast from essentially Character type to essentially Enum Type. | +| test.c:23:3:23:10 | (float)... | Incompatible cast from essentially Character type to essentially Floating type. | +| test.c:24:3:24:20 | (_Complex float)... | Incompatible cast from essentially Character type to essentially Complex Floating type. | +| test.c:27:3:27:11 | (bool)... | Incompatible cast from essentially Enum Type to essentially Boolean type. | +| test.c:29:3:29:13 | (E1)... | Incompatible cast from E2 to E1. | +| test.c:37:3:37:11 | (bool)... | Incompatible cast from essentially Signed type to essentially Boolean type. | +| test.c:39:3:39:13 | (E1)... | Incompatible cast from essentially Signed type to essentially Enum Type. | +| test.c:46:3:46:11 | (bool)... | Incompatible cast from essentially Unsigned type to essentially Boolean type. | +| test.c:48:3:48:13 | (E1)... | Incompatible cast from essentially Unsigned type to essentially Enum Type. | +| test.c:55:3:55:11 | (bool)... | Incompatible cast from essentially Floating type to essentially Boolean type. | +| test.c:56:3:56:9 | (char)... | Incompatible cast from essentially Floating type to essentially Character type. | +| test.c:57:3:57:13 | (E1)... | Incompatible cast from essentially Floating type to essentially Enum Type. | +| test.c:64:3:64:12 | (bool)... | Incompatible cast from essentially Complex Floating type to essentially Boolean type. | +| test.c:65:3:65:10 | (char)... | Incompatible cast from essentially Complex Floating type to essentially Character type. | +| test.c:66:3:66:14 | (E1)... | Incompatible cast from essentially Complex Floating type to essentially Enum Type. | +| test.c:84:3:84:10 | (bool)... | Incompatible cast from essentially Signed type to essentially Boolean type. | +| test.c:88:3:88:16 | (MyBool)... | Incompatible cast from essentially Signed type to essentially Boolean type. | +| test.c:92:3:92:12 | (boolean)... | Incompatible cast from essentially Signed type to essentially Boolean type. | diff --git a/c/misra/test/rules/RULE-10-5/InappropriateEssentialTypeCast.qlref b/c/misra/test/rules/RULE-10-5/InappropriateEssentialTypeCast.qlref new file mode 100644 index 0000000000..e871beb36b --- /dev/null +++ b/c/misra/test/rules/RULE-10-5/InappropriateEssentialTypeCast.qlref @@ -0,0 +1 @@ +rules/RULE-10-5/InappropriateEssentialTypeCast.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-5/test.c b/c/misra/test/rules/RULE-10-5/test.c new file mode 100644 index 0000000000..d7a6d878f1 --- /dev/null +++ b/c/misra/test/rules/RULE-10-5/test.c @@ -0,0 +1,93 @@ +#include +#include + +void testIncompatibleCasts() { + enum E1 { A, B }; + + _Bool b = true; + + (_Bool) b; // COMPLIANT + (char)b; // NON_COMPLIANT + (enum E1) b; // NON_COMPLIANT + (signed int)b; // NON_COMPLIANT + (unsigned int)b; // NON_COMPLIANT + (float)b; // NON_COMPLIANT + (float _Complex) b; // NON_COMPLIANT + + char c = 100; + (_Bool) c; // NON_COMPLIANT + (char)c; // COMPLIANT + (enum E1) c; // NON_COMPLIANT + (signed int)c; // COMPLIANT + (unsigned int)c; // COMPLIANT + (float)c; // NON_COMPLIANT + (float _Complex) c; // NON_COMPLIANT + + enum E2 { C, D } e = C; + (_Bool) e; // NON_COMPLIANT + (char)e; // COMPLIANT + (enum E1) e; // NON_COMPLIANT + (enum E2) e; // COMPLIANT + (signed int)e; // COMPLIANT + (unsigned int)e; // COMPLIANT + (float)e; // COMPLIANT + (float _Complex) e; // COMPLIANT + + signed int i = 100; + (_Bool) i; // NON_COMPLIANT + (char)i; // COMPLIANT + (enum E1) i; // NON_COMPLIANT + (signed int)i; // COMPLIANT + (unsigned int)i; // COMPLIANT + (float)i; // COMPLIANT + (float _Complex) i; // COMPLIANT + + unsigned int u = 100; + (_Bool) u; // NON_COMPLIANT + (char)u; // COMPLIANT + (enum E1) u; // NON_COMPLIANT + (signed int)u; // COMPLIANT + (unsigned int)u; // COMPLIANT + (float)u; // COMPLIANT + (float _Complex) u; // COMPLIANT + + float f = 100.0; + (_Bool) f; // NON_COMPLIANT + (char)f; // NON_COMPLIANT + (enum E1) f; // NON_COMPLIANT + (signed int)f; // COMPLIANT + (unsigned int)f; // COMPLIANT + (float)f; // COMPLIANT + (float _Complex) f; // COMPLIANT + + float _Complex cf = 100.0; + (_Bool) cf; // NON_COMPLIANT + (char)cf; // NON_COMPLIANT + (enum E1) cf; // NON_COMPLIANT + (signed int)cf; // COMPLIANT + (unsigned int)cf; // COMPLIANT + (float)cf; // COMPLIANT + (float _Complex) cf; // COMPLIANT +} + +void testImplicit() { + // Implicit conversions are not checked by this rule. + char c = true; // Not covered by rule + _Bool b = 100; // Not covered by rule + unsigned int u = 100; + _Bool b2 = u; // Not covered by rule +} + +void testIntegerConstantBool() { + (_Bool)0; // COMPLIANT + (_Bool)1; // COMPLIANT + (_Bool)2; // NON_COMPLIANT + enum MyBool { f, t }; + (enum MyBool)0; // COMPLIANT + (enum MyBool)1; // COMPLIANT + (enum MyBool)2; // NON_COMPLIANT + typedef int boolean; + (boolean)0; // COMPLIANT + (boolean)1; // COMPLIANT + (boolean)2; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-6/AssignmentToWiderEssentialType.expected b/c/misra/test/rules/RULE-10-6/AssignmentToWiderEssentialType.expected new file mode 100644 index 0000000000..6bae1a0a39 --- /dev/null +++ b/c/misra/test/rules/RULE-10-6/AssignmentToWiderEssentialType.expected @@ -0,0 +1,3 @@ +| test.c:5:9:5:17 | ... + ... | Assignment to wider essential type `unsigned int`. | +| test.c:7:24:7:32 | ... + ... | Assignment to wider essential type `unsigned int`. | +| test.c:8:27:8:35 | ... + ... | Assignment to wider essential type `unsigned int`. | diff --git a/c/misra/test/rules/RULE-10-6/AssignmentToWiderEssentialType.qlref b/c/misra/test/rules/RULE-10-6/AssignmentToWiderEssentialType.qlref new file mode 100644 index 0000000000..5b3ab4e556 --- /dev/null +++ b/c/misra/test/rules/RULE-10-6/AssignmentToWiderEssentialType.qlref @@ -0,0 +1 @@ +rules/RULE-10-6/AssignmentToWiderEssentialType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-6/test.c b/c/misra/test/rules/RULE-10-6/test.c new file mode 100644 index 0000000000..a6ee9da77b --- /dev/null +++ b/c/misra/test/rules/RULE-10-6/test.c @@ -0,0 +1,11 @@ +void testWiderAssignment() { + unsigned int u32 = 100; + unsigned short u16 = 100; + u16 = u16 + u16; // COMPLIANT + u32 = u16 + u16; // NON_COMPLIANT + u32 = (unsigned int)(u16 + u16); // COMPLIANT + unsigned int u32_2 = u16 + u16; // NON_COMPLIANT + unsigned int u32a[1] = {u16 + u16}; // NON_COMPLIANT + + signed int s32 = u16 + u16; // ignored - prohibited by Rule 10.3 +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.expected b/c/misra/test/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.expected new file mode 100644 index 0000000000..ea8fc433b1 --- /dev/null +++ b/c/misra/test/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.expected @@ -0,0 +1,7 @@ +| test.c:5:3:5:16 | ... + ... | Implicit conversion of $@ from unsigned short to unsigned int | test.c:5:9:5:16 | ... * ... | composite op | +| test.c:6:3:6:18 | ... * ... | Implicit conversion of $@ from unsigned short to unsigned int | test.c:6:9:6:17 | ... + ... | composite op | +| test.c:9:3:9:20 | ... += ... | Implicit conversion of $@ from unsigned short to unsigned int | test.c:9:11:9:19 | ... + ... | composite op | +| test.c:24:3:24:19 | ... + ... | Implicit conversion of $@ from float to double | test.c:24:10:24:18 | ... + ... | composite op | +| test.c:25:3:25:21 | ... + ... | Implicit conversion of $@ from _Complex float to double | test.c:25:10:25:20 | ... + ... | composite op | +| test.c:26:3:26:20 | ... + ... | Implicit conversion of $@ from float to _Complex double | test.c:26:11:26:19 | ... + ... | composite op | +| test.c:27:3:27:22 | ... + ... | Implicit conversion of $@ from _Complex float to _Complex double | test.c:27:11:27:21 | ... + ... | composite op | diff --git a/c/misra/test/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.qlref b/c/misra/test/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.qlref new file mode 100644 index 0000000000..fc83fbb902 --- /dev/null +++ b/c/misra/test/rules/RULE-10-7/ImplicitConversionOfCompositeExpression.qlref @@ -0,0 +1 @@ +rules/RULE-10-7/ImplicitConversionOfCompositeExpression.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-7/test.c b/c/misra/test/rules/RULE-10-7/test.c new file mode 100644 index 0000000000..7aaa1847e4 --- /dev/null +++ b/c/misra/test/rules/RULE-10-7/test.c @@ -0,0 +1,28 @@ +void testComposite() { + unsigned int u32 = 100; + unsigned short u16 = 100; + u16 + u32 *u16; // COMPLIANT + u32 + u16 *u16; // NON_COMPLIANT + u32 *(u16 + u16); // NON_COMPLIANT + u32 *(unsigned int)(u16 + u16); // COMPLIANT + u32 + u16 + u16; // COMPLIANT + u32 += (u16 + u16); // NON_COMPLIANT + u32 += (u32 + u16); // COMPLIANT + + signed int s32 = 100; + s32 += (u16 + u16); // // ignored - prohibited by Rule 10.4 + + float f32 = 10.0f; + double f64 = 10.0f; + float _Complex cf32 = 10.0f; + double _Complex cf64 = 10.0f; + + f32 + (f32 + f32); // COMPLIANT + cf32 + (cf32 + cf32); // COMPLIANT + f32 + (cf32 + cf32); // COMPLIANT + cf32 + (f32 + f32); // COMPLIANT + f64 + (f32 + f32); // NON_COMPLIANT + f64 + (cf32 + cf32); // NON_COMPLIANT + cf64 + (f32 + f32); // NON_COMPLIANT + cf64 + (cf32 + cf32); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-8/InappropriateCastOfCompositeExpression.expected b/c/misra/test/rules/RULE-10-8/InappropriateCastOfCompositeExpression.expected new file mode 100644 index 0000000000..659b41199d --- /dev/null +++ b/c/misra/test/rules/RULE-10-8/InappropriateCastOfCompositeExpression.expected @@ -0,0 +1,10 @@ +| test.c:4:16:4:20 | ... + ... | Cast from essentially Unsigned type to essentially Signed type changes type category. | +| test.c:5:18:5:22 | ... + ... | Cast from essentially Signed type to essentially Unsigned type changes type category. | +| test.c:11:11:11:15 | ... + ... | Cast from essentially Unsigned type to essentially Floating type changes type category. | +| test.c:12:20:12:24 | ... + ... | Cast from essentially Unsigned type to essentially Complex Floating type changes type category. | +| test.c:13:18:13:22 | ... + ... | Cast from essentially Floating type to essentially Unsigned type changes type category. | +| test.c:14:18:14:24 | ... + ... | Cast from essentially Complex Floating type to essentially Unsigned type changes type category. | +| test.c:25:18:25:24 | ... + ... | Cast from essentially Unsigned type to essentially Unsigned type widens type. | +| test.c:31:16:31:22 | ... + ... | Cast from essentially Signed type to essentially Signed type widens type. | +| test.c:43:12:43:20 | ... + ... | Cast from essentially Floating type to essentially Floating type widens type. | +| test.c:44:12:44:22 | ... + ... | Cast from essentially Complex Floating type to essentially Floating type widens type. | diff --git a/c/misra/test/rules/RULE-10-8/InappropriateCastOfCompositeExpression.qlref b/c/misra/test/rules/RULE-10-8/InappropriateCastOfCompositeExpression.qlref new file mode 100644 index 0000000000..58e1592686 --- /dev/null +++ b/c/misra/test/rules/RULE-10-8/InappropriateCastOfCompositeExpression.qlref @@ -0,0 +1 @@ +rules/RULE-10-8/InappropriateCastOfCompositeExpression.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-10-8/test.c b/c/misra/test/rules/RULE-10-8/test.c new file mode 100644 index 0000000000..31294ed550 --- /dev/null +++ b/c/misra/test/rules/RULE-10-8/test.c @@ -0,0 +1,47 @@ +void testDifferentEssentialType() { + unsigned int u = 1; + signed int s = 1; + (signed int)(u + u); // NON_COMPLIANT + (unsigned int)(s + s); // NON_COMPLIANT + (signed int)(s + s); // COMPLIANT + (unsigned int)(u + u); // COMPLIANT + + float f = 1.0; + float _Complex cf = 1.0; + (float)(u + u); // NON_COMPLIANT + (float _Complex)(u + u); // NON_COMPLIANT + (unsigned int)(f + f); // NON_COMPLIANT + (unsigned int)(cf + cf); // NON_COMPLIANT + (float)(f + f); // COMPLIANT + (float)(cf + cf); // COMPLIANT + (float _Complex)(f + f); // COMPLIANT + (float _Complex)(cf + cf); // COMPLIANT +} + +void testWiderType() { + unsigned short us = 1; + unsigned int u = 1; + + (unsigned int)(us + us); // NON_COMPLIANT + (unsigned short)(u + u); // COMPLIANT + + signed short ss = 1; + signed int s = 1; + + (signed int)(ss + ss); // NON_COMPLIANT + (signed short)(s + s); // COMPLIANT + + float f32 = 1.0; + double f64 = 1.0; + float _Complex cf32 = 1.0; + double _Complex cf64 = 1.0; + + (float)(f32 + f32); // COMPLIANT + (float)(cf32 + cf32); // COMPLIANT + (float _Complex)(f32 + f32); // COMPLIANT + (float _Complex)(cf32 + cf32); // COMPLIANT + (double)(f32 + f32); // NON_COMPLIANT + (double)(cf32 + cf32); // NON_COMPLIANT + (double _Complex)(f64 + f64); // COMPLIANT + (double _Complex)(cf64 + cf64); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected b/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected index ebe2c74742..0144180616 100644 --- a/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected +++ b/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected @@ -1,7 +1,6 @@ | test.c:11:8:11:16 | (fp1 *)... | Cast performed between a function pointer and another type. | | test.c:11:8:11:16 | (fp1)... | Cast performed between a function pointer and another type. | | test.c:12:14:12:23 | (void *)... | Cast performed between a function pointer and another type. | -| test.c:14:8:14:15 | (fp2)... | Cast performed between a function pointer and another type. | | test.c:15:8:15:15 | (fp2)... | Cast performed between a function pointer and another type. | | test.c:22:12:22:13 | (fp1)... | Cast performed between a function pointer and another type. | | test.c:25:8:25:9 | (fp1)... | Cast performed between a function pointer and another type. | diff --git a/c/misra/test/rules/RULE-11-1/test.c b/c/misra/test/rules/RULE-11-1/test.c index 858c6e68a9..4fcabb0599 100644 --- a/c/misra/test/rules/RULE-11-1/test.c +++ b/c/misra/test/rules/RULE-11-1/test.c @@ -11,7 +11,7 @@ void f1(void) { v1 = (fp1 *)v2; // NON_COMPLIANT void *v3 = (void *)v1; // NON_COMPLIANT - v2 = (fp2 *)0; // NON_COMPLIANT + v2 = (fp2 *)0; // COMPLIANT - null pointer constant v2 = (fp2 *)1; // NON_COMPLIANT pfp2 v4; diff --git a/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.expected b/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.expected new file mode 100644 index 0000000000..d38aac6455 --- /dev/null +++ b/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.expected @@ -0,0 +1,8 @@ +| test.c:3:15:3:16 | definition of g3 | g3 declared with an atomic void type. | +| test.c:10:17:10:18 | definition of m3 | m3 declared with an atomic void type. | +| test.c:15:22:15:23 | definition of p2 | p2 declared with an atomic void type. | +| test.c:20:23:20:24 | declaration of f2 | f2 declared with an atomic void type. | +| test.c:21:25:21:26 | declaration of f3 | f3 declared with an atomic void type. | +| test.c:22:14:22:15 | declaration of f4 | f4 declared with an atomic void type. | +| test.c:23:16:23:17 | declaration of f5 | f5 declared with an atomic void type. | +| test.c:27:3:27:19 | (_Atomic(void) *)... | Cast declared with an atomic void type. | diff --git a/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.expected b/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.expected.clang similarity index 100% rename from cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.expected rename to c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.expected.clang diff --git a/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.qlref b/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.qlref new file mode 100644 index 0000000000..2046575237 --- /dev/null +++ b/c/misra/test/rules/RULE-11-10/AtomicQualifierAppliedToVoid.qlref @@ -0,0 +1 @@ +rules/RULE-11-10/AtomicQualifierAppliedToVoid.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-10/test.c b/c/misra/test/rules/RULE-11-10/test.c new file mode 100644 index 0000000000..8f8e837b66 --- /dev/null +++ b/c/misra/test/rules/RULE-11-10/test.c @@ -0,0 +1,28 @@ +// _Atomic void g1; // doesn't compile +_Atomic int g2; // COMPLIANT +_Atomic void *g3; // NON_COMPLIANT +// _Atomic void g4[]; // doesn't compile +void *_Atomic g5; // COMPLIANT + +struct { + _Atomic int m1; // COMPLIANT + // _Atomic void m2; // doesn't compile + _Atomic void *m3; // NON_COMPLIANT + void *_Atomic m4; // COMPLIANT +} s1; + +void f(_Atomic int p1, // COMPLIANT + _Atomic void *p2 // NON_COMPLIANT + // _Atomic void p3[] // doesn't compile, even though it perhaps should as + // it is adjusted to void*. +) {} + +typedef _Atomic void *f2(void); // NON_COMPLIANT +typedef _Atomic void *(*f3)(void); // NON_COMPLIANT +typedef void f4(_Atomic void *); // NON_COMPLIANT +typedef void (*f5)(_Atomic void *); // NON_COMPLIANT + +void f6() { + (void *)0; // COMPLIANT + (_Atomic void *)0; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-10/test.c.clang b/c/misra/test/rules/RULE-11-10/test.c.clang new file mode 100644 index 0000000000..c30368a48d --- /dev/null +++ b/c/misra/test/rules/RULE-11-10/test.c.clang @@ -0,0 +1,28 @@ +// _Atomic void g1; // doesn't compile +_Atomic int g2; // COMPLIANT +// _Atomic void *g3; // NON_COMPLIANT +// _Atomic void g4[]; // doesn't compile +void *_Atomic g5; // COMPLIANT + +struct { + _Atomic int m1; // COMPLIANT + // _Atomic void m2; // doesn't compile + // _Atomic void *m3; // NON_COMPLIANT + void *_Atomic m4; // COMPLIANT +} s1; + +void f(_Atomic int p1 // COMPLIANT + // _Atomic void *p2 // NON_COMPLIANT + // _Atomic void p3[] // doesn't compile, even though it perhaps should as + // it is adjusted to void*. +) {} + +// typedef _Atomic void *f2(void); // NON_COMPLIANT +// typedef _Atomic void *(*f3)(void); // NON_COMPLIANT +// typedef void f4(_Atomic void *); // NON_COMPLIANT +// typedef void (*f5)(_Atomic void *); // NON_COMPLIANT + +void f6() { + (void *)0; // COMPLIANT + // (_Atomic void *)0; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.expected b/c/misra/test/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.expected index 91fd9f274a..24e6c4d5af 100644 --- a/c/misra/test/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.expected +++ b/c/misra/test/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.expected @@ -2,3 +2,11 @@ | test.c:14:8:14:9 | (int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (int). | | test.c:15:8:15:25 | (int *)... | Cast performed between a pointer to object type (short) and a pointer to a different object type (int). | | test.c:15:15:15:25 | (short *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (short). | +| test.c:20:3:20:17 | (const int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (const int). | +| test.c:21:3:21:16 | (int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (int). | +| test.c:22:20:22:21 | (int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (int). | +| test.c:23:3:23:18 | (long long *)... | Cast performed between a pointer to object type (int) and a pointer to a different object type (long long). | +| test.c:26:3:26:13 | (char *)... | Cast performed between a pointer to object type (_Atomic(int)) and a pointer to a different object type (char). | +| test.c:27:8:27:10 | (char *)... | Cast performed between a pointer to object type (_Atomic(int)) and a pointer to a different object type (char). | +| test.c:28:3:28:21 | (_Atomic(char) *)... | Cast performed between a pointer to object type (_Atomic(int)) and a pointer to a different object type (_Atomic(char)). | +| test.c:29:23:29:25 | (_Atomic(char) *)... | Cast performed between a pointer to object type (_Atomic(int)) and a pointer to a different object type (_Atomic(char)). | diff --git a/c/misra/test/rules/RULE-11-3/test.c b/c/misra/test/rules/RULE-11-3/test.c index 64ae688993..0d91740438 100644 --- a/c/misra/test/rules/RULE-11-3/test.c +++ b/c/misra/test/rules/RULE-11-3/test.c @@ -13,4 +13,18 @@ void f1(void) { int *v8 = (int *)0; // COMPLIANT v8 = v2; // NON_COMPLIANT v8 = (int *)(short *)v2; // NON_COMPLIANT + (const void *)v1; // COMPLIANT + const void *v9 = v1; // COMPLIANT + (int *)v9; // COMPLIANT - cast from void* + (const void *)v2; // COMPLIANT + (const int *)v2; // NON_COMPLIANT + (int *const)v2; // NON_COMPLIANT + int *const v10 = v2; // NON_COMPLIANT + (long long *)v10; // NON_COMPLIANT + + _Atomic int *v11 = 0; + (char *)v11; // NON_COMPLIANT + v2 = v11; // NON_COMPLIANT + (_Atomic char *)v11; // NON_COMPLIANT + _Atomic char *v12 = v11; // NON_COMPLIANT } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.expected b/c/misra/test/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.expected index 5fedfdcce4..17a2fa223f 100644 --- a/c/misra/test/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.expected +++ b/c/misra/test/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.expected @@ -1,6 +1,6 @@ -| test.c:5:21:5:42 | (unsigned int)... | Cast performed between a pointer to object type and a pointer to an integer type. | -| test.c:5:35:5:42 | (int *)... | Cast performed between a pointer to object type and a pointer to an integer type. | -| test.c:6:21:6:37 | (unsigned int)... | Cast performed between a pointer to object type and a pointer to an integer type. | -| test.c:8:8:8:24 | (unsigned int)... | Cast performed between a pointer to object type and a pointer to an integer type. | -| test.c:10:22:10:22 | (unsigned int *)... | Cast performed between a pointer to object type and a pointer to an integer type. | -| test.c:12:22:12:39 | (unsigned int *)... | Cast performed between a pointer to object type and a pointer to an integer type. | +| test.c:6:21:6:37 | (unsigned int)... | Cast from pointer to object type 'unsigned int *' to integer type 'unsigned int'. | test.c:6:21:6:37 | (unsigned int)... | | +| test.c:8:8:8:24 | (unsigned int)... | Cast from pointer to object type 'unsigned int *' to integer type 'unsigned int'. | test.c:8:8:8:24 | (unsigned int)... | | +| test.c:12:22:12:39 | (unsigned int *)... | Cast from integer type 'unsigned int' to pointer to object type 'unsigned int *'. | test.c:12:22:12:39 | (unsigned int *)... | | +| test.c:18:1:18:24 | #define FOO (int *)0x200 | Cast from integer type 'int' to pointer to object type 'int *'. | test.c:18:1:18:24 | #define FOO (int *)0x200 | | +| test.c:26:3:26:22 | (int *)... | Cast from integer type 'int' to pointer to object type 'int *' from expansion of macro $@. | test.c:20:1:20:34 | #define FOO_FUNCTIONAL(x) (int *)x | FOO_FUNCTIONAL | +| test.c:27:14:27:25 | (int *)... | Cast from integer type 'int' to pointer to object type 'int *' from expansion of macro $@. | test.c:21:1:21:23 | #define FOO_INSERT(x) x | FOO_INSERT | diff --git a/c/misra/test/rules/RULE-11-4/test.c b/c/misra/test/rules/RULE-11-4/test.c index 25e3f3c4b2..283af5e560 100644 --- a/c/misra/test/rules/RULE-11-4/test.c +++ b/c/misra/test/rules/RULE-11-4/test.c @@ -2,12 +2,27 @@ void f1(void) { unsigned int v1 = (unsigned int)(void *)0; // COMPLIANT - unsigned int v2 = (unsigned int)(int *)0; // NON_COMPLIANT + unsigned int v2 = (unsigned int)(int *)0; // COMPLIANT unsigned int v3 = (unsigned int)&v2; // NON_COMPLIANT v3 = v2; // COMPLIANT v3 = (unsigned int)&v2; // NON_COMPLIANT v3 = NULL; // COMPLIANT - unsigned int *v4 = 0; // NON_COMPLIANT + unsigned int *v4 = 0; // COMPLIANT unsigned int *v5 = NULL; // COMPLIANT unsigned int *v6 = (unsigned int *)v2; // NON_COMPLIANT + const void *v7 = 0; + (unsigned int)v7; // COMPLIANT - cast const void to int + (const void *)v1; // COMPLIANT - casting int to const void +} + +#define FOO (int *)0x200 // NON_COMPLIANT +#define FOO_WRAPPER FOO; +#define FOO_FUNCTIONAL(x) (int *)x +#define FOO_INSERT(x) x + +void test_macros() { + FOO; // Issue is reported at the macro + FOO_WRAPPER; // Issue is reported at the macro + FOO_FUNCTIONAL(0x200); // NON_COMPLIANT + FOO_INSERT((int *)0x200); // NON_COMPLIANT } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.expected b/c/misra/test/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.expected index 5b4eec8d15..42cf288b34 100644 --- a/c/misra/test/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.expected +++ b/c/misra/test/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.expected @@ -1 +1,2 @@ | test.c:6:13:6:21 | (int *)... | Cast performed from a void pointer into a pointer to an object (int *). | +| test.c:11:3:11:11 | (int *)... | Cast performed from a void pointer into a pointer to an object (int *). | diff --git a/c/misra/test/rules/RULE-11-5/test.c b/c/misra/test/rules/RULE-11-5/test.c index a7ffa4822e..5b5a5b3a52 100644 --- a/c/misra/test/rules/RULE-11-5/test.c +++ b/c/misra/test/rules/RULE-11-5/test.c @@ -7,4 +7,8 @@ void f1(void) { v2 = NULL; // COMPLIANT void *v3 = (void *)v1; // COMPLIANT v3 = (void *)v2; // COMPLIANT + const void *v4 = 0; + (int *)v4; // NON_COMPLIANT - const in type is irrelevant + (const void *)v1; // COMPLIANT - casting is from void to void, const addition + // should be irrelevant } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected b/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected index 0b96b3c747..133e568499 100644 --- a/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected +++ b/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected @@ -1,3 +1,3 @@ -| test.c:5:13:5:20 | (bool)... | Cast performed between a pointer to void type and a non-integer arithmetic type. | -| test.c:7:21:7:28 | (bool)... | Cast performed between a pointer to void type and a non-integer arithmetic type. | -| test.c:8:8:8:16 | (int *)... | Cast performed between a pointer to void type and a non-integer arithmetic type. | +| test.c:5:13:5:20 | (bool)... | Cast performed between a pointer to object type and a non-integer arithmetic type. | +| test.c:7:21:7:28 | (bool)... | Cast performed between a pointer to object type and a non-integer arithmetic type. | +| test.c:8:8:8:16 | (int *)... | Cast performed between a pointer to object type and a non-integer arithmetic type. | diff --git a/c/misra/test/rules/RULE-11-7/test.c b/c/misra/test/rules/RULE-11-7/test.c index b7dd989b00..4891aaae85 100644 --- a/c/misra/test/rules/RULE-11-7/test.c +++ b/c/misra/test/rules/RULE-11-7/test.c @@ -7,4 +7,12 @@ void f1(void) { float v4 = (float)(bool)v1; // NON_COMPLIANT v1 = (int *)v2; // NON_COMPLIANT v4 = (float)v3; // COMPLIANT + void *v5 = 0; + const void *v6 = 0; + // void pointers (regardless of specifier) are not pointers to object, so all + // these examples are compliant according to this rule + (bool)v5; // COMPLIANT + (bool)v6; // COMPLIANT + (void *)v2; // COMPLIANT + (const void *)v2; // COMPLIANT } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.expected b/c/misra/test/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.expected index 48658e2176..aa7752d28a 100644 --- a/c/misra/test/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.expected +++ b/c/misra/test/rules/RULE-11-8/CastRemovesConstOrVolatileQualification.expected @@ -1,2 +1,6 @@ | test.c:4:19:4:33 | (const char *)... | Cast of pointer removes volatile qualification from its base type. | | test.c:6:13:6:21 | (char *)... | Cast of pointer removes const qualification from its base type. | +| test.c:9:3:9:11 | (char *)... | Cast of pointer removes atomic qualification from its base type. | +| test.c:10:7:10:7 | (char *)... | Cast of pointer removes atomic qualification from its base type. | +| test.c:11:3:11:17 | (const char *)... | Cast of pointer removes atomic qualification from its base type. | +| test.c:12:7:12:7 | (const char *)... | Cast of pointer removes atomic qualification from its base type. | diff --git a/c/misra/test/rules/RULE-11-8/test.c b/c/misra/test/rules/RULE-11-8/test.c index 75c7fc189a..e0e3b3a2fb 100644 --- a/c/misra/test/rules/RULE-11-8/test.c +++ b/c/misra/test/rules/RULE-11-8/test.c @@ -5,5 +5,12 @@ int f1(void) { const char *c2 = (const char *)c; // COMPLIANT char *d = (char *)c; // NON_COMPLIANT const char *e = (const char *)d; // COMPLIANT + _Atomic char *f = 0; + (char *)f; // NON_COMPLIANT + d = f; // NON_COMPLIANT + (const char *)f; // NON_COMPLIANT + e = f; // NON_COMPLIANT + (const _Atomic char *)f; // COMPLIANT + (const _Atomic char *)f; // COMPLIANT return 0; } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.expected b/c/misra/test/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.expected index 8cdd34edd1..d854730296 100644 --- a/c/misra/test/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.expected +++ b/c/misra/test/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.expected @@ -1,4 +1,5 @@ | test.c:15:13:15:13 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:15:7:15:13 | ... == ... | Equality operator | | test.c:17:8:17:8 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:17:3:17:8 | ... = ... | Assignment to pointer | -| test.c:25:20:25:20 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:25:3:25:35 | ... ? ... : ... | Ternary operator | -| test.c:25:20:25:20 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:25:15:25:20 | ... = ... | Assignment to pointer | +| test.c:23:13:23:13 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:23:3:23:13 | ... ? ... : ... | Ternary operator | +| test.c:24:8:24:8 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:24:3:24:13 | ... ? ... : ... | Ternary operator | +| test.c:31:14:31:14 | 0 | $@ uses zero-value integer constant expression as null pointer constant. | test.c:31:9:31:14 | ... = ... | Assignment to pointer | diff --git a/c/misra/test/rules/RULE-11-9/test.c b/c/misra/test/rules/RULE-11-9/test.c index 216ea2b280..e87366d831 100644 --- a/c/misra/test/rules/RULE-11-9/test.c +++ b/c/misra/test/rules/RULE-11-9/test.c @@ -19,9 +19,16 @@ void *f1(void *p1, int p2) { p1 = NULL; // COMPLIANT if (p2 == 0) { // COMPLIANT return NULL; - } // COMPLIANT - (p1) ? (p1 = NULL) : (p1 = NULL); // COMPLIANT - (p2 > 0) ? (p1 = NULL) : (p1 = NULL); // COMPLIANT - (p2 > 0) ? (p1 = 0) : (p1 = NULL); // NON_COMPLIANT - return 0; // COMPLIANT + } + p2 ? p1 : 0; // NON_COMPLIANT + p2 ? 0 : p1; // NON_COMPLIANT + p2 ? (void *)0 : p1; // COMPLIANT + p2 ? p1 : (void *)0; // COMPLIANT + p2 ? p2 : 0; // COMPLIANT - p2 is not a pointer type + p2 ? 0 : p2; // COMPLIANT - p2 is not a pointer type + int x; + int *y; + p2 ? (p1 = 0) : p1; // NON_COMPLIANT - p1 is a pointer type + p2 ? (p2 = 0) : p1; // COMPLIANT - p2 is not a pointer type + return 0; // COMPLIANT } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-2/RightHandOperandOfAShiftRange.expected b/c/misra/test/rules/RULE-12-2/RightHandOperandOfAShiftRange.expected new file mode 100644 index 0000000000..5ac6f8bfd4 --- /dev/null +++ b/c/misra/test/rules/RULE-12-2/RightHandOperandOfAShiftRange.expected @@ -0,0 +1,12 @@ +| test.c:8:10:8:10 | 8 | The possible range of the right operand of the shift operator (8..8) is outside the the valid shift range (0..7) for the essential type of the left operand (uint8_t). | test.c:8:3:8:10 | ... >> ... | | +| test.c:9:10:9:11 | - ... | The possible range of the right operand of the shift operator (-1..-1) is outside the the valid shift range (0..7) for the essential type of the left operand (uint8_t). | test.c:9:3:9:11 | ... >> ... | | +| test.c:10:10:10:14 | ... + ... | The possible range of the right operand of the shift operator (8..8) is outside the the valid shift range (0..7) for the essential type of the left operand (uint8_t). | test.c:10:3:10:14 | ... >> ... | | +| test.c:11:10:11:14 | ... + ... | The possible range of the right operand of the shift operator (8..8) is outside the the valid shift range (0..7) for the essential type of the left operand (uint8_t). | test.c:11:3:11:14 | ... << ... | | +| test.c:13:21:13:22 | 16 | The possible range of the right operand of the shift operator (16..16) is outside the the valid shift range (0..15) for the essential type of the left operand (uint16_t). | test.c:13:3:13:22 | ... << ... | | +| test.c:16:9:16:9 | 8 | The possible range of the right operand of the shift operator (8..8) is outside the the valid shift range (0..7) for the essential type of the left operand (unsigned char). | test.c:16:3:16:9 | ... << ... | | +| test.c:21:9:21:10 | 64 | The possible range of the right operand of the shift operator (64..64) is outside the the valid shift range (0..63) for the essential type of the left operand (unsigned long). | test.c:21:3:21:10 | ... << ... | | +| test.c:26:10:26:11 | 64 | The possible range of the right operand of the shift operator (64..64) is outside the the valid shift range (0..63) for the essential type of the left operand (unsigned long). | test.c:26:3:26:11 | ... << ... | | +| test.c:30:16:30:17 | 64 | The possible range of the right operand of the shift operator (64..64) is outside the the valid shift range (0..63) for the essential type of the left operand (unsigned long). | test.c:30:3:30:17 | ... << ... | | +| test.c:34:8:34:8 | y | The possible range of the right operand of the shift operator (0..4294967295) is outside the the valid shift range (0..31) for the essential type of the left operand (unsigned int). | test.c:34:3:34:8 | ... >> ... | | +| test.c:40:8:40:8 | y | The possible range of the right operand of the shift operator (-2147483648..2147483647) is outside the the valid shift range (0..31) for the essential type of the left operand (signed int). | test.c:40:3:40:8 | ... >> ... | | +| test.c:42:8:42:8 | y | The possible range of the right operand of the shift operator (-31..31) is outside the the valid shift range (0..31) for the essential type of the left operand (signed int). | test.c:42:3:42:8 | ... >> ... | | diff --git a/c/misra/test/rules/RULE-12-2/RightHandOperandOfAShiftRange.qlref b/c/misra/test/rules/RULE-12-2/RightHandOperandOfAShiftRange.qlref new file mode 100644 index 0000000000..4012a02734 --- /dev/null +++ b/c/misra/test/rules/RULE-12-2/RightHandOperandOfAShiftRange.qlref @@ -0,0 +1 @@ +rules/RULE-12-2/RightHandOperandOfAShiftRange.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-2/test.c b/c/misra/test/rules/RULE-12-2/test.c new file mode 100644 index 0000000000..db7b7b062d --- /dev/null +++ b/c/misra/test/rules/RULE-12-2/test.c @@ -0,0 +1,43 @@ +#include +#include +void f1() { + uint8_t ui8; + int b = 4; + + ui8 << 7; // COMPLIANT + ui8 >> 8; // NON_COMPLIANT + ui8 >> -1; // NON_COMPLIANT + ui8 >> 4 + b; // NON_COMPLIANT + ui8 << b + b; // NON_COMPLIANT + (uint16_t) ui8 << 8; // COMPLIANT + (uint16_t) ui8 << 16; // NON_COMPLIANT + + // 0u essential type is essentially unsigned char + 0u << 8; // NON_COMPLIANT + (uint16_t)0u << 8; // COMPLIANT + + unsigned long ul; + ul << 8; // COMPLIANT + ul << 64; // NON_COMPLIANT + + // 1UL essential type is essentially unsigned char + 1UL << 7; // COMPLIANT + 1UL << 8; // NON_COMPLIANT + 1UL << 64; // NON_COMPLIANT + + // ULONG_MAX essential type is essentially unsigned long + ULONG_MAX << 8; // COMPLIANT + ULONG_MAX << 64; // NON_COMPLIANT +} + +void unsignedRemAssign(unsigned int y, unsigned int x) { + x >> y; // NON_COMPLIANT + y %= 32; + x >> y; // COMPLIANT +} + +void signedRemAssign(signed int y, signed int x) { + x >> y; // NON_COMPLIANT + y %= 32; + x >> y; // NON_COMPLIANT - may be negative +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-4/ConstantUnsignedIntegerExpressionsWrapAround.testref b/c/misra/test/rules/RULE-12-4/ConstantUnsignedIntegerExpressionsWrapAround.testref new file mode 100644 index 0000000000..7e97e39764 --- /dev/null +++ b/c/misra/test/rules/RULE-12-4/ConstantUnsignedIntegerExpressionsWrapAround.testref @@ -0,0 +1 @@ +c/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.expected b/c/misra/test/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.expected new file mode 100644 index 0000000000..777df8349a --- /dev/null +++ b/c/misra/test/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.expected @@ -0,0 +1,2 @@ +| test.c:6:12:6:23 | sizeof() | The sizeof operator is called on an array-type parameter nums. | +| test.c:13:12:13:25 | sizeof() | The sizeof operator is called on an array-type parameter string. | \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.qlref b/c/misra/test/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.qlref new file mode 100644 index 0000000000..6b6424aad4 --- /dev/null +++ b/c/misra/test/rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.qlref @@ -0,0 +1 @@ +rules/RULE-12-5/SizeofOperatorUsedOnArrayTypeParam.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-5/test.c b/c/misra/test/rules/RULE-12-5/test.c new file mode 100644 index 0000000000..79920737e9 --- /dev/null +++ b/c/misra/test/rules/RULE-12-5/test.c @@ -0,0 +1,26 @@ +#include +#include + +void sample(int32_t nums[4], const char string[], int32_t x) { + for (int i = 0; + i < sizeof(nums) / // NON_COMPLIANT: `sizeof` directly invoked on `nums` + sizeof(int32_t); + i++) { + printf("%d\n", nums[i]); + } + + for (int i = 0; + i < sizeof(string) / // NON_COMPLIANT: directly invoked on `string` + sizeof(char); + i++) { + printf("%c", string[i]); + } + + printf("%lu\n", sizeof(x)); // COMPLIANT: `x` not a array type parameter + + char local_string[5] = "abcd"; + printf( + "%lu\n", + sizeof( + local_string)); // COMPLIANT: `local_string` not a function parameter +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.expected b/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.expected new file mode 100644 index 0000000000..5a92fc72fd --- /dev/null +++ b/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.expected @@ -0,0 +1,13 @@ +| test.c:43:13:43:13 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:44:18:44:18 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:45:13:45:13 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:46:18:46:18 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:65:6:65:6 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:71:9:71:9 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:82:18:82:18 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:83:3:83:31 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:84:3:84:39 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:85:3:85:19 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:86:3:86:23 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:87:3:87:19 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | +| test.c:88:3:88:23 | x | Invalid access to member '$@' on atomic struct or union. | test.c:5:7:5:7 | x | x | diff --git a/cpp/autosar/test/rules/A4-5-1/test.cpp b/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.expected.clang similarity index 100% rename from cpp/autosar/test/rules/A4-5-1/test.cpp rename to c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.expected.clang diff --git a/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.qlref b/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.qlref new file mode 100644 index 0000000000..2196eeace1 --- /dev/null +++ b/c/misra/test/rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.qlref @@ -0,0 +1 @@ +rules/RULE-12-6/AtomicAggregateObjectDirectlyAccessed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-6/test.c b/c/misra/test/rules/RULE-12-6/test.c new file mode 100644 index 0000000000..74d9de2fca --- /dev/null +++ b/c/misra/test/rules/RULE-12-6/test.c @@ -0,0 +1,89 @@ +#include "stdatomic.h" +#include "string.h" + +typedef struct s1 { + int x; +} s1; + +_Atomic s1 atomic_s1; +// A non-atomic pointer to an atomic s1 +_Atomic s1 *ptr_atomic_s1; +// An atomic pointer to a non-atomic s1 +s1 *_Atomic s1_atomic_ptr; + +_Atomic int g3; + +void takeCopy(s1 p1); + +void f1() { + s1 l1; + s1 *l2; + l1 = atomic_load(&atomic_s1); // COMPLIANT + l1 = atomic_load(ptr_atomic_s1); // COMPLIANT + l2 = atomic_load(&s1_atomic_ptr); // COMPLIANT + l1.x = 4; // COMPLIANT + l2->x = 4; // COMPLIANT + atomic_store(&atomic_s1, l1); // COMPLIANT + atomic_store(ptr_atomic_s1, l1); // COMPLIANT + atomic_store(&s1_atomic_ptr, l2); // COMPLIANT + + // Undefined behavior, but not banned by this rule. + memset(&atomic_s1, 0, sizeof(atomic_s1)); // COMPLIANT + memset(ptr_atomic_s1, 0, sizeof(*ptr_atomic_s1)); // COMPLIANT + + // OK: whole loads and stores are protected from data-races. + takeCopy(atomic_s1); // COMPLIANT + takeCopy(*ptr_atomic_s1); // COMPLIANT + atomic_s1 = (s1){0}; // COMPLIANT + *ptr_atomic_s1 = (s1){0}; // COMPLIANT + atomic_s1 = *l2; // COMPLIANT + ptr_atomic_s1 = l2; // COMPLIANT + + // Banned: circumvents data-race protection, results in UB. + atomic_s1.x; // NON-COMPLIANT + ptr_atomic_s1->x; // NON-COMPLIANT + atomic_s1.x = 0; // NON-COMPLIANT + ptr_atomic_s1->x = 0; // NON-COMPLIANT + + // OK: not evaluated. + sizeof(atomic_s1); // COMPLIANT + sizeof(ptr_atomic_s1); // COMPLIANT + sizeof(atomic_s1.x); // COMPLIANT + sizeof(ptr_atomic_s1->x); // COMPLIANT + + // All OK: not an atomic struct, but rather an atomic pointer to non-atomic + // struct. + memset(s1_atomic_ptr, 0, sizeof(*s1_atomic_ptr)); // COMPLIANT + takeCopy(*s1_atomic_ptr); // COMPLIANT + *s1_atomic_ptr = (s1){0}; // COMPLIANT + s1_atomic_ptr = l2; // COMPLIANT + s1_atomic_ptr->x; // COMPLIANT + + // Atomic specifier hidden behind a typedef, still atomic: + typedef _Atomic s1 atomic_s1; + atomic_s1 l3; + l3.x; // NON_COMPLIANT + + // Worst case scenario: a typedef of a volatile const pointer to an atomic + // typedef type. + typedef atomic_s1 *volatile const atomic_s1_specified_ptr; + atomic_s1_specified_ptr l4; + (l4)->x; // NON_COMPLIANT +} + +#define NOOP(x) (x) +#define DOT_FIELD_ACCESS_X(v) (v).x +#define POINTER_FIELD_ACCESS_X(v) (v)->x +#define GET_X_ATOMIC_S1() atomic_s1.x +#define GET_X_PTR_ATOMIC_S1() atomic_s1.x + +void f2() { + // Banned UB with user macros: + NOOP(atomic_s1.x); // NON-COMPLIANT + DOT_FIELD_ACCESS_X(atomic_s1); // NON-COMPLIANT + POINTER_FIELD_ACCESS_X(ptr_atomic_s1); // NON-COMPLIANT + GET_X_ATOMIC_S1(); // NON-COMPLIANT + GET_X_PTR_ATOMIC_S1(); // NON-COMPLIANT + GET_X_ATOMIC_S1() = 0; // NON-COMPLIANT + GET_X_PTR_ATOMIC_S1() = 0; // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-12-6/test.c.clang b/c/misra/test/rules/RULE-12-6/test.c.clang new file mode 100644 index 0000000000..83ad24cdb5 --- /dev/null +++ b/c/misra/test/rules/RULE-12-6/test.c.clang @@ -0,0 +1,89 @@ +#include "stdatomic.h" +#include "string.h" + +typedef struct s1 { + int x; +} s1; + +_Atomic s1 atomic_s1; +// A non-atomic pointer to an atomic s1 +_Atomic s1 *ptr_atomic_s1; +// An atomic pointer to a non-atomic s1 +s1 *_Atomic s1_atomic_ptr; + +_Atomic int g3; + +void takeCopy(s1 p1); + +void f1() { + s1 l1; + s1 *l2; + l1 = atomic_load(&atomic_s1); // COMPLIANT + l1 = atomic_load(ptr_atomic_s1); // COMPLIANT + l2 = atomic_load(&s1_atomic_ptr); // COMPLIANT + l1.x = 4; // COMPLIANT + l2->x = 4; // COMPLIANT + atomic_store(&atomic_s1, l1); // COMPLIANT + atomic_store(ptr_atomic_s1, l1); // COMPLIANT + atomic_store(&s1_atomic_ptr, l2); // COMPLIANT + + // Undefined behavior, but not banned by this rule. + memset(&atomic_s1, 0, sizeof(atomic_s1)); // COMPLIANT + memset(ptr_atomic_s1, 0, sizeof(*ptr_atomic_s1)); // COMPLIANT + + // OK: whole loads and stores are protected from data-races. + takeCopy(atomic_s1); // COMPLIANT + takeCopy(*ptr_atomic_s1); // COMPLIANT + atomic_s1 = (s1){0}; // COMPLIANT + *ptr_atomic_s1 = (s1){0}; // COMPLIANT + atomic_s1 = *l2; // COMPLIANT + ptr_atomic_s1 = l2; // COMPLIANT + + // Banned: circumvents data-race protection, results in UB. + // atomic_s1.x; // NON-COMPLIANT + // ptr_atomic_s1->x; // NON-COMPLIANT + // atomic_s1.x = 0; // NON-COMPLIANT + // ptr_atomic_s1->x = 0; // NON-COMPLIANT + + // OK: not evaluated. + sizeof(atomic_s1); // COMPLIANT + sizeof(ptr_atomic_s1); // COMPLIANT + // sizeof(atomic_s1.x); // COMPLIANT + // sizeof(ptr_atomic_s1->x); // COMPLIANT + + // All OK: not an atomic struct, but rather an atomic pointer to non-atomic + // struct. + memset(s1_atomic_ptr, 0, sizeof(*s1_atomic_ptr)); // COMPLIANT + takeCopy(*s1_atomic_ptr); // COMPLIANT + *s1_atomic_ptr = (s1){0}; // COMPLIANT + s1_atomic_ptr = l2; // COMPLIANT + s1_atomic_ptr->x; // COMPLIANT + + // Atomic specifier hidden behind a typedef, still atomic: + typedef _Atomic s1 atomic_s1; + atomic_s1 l3; + // l3.x; // NON_COMPLIANT + + // Worst case scenario: a typedef of a volatile const pointer to an atomic + // typedef type. + typedef atomic_s1 *volatile const atomic_s1_specified_ptr; + atomic_s1_specified_ptr l4; + // (l4)->x; // NON_COMPLIANT +} + +#define NOOP(x) (x) +#define DOT_FIELD_ACCESS_X(v) (v).x +#define POINTER_FIELD_ACCESS_X(v) (v)->x +#define GET_X_ATOMIC_S1() atomic_s1.x +#define GET_X_PTR_ATOMIC_S1() atomic_s1.x + +void f2() { + // Banned UB with user macros: + // NOOP(atomic_s1.x); // NON-COMPLIANT + // DOT_FIELD_ACCESS_X(atomic_s1); // NON-COMPLIANT + // POINTER_FIELD_ACCESS_X(ptr_atomic_s1); // NON-COMPLIANT + // GET_X_ATOMIC_S1(); // NON-COMPLIANT + // GET_X_PTR_ATOMIC_S1(); // NON-COMPLIANT + // GET_X_ATOMIC_S1() = 0; // NON-COMPLIANT + // GET_X_PTR_ATOMIC_S1() = 0; // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected new file mode 100644 index 0000000000..4fa06eb069 --- /dev/null +++ b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected @@ -0,0 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,67-75) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,5-18) +| test.c:44:12:44:18 | ... + ... | Atomic variable $@ has a $@ that is unsequenced with $@. | test.c:42:15:42:16 | a1 | a1 | test.c:44:12:44:13 | a1 | previous read | test.c:44:17:44:18 | a1 | another read | +| test.c:46:3:46:37 | ... + ... | Atomic variable $@ has a $@ that is unsequenced with $@. | test.c:42:15:42:16 | a1 | a1 | test.c:46:16:46:17 | a1 | previous read | test.c:46:35:46:36 | a1 | another read | diff --git a/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected.gcc b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected.gcc new file mode 100644 index 0000000000..ccfb4e6a7b --- /dev/null +++ b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.expected.gcc @@ -0,0 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,67-75) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UnsequencedAtomicReads.ql:112,5-18) +| test.c:44:12:44:18 | ... + ... | Atomic variable $@ has a $@ that is unsequenced with $@. | test.c:42:15:42:16 | a1 | a1 | test.c:44:12:44:13 | a1 | previous read | test.c:44:17:44:18 | a1 | another read | +| test.c:46:15:46:17 | & ... | Atomic variable $@ has a $@ that is unsequenced with $@. | test.c:42:15:42:16 | a1 | a1 | test.c:46:16:46:17 | a1 | previous read | test.c:46:35:46:36 | a1 | another read | diff --git a/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.qlref b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.qlref new file mode 100644 index 0000000000..46242df1b0 --- /dev/null +++ b/c/misra/test/rules/RULE-13-2/UnsequencedAtomicReads.qlref @@ -0,0 +1 @@ +rules/RULE-13-2/UnsequencedAtomicReads.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-13-2/UnsequencedSideEffects.expected b/c/misra/test/rules/RULE-13-2/UnsequencedSideEffects.expected new file mode 100644 index 0000000000..b6c704322c --- /dev/null +++ b/c/misra/test/rules/RULE-13-2/UnsequencedSideEffects.expected @@ -0,0 +1,6 @@ +| test.c:8:12:8:18 | ... + ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:8:12:8:13 | l1 | side effect | test.c:8:12:8:13 | l1 | l1 | test.c:8:17:8:18 | l1 | side effect | test.c:8:17:8:18 | l1 | l1 | +| test.c:9:12:9:18 | ... + ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:9:12:9:13 | l1 | side effect | test.c:9:12:9:13 | l1 | l1 | test.c:9:17:9:18 | l2 | side effect | test.c:9:17:9:18 | l2 | l2 | +| test.c:19:3:19:21 | ... = ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:19:8:19:9 | l1 | side effect | test.c:19:8:19:9 | l1 | l1 | test.c:19:13:19:14 | l1 | side effect | test.c:19:13:19:14 | l1 | l1 | +| test.c:21:3:21:5 | call to foo | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:21:7:21:8 | l1 | side effect | test.c:21:7:21:8 | l1 | l1 | test.c:21:11:21:12 | l2 | side effect | test.c:21:11:21:12 | l2 | l2 | +| test.c:27:3:27:5 | call to foo | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:27:7:27:10 | ... ++ | side effect | test.c:27:7:27:8 | l8 | l8 | test.c:27:13:27:14 | l8 | read | test.c:27:13:27:14 | l8 | l8 | +| test.c:37:5:37:13 | ... = ... | The expression contains unsequenced $@ to $@ and $@ to $@. | test.c:37:10:37:12 | ... ++ | side effect | test.c:37:10:37:10 | i | i | test.c:37:10:37:12 | ... ++ | side effect | test.c:37:10:37:10 | i | i | diff --git a/c/misra/test/rules/RULE-13-2/UnsequencedSideEffects.qlref b/c/misra/test/rules/RULE-13-2/UnsequencedSideEffects.qlref new file mode 100644 index 0000000000..0cb8d40dbb --- /dev/null +++ b/c/misra/test/rules/RULE-13-2/UnsequencedSideEffects.qlref @@ -0,0 +1 @@ +rules/RULE-13-2/UnsequencedSideEffects.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-13-2/test.c b/c/misra/test/rules/RULE-13-2/test.c new file mode 100644 index 0000000000..e1be53a037 --- /dev/null +++ b/c/misra/test/rules/RULE-13-2/test.c @@ -0,0 +1,50 @@ +#include + +void foo(int, int); + +void unsequenced_sideeffects1() { + volatile int l1, l2; + + int l3 = l1 + l1; // NON_COMPLIANT + int l4 = l1 + l2; // NON_COMPLIANT + + // Store value of volatile object in temporary non-volatile object. + int l5 = l1; + // Store value of volatile object in temporary non-volatile object. + int l6 = l2; + int l7 = l5 + l6; // COMPLIANT + + int l8, l9; + l1 = l1 & 0x80; // COMPLIANT + l8 = l1 = l1 & 0x80; // NON_COMPLIANT + + foo(l1, l2); // NON_COMPLIANT + // Store value of volatile object in temporary non-volatile object. + l8 = l1; + // Store value of volatile object in temporary non-volatile object. + l9 = l2; + foo(l8, l9); // COMPLIANT + foo(l8++, l8); // NON_COMPLIANT + + int l10 = l8++, l11 = l8++; // COMPLIANT +} + +int g1[], g2[]; +#define test(i) (g1[i] = g2[i]) +void unsequenced_sideeffects2() { + int i; + for (i = 0; i < 10; i++) { + test(i++); // NON_COMPLIANT + } +} + +void atomics() { + _Atomic int a1, a2; + int l3 = a1 + a2; // COMPLIANT + int l4 = a1 + a1; // NON_COMPLIANT + a1 = a1 + 1; // COMPLIANT + atomic_load(&a1) + atomic_load(&a1); // NON_COMPLIANT + atomic_load(&a1) + atomic_load(&a2); // COMPLIANT + atomic_store(&a1, atomic_load(&a1)); // COMPLIANT + atomic_store(&a1, a1); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected b/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected deleted file mode 100644 index 57f90043e1..0000000000 --- a/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.c:7:7:7:12 | ... = ... | Use of an assignment operator's result. | -| test.c:11:11:11:16 | ... = ... | Use of an assignment operator's result. | -| test.c:13:8:13:13 | ... = ... | Use of an assignment operator's result. | diff --git a/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.qlref b/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.qlref deleted file mode 100644 index 16d027d915..0000000000 --- a/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.testref b/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.testref new file mode 100644 index 0000000000..41e225624c --- /dev/null +++ b/c/misra/test/rules/RULE-13-4/ResultOfAnAssignmentOperatorShouldNotBeUsed.testref @@ -0,0 +1 @@ +c/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-13-4/test.c b/c/misra/test/rules/RULE-13-4/test.c deleted file mode 100644 index aeabb60fac..0000000000 --- a/c/misra/test/rules/RULE-13-4/test.c +++ /dev/null @@ -1,14 +0,0 @@ -void test() { - int l1, l2; - int l3[1]; - - l1 = l2; // COMPLIANT - - if (l1 = 1) // NON_COMPLIANT - { - } - - l1 = l3[l2 = 0]; // NON_COMPLIANT - - l1 = l2 = 0; // NON_COMPLIANT -} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-1/LoopOverEssentiallyFloatType.expected b/c/misra/test/rules/RULE-14-1/LoopOverEssentiallyFloatType.expected new file mode 100644 index 0000000000..0dc7c13836 --- /dev/null +++ b/c/misra/test/rules/RULE-14-1/LoopOverEssentiallyFloatType.expected @@ -0,0 +1,3 @@ +| test.c:4:14:4:14 | f | Loop iteration variable f is essentially Floating type. | +| test.c:6:15:6:15 | d | Loop iteration variable d is essentially Floating type. | +| test.c:8:18:8:18 | f | Loop iteration variable f is essentially Floating type. | diff --git a/c/misra/test/rules/RULE-14-1/LoopOverEssentiallyFloatType.qlref b/c/misra/test/rules/RULE-14-1/LoopOverEssentiallyFloatType.qlref new file mode 100644 index 0000000000..e488267d22 --- /dev/null +++ b/c/misra/test/rules/RULE-14-1/LoopOverEssentiallyFloatType.qlref @@ -0,0 +1 @@ +rules/RULE-14-1/LoopOverEssentiallyFloatType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-1/test.c b/c/misra/test/rules/RULE-14-1/test.c new file mode 100644 index 0000000000..50c2240735 --- /dev/null +++ b/c/misra/test/rules/RULE-14-1/test.c @@ -0,0 +1,15 @@ +typedef float float32_t; + +void test_floating_point_loop() { + for (float f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT + } + for (double d = 0.0F; d < 10.0F; d += 0.2F) { // NON_COMPLIANT + } + for (float32_t f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT + } +} + +void test_non_floating_point_loop() { + for (int i = 0; i < 10; i++) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.expected b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.expected new file mode 100644 index 0000000000..fc7fbc7c5f --- /dev/null +++ b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.expected @@ -0,0 +1,7 @@ +| test.c:7:3:8:3 | for(...;...;...) ... | For loop is not well formed, it uses a loop counter '$@' of type floating-point. | test.c:7:14:7:14 | f | f | +| test.c:14:3:15:3 | for(...;...;...) ... | For loop is not well formed, it uses multiple loop counters$@. | file://:0:0:0:0 | | | +| test.c:20:3:21:3 | for(...;...;...) ... | For loop is not well formed, it uses multiple loop counters$@. | file://:0:0:0:0 | | | +| test.c:25:3:26:3 | for(...;...;...) ... | For loop is not well formed, it $@ its loop counter 'i' with an operation that is not an increment or decrement. | test.c:25:28:25:28 | i | updates | +| test.c:38:3:39:3 | for(...;...;...) ... | For loop is not well formed, it $@ the loop counter 'x' irregularly. | test.c:38:26:38:26 | x | updates | +| test.c:52:3:53:3 | for(...;...;...) ... | For loop is not well formed, it updates $@, a loop control variable other than the loop counter, in the update expression of the loop. | test.c:52:28:52:29 | p1 | p1 | +| test.c:64:3:67:3 | for(...;...;...) ... | For loop is not well formed, it $@ the loop counter 'x' in the body of the loop. | test.c:65:5:65:5 | x | updates | diff --git a/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.qlref b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.qlref new file mode 100644 index 0000000000..f65068dfb2 --- /dev/null +++ b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.qlref @@ -0,0 +1 @@ +rules/RULE-14-2/ForLoopNotWellFormed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-2/test.c b/c/misra/test/rules/RULE-14-2/test.c new file mode 100644 index 0000000000..fbeb4be21f --- /dev/null +++ b/c/misra/test/rules/RULE-14-2/test.c @@ -0,0 +1,73 @@ + +#include "stdbool.h" +int g1 = 10; +int f1() { return g1++; } + +void f2() { + for (float f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT + } + for (int i = 0; i < 10; i++) { // COMPLIANT + } +} + +void f3() { + for (int i = 0, j = 0; i < j; i++, j++) { // NON_COMPLIANT + } +} + +void f4() { + int i, j; + for (i = 0, j = 0; i < j; i++, j++) { // NON_COMPLIANT + } +} + +void f5() { + for (int i = 0; i != 10; i += 3) { // NON_COMPLIANT + } + + for (int i = 0; i != 10; i++) { // COMPLIANT + } +} + +void f7() { + for (int i = 0; i < 100; i += g1) { // COMPLIANT + } +} + +void f8() { + for (int x = 0; x < 5; x += f1()) { // NON_COMPLIANT + } +} + +void f9() { + bool l1 = true; + for (int x = 0; (x < 5) && l1; ++x) { // COMPLIANT + l1 = false; + } +} + +bool f10(int p1) { return false; } +void f11() { + bool p1 = true; + for (int x = 0; (x < 5); p1 = f10(++x)) { // NON_COMPLIANT + } +} + +void f12() { + bool l1 = true; + for (int x = 0; (x < 5) && l1; ++x) { // COMPLIANT + } +} + +void f13() { + int l1 = 1; + for (int x = 0; x < 5 && l1 == 9; ++x) { // NON_COMPLIANT + x = x + 2; + g1--; + } +} + +void f14() { + for (int i = 0; i < 10; i += 3) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.expected b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.expected new file mode 100644 index 0000000000..3beb834f84 --- /dev/null +++ b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.expected @@ -0,0 +1,8 @@ +| test.c:4:7:4:11 | ... > ... | Controlling expression in if statement has an invariant value. | +| test.c:15:10:15:16 | ... > ... | Controlling expression in loop statement has an invariant value. | +| test.c:16:9:16:13 | ... > ... | Controlling expression in if statement has an invariant value. | +| test.c:20:20:20:24 | ... < ... | Controlling expression in loop statement has an invariant value. | +| test.c:27:10:27:14 | ... < ... | Controlling expression in loop statement has an invariant value. | +| test.c:37:3:37:6 | 1 | Controlling expression in conditional statement has an invariant value. | +| test.c:38:3:38:3 | 1 | Controlling expression in conditional statement has an invariant value. | +| test.c:45:10:45:26 | ... && ... | Controlling expression in loop statement has an invariant value. | diff --git a/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.qlref b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.qlref new file mode 100644 index 0000000000..dcee0a35ac --- /dev/null +++ b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.qlref @@ -0,0 +1 @@ +rules/RULE-14-3/ControllingExprInvariant.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-3/test.c b/c/misra/test/rules/RULE-14-3/test.c new file mode 100644 index 0000000000..ed8854afd2 --- /dev/null +++ b/c/misra/test/rules/RULE-14-3/test.c @@ -0,0 +1,47 @@ +#include + +void f1(int p1) { + if (2 > 3) { // NON_COMPLIANT + } + + if (p1 > 0) { // COMPLIANT + } + + if (p1 < 10 && p1 > 20) { // NON_COMPLIANT[FALSE_NEGATIVE] + } +} + +void f2() { + while (20 > 10) { // NON_COMPLIANT + if (1 > 2) { + } // NON_COMPLIANT + } + + for (int i = 10; i < 5; i++) { // NON_COMPLIANT + } +} + +void f3() { + while (true) { // COMPLIANT - permitted by exception 1 + } + while (1 < 2) { // NON_COMPLIANT - likely an indication of a bug + } +} + +void f4() { + do { + } while (0u == 1u); // COMPLIANT - by exception 2 +} + +void f5(bool b1) { + true ? 1 : 2; // NON_COMPLIANT + 1 ? 1 : 2; // NON_COMPLIANT + b1 ? 1 : 2; // COMPLIANT +} + +void f6(int p1) { + while (p1 < 10 && p1 > 12) { // NON_COMPLIANT[FALSE_NEGATIVE] + } + while (1 == 0 && p1 > 12) { // NON_COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.expected b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.expected new file mode 100644 index 0000000000..c8a7508f2b --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.expected @@ -0,0 +1,3 @@ +| test.c:7:7:7:8 | l1 | If condition has non boolean essential type int. | +| test.c:9:7:9:8 | call to f1 | If condition has non boolean essential type int. | +| test.c:12:7:12:8 | l2 | If condition has non boolean essential type void *. | diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.qlref b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.qlref new file mode 100644 index 0000000000..cdfd3b5ea3 --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.qlref @@ -0,0 +1 @@ +rules/RULE-14-4/NonBooleanIfCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.expected b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.expected new file mode 100644 index 0000000000..daf7a4be85 --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.expected @@ -0,0 +1,2 @@ +| test_iteration.c:5:20:5:20 | i | Iteration condition has non boolean type int. | +| test_iteration.c:7:10:7:11 | l1 | Iteration condition has non boolean type int. | diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.qlref b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.qlref new file mode 100644 index 0000000000..b7483581b4 --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.qlref @@ -0,0 +1 @@ +rules/RULE-14-4/NonBooleanIterationCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/test.c b/c/misra/test/rules/RULE-14-4/test.c new file mode 100644 index 0000000000..faf7efd83b --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/test.c @@ -0,0 +1,30 @@ +#include +int f1(); +void *f2(); + +void f3() { + int l1 = 1; + if (l1) { // NON_COMPLIANT + } + if (f1()) { // NON_COMPLIANT + } + void *l2 = f2(); + if (l2) { // NON_COMPLIANT + } +} + +void f4() { + int l1 = 1; + if ((bool)l1) { // COMPLIANT + } + + int l2 = 1; + if ((const bool)l2) { // COMPLIANT + } + + if (l2 < 3) { // COMPLIANT + } + + if (true) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/test_iteration.c b/c/misra/test/rules/RULE-14-4/test_iteration.c new file mode 100644 index 0000000000..8ecbb1c1fd --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/test_iteration.c @@ -0,0 +1,15 @@ + + +void f1() { + int l1; + for (int i = 10; i; i++) { // NON_COMPLIANT + } + while (l1) { // NON_COMPLIANT + } +} + +void f2() { + int j = 0; + for (int i = 0; i < 10; i++) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-1/GotoStatementUsed.testref b/c/misra/test/rules/RULE-15-1/GotoStatementUsed.testref new file mode 100644 index 0000000000..1834c6e140 --- /dev/null +++ b/c/misra/test/rules/RULE-15-1/GotoStatementUsed.testref @@ -0,0 +1 @@ +c/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-2-4/test.cpp b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.expected similarity index 100% rename from cpp/autosar/test/rules/M3-2-4/test.cpp rename to c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.expected diff --git a/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.qlref b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.qlref new file mode 100644 index 0000000000..6a35e05154 --- /dev/null +++ b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-2/GotoLabelLocationCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.testref b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.testref new file mode 100644 index 0000000000..0573c85129 --- /dev/null +++ b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.testref b/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.testref new file mode 100644 index 0000000000..cf558d9350 --- /dev/null +++ b/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-4/LoopIterationCondition.expected b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.expected new file mode 100644 index 0000000000..a8dae0f411 --- /dev/null +++ b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.expected @@ -0,0 +1,7 @@ +| test.c:24:3:32:3 | for(...;...;...) ... | Iteration statement contains more than one $@. | test.c:26:7:26:12 | break; | break or goto statement | +| test.c:24:3:32:3 | for(...;...;...) ... | Iteration statement contains more than one $@. | test.c:29:7:29:12 | break; | break or goto statement | +| test.c:24:3:32:3 | for(...;...;...) ... | Iteration statement contains more than one $@. | test.c:31:5:31:12 | goto ... | break or goto statement | +| test.c:38:3:45:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:40:7:40:12 | break; | break or goto statement | +| test.c:38:3:45:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:43:7:43:14 | goto ... | break or goto statement | +| test.c:61:3:72:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:64:7:64:12 | break; | break or goto statement | +| test.c:61:3:72:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:68:7:68:14 | goto ... | break or goto statement | diff --git a/c/misra/test/rules/RULE-15-4/LoopIterationCondition.qlref b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.qlref new file mode 100644 index 0000000000..33ff4561da --- /dev/null +++ b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-4/LoopIterationCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-4/test.c b/c/misra/test/rules/RULE-15-4/test.c new file mode 100644 index 0000000000..2d4a0677a7 --- /dev/null +++ b/c/misra/test/rules/RULE-15-4/test.c @@ -0,0 +1,91 @@ +void f1() { +L1:; + + for (int k = 0; k < 10; k++) { // COMPLIANT + ; + } + + for (int i = 0; i < 10; i++) { // COMPLIANT + if (i > 5) { + break; + } + } + + for (int j = 0; j < 10; j++) { // COMPLIANT + goto L1; + } +} + +void f2() { +L1:; + + int k = 0; + + for (int i = 0; i < 10; i++) { // NON_COMPLIANT + if (i > 5) { + break; + } + if (i < 10) { + break; + } + goto L1; + } + + while (k < 10) { // COMPLIANT + ; + } + + while (k < 10) { // NON_COMPLIANT + if (k > 5) { + break; + } + while (k < 3) { // COMPLIANT + goto L1; + } + } + + while (k < 10) { // COMPLIANT - the nested goto + // only applies to the nested loop + if (k > 5) { + break; + } + while (k < 3) { // COMPLIANT + break; + } + } +} + +void f3(int k) { +L3: + k++; + while (k < 10) { // NON_COMPLIANT - the nested goto + // is an additional exit point for the while loop + if (k > 5) { + break; + } + switch (k) { + case 1: + goto L3; + case 2: + break; + } + } +} + +void f4(int k) { + k++; + while (k < 10) { // COMPLIANT + if (k > 5) { + break; + } + switch (k) { + case 1: + goto L4; + case 2: + k += 1; + L4: + k += 2; + break; + } + } +} diff --git a/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.expected b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.expected new file mode 100644 index 0000000000..dde5d709dd --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.expected @@ -0,0 +1,5 @@ +| test.c:1:6:1:7 | f1 | Function has more than one $@. | test.c:3:5:3:11 | return ... | return statement | +| test.c:1:6:1:7 | f1 | Function has more than one $@. | test.c:5:3:5:9 | return ... | return statement | +| test.c:14:6:14:7 | f3 | The $@ is not the last statement of the function. | test.c:17:3:17:9 | return ... | return statement | +| test.c:21:6:21:7 | f4 | Function has more than one $@. | test.c:23:5:23:11 | return ... | return statement | +| test.c:21:6:21:7 | f4 | Function has more than one $@. | test.c:25:3:25:9 | return ... | return statement | diff --git a/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref new file mode 100644 index 0000000000..fef14a8d42 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-5/FunctionReturnCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/test.c b/c/misra/test/rules/RULE-15-5/test.c new file mode 100644 index 0000000000..cbe36668f5 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/test.c @@ -0,0 +1,29 @@ +void f1(int p1) { // NON_COMPLIANT + if (p1) { + return; + } + return; +} + +void f2(int p1) { // COMPLIANT + if (p1) { + } + return; +} + +void f3(int p1) { // NON_COMPLIANT + if (p1) { + } + return; + p1++; +} + +void f4(int p1) { // NON_COMPLIANT + if (p1) { + return; + } + return; + p1++; +} + +void f5(); // Ignored - no body \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.expected b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.expected new file mode 100644 index 0000000000..263fee14de --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.expected @@ -0,0 +1,4 @@ +| test.c:4:3:5:9 | while (...) ... | Loop body not enclosed within braces. | +| test.c:7:3:8:5 | while (...) ... | Loop body not enclosed within braces. | +| test.c:11:3:12:9 | for(...;...;...) ... | Loop body not enclosed within braces. | +| test.c:14:3:15:5 | while (...) ... | Loop body not enclosed within braces. | diff --git a/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.qlref b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.qlref new file mode 100644 index 0000000000..8cd3c36d27 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-6/LoopCompoundCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.expected b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.expected new file mode 100644 index 0000000000..661d118a69 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.expected @@ -0,0 +1,4 @@ +| test.c:29:3:32:5 | if (...) ... | If statement not enclosed within braces. | +| test.c:34:3:41:7 | if (...) ... | If statement not enclosed within braces. | +| test.c:36:8:41:7 | if (...) ... | If statement not enclosed within braces. | +| test.c:37:5:41:7 | if (...) ... | If statement not enclosed within braces. | diff --git a/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.qlref b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.qlref new file mode 100644 index 0000000000..b62fe0b2c8 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-6/SelectionCompoundCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.expected b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.expected new file mode 100644 index 0000000000..eedc122cd6 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.expected @@ -0,0 +1 @@ +| test.c:75:3:79:5 | switch (...) ... | Switch body not enclosed within braces. | diff --git a/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.qlref b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.qlref new file mode 100644 index 0000000000..c34e33fcbd --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-6/SwitchCompoundCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/test.c b/c/misra/test/rules/RULE-15-6/test.c new file mode 100644 index 0000000000..e8ed064b32 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/test.c @@ -0,0 +1,80 @@ +void f1(); + +void f2(int p1) { + while (p1) // NON_COMPLIANT + f1(); + + while (p1) // NON_COMPLIANT + ; + f1(); + + for (int i = 0; i < p1; i++) // NON_COMPLIANT + f1(); + + while (p1) + ; + { // NON_COMPLIANT + ; + } + + while (p1) { // COMPLIANT + ; + } + for (int i = 0; i < p1; i++) { // COMPLIANT + ; + } +} + +void f3(int p1) { + if (p1) // NON_COMPLIANT + ; + else + ; + + if (p1) // NON_COMPLIANT + ; + else if (p1) // NON_COMPLIANT + if (p1) // NON_COMPLIANT + + if (p1) { // COMPLIANT + ; + } + + if (p1) { // COMPLIANT + ; + } else { // COMPLIANT + ; + } + + if (p1) { // COMPLIANT + ; + } else if (p1) { // COMPLIANT + ; + } else { // COMPLIANT + ; + } +} + +void f4(int p1) { + + switch (p1) { // COMPLIANT + case 0: + while (p1) { + ; + } + break; + case 1: + if (p1) { + ; + } + break; + default: + break; + } + + switch (p1) // NON_COMPLIANT + case 0: + while (p1) { + ; + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-7/IfElseEndCondition.testref b/c/misra/test/rules/RULE-15-7/IfElseEndCondition.testref new file mode 100644 index 0000000000..89caf8f257 --- /dev/null +++ b/c/misra/test/rules/RULE-15-7/IfElseEndCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-1/SwitchCaseStartCondition.testref b/c/misra/test/rules/RULE-16-1/SwitchCaseStartCondition.testref new file mode 100644 index 0000000000..5d2b2ff0d1 --- /dev/null +++ b/c/misra/test/rules/RULE-16-1/SwitchCaseStartCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-1/SwitchStmtNotWellFormed.testref b/c/misra/test/rules/RULE-16-1/SwitchStmtNotWellFormed.testref new file mode 100644 index 0000000000..e37234ee4b --- /dev/null +++ b/c/misra/test/rules/RULE-16-1/SwitchStmtNotWellFormed.testref @@ -0,0 +1 @@ +c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.testref b/c/misra/test/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.testref new file mode 100644 index 0000000000..329212287e --- /dev/null +++ b/c/misra/test/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.testref @@ -0,0 +1 @@ +c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.expected b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.expected new file mode 100644 index 0000000000..cac08cc449 --- /dev/null +++ b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.expected @@ -0,0 +1,4 @@ +| test.c:11:3:11:9 | case ...: | The switch $@ does not terminate with a break statement. | test.c:11:3:11:9 | case ...: | clause | +| test.c:14:3:14:9 | case ...: | The switch $@ does not terminate with a break statement. | test.c:14:3:14:9 | case ...: | clause | +| test.c:26:3:26:10 | default: | The switch $@ does not terminate with a break statement. | test.c:26:3:26:10 | default: | clause | +| test.c:45:3:45:10 | default: | The switch $@ does not terminate with a break statement. | test.c:45:3:45:10 | default: | clause | diff --git a/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.qlref b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.qlref new file mode 100644 index 0000000000..9764f620d0 --- /dev/null +++ b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.qlref @@ -0,0 +1 @@ +rules/RULE-16-3/BreakShallTerminateSwitchClause.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-3/test.c b/c/misra/test/rules/RULE-16-3/test.c new file mode 100644 index 0000000000..ade65474f2 --- /dev/null +++ b/c/misra/test/rules/RULE-16-3/test.c @@ -0,0 +1,50 @@ +void f1(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + break; + case 2: // COMPLIANT + case 3: // COMPLIANT + case 4: // COMPLIANT + break; + case 5: // NON_COMPLIANT + i = j; + j++; + case 6: // NON_COMPLIANT + if (i > j) { + j++; + i++; + break; + } + case 7: // COMPLIANT + if (i > j) { + j++; + i++; + } + break; + default: // NON_COMPLIANT + i++; + } +} + +void f2(int p1) { + switch (p1) { + case 1: // COMPLIANT + break; + case 2: // COMPLIANT + case 3: // COMPLIANT + case 4: // COMPLIANT + default: // COMPLIANT + break; + } +} + +void f3(int p1) { + switch (p1) { + default: // NON_COMPLIANT + p1++; + case 1: // COMPLIANT + break; + } +} diff --git a/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.expected b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.expected new file mode 100644 index 0000000000..6ecfe62c3e --- /dev/null +++ b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.expected @@ -0,0 +1,3 @@ +| test.c:4:3:12:3 | switch (...) ... | $@ statement has missing default clause. | test.c:4:3:12:3 | switch (...) ... | Switch | +| test.c:13:3:22:3 | switch (...) ... | $@ statement has default label that does not terminate in a statement or comment before break statement | test.c:13:3:22:3 | switch (...) ... | Switch | +| test.c:53:3:60:3 | switch (...) ... | $@ statement has default label that does not terminate in a statement or comment before break statement | test.c:53:3:60:3 | switch (...) ... | Switch | diff --git a/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.qlref b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.qlref new file mode 100644 index 0000000000..394a5e941a --- /dev/null +++ b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.qlref @@ -0,0 +1 @@ +rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-4/test.c b/c/misra/test/rules/RULE-16-4/test.c new file mode 100644 index 0000000000..45fa298fc6 --- /dev/null +++ b/c/misra/test/rules/RULE-16-4/test.c @@ -0,0 +1,61 @@ +void f1(int p1) { + int i; + int j; + switch (p1) { // NON COMPLIANT + case 1: + i++; + j++; + break; + case 2: + case 3: + break; + } + switch (p1) { // NON_COMPLIANT + case 1: + i++; + break; + case 2: + j++; + break; + default: + break; + } + switch (p1) { // COMPLIANT + case 1: + i++; + break; + case 2: + j++; + break; + default: + // codeql + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + break; + default: + j++; + break; + } + switch (p1) { // COMPLIANT + case 1: + i++; + break; + default: + j++; + i++; + break; + } + + switch (p1) { // NON_COMPLIANT + case 1: + i++; + break; + default: { + break; + } + } +} diff --git a/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.expected b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.expected new file mode 100644 index 0000000000..a17969c296 --- /dev/null +++ b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.expected @@ -0,0 +1 @@ +| test.c:16:3:16:10 | default: | $@ statement does not have $@ case as first or last switch label. | test.c:12:3:22:3 | switch (...) ... | Switch | test.c:16:3:16:10 | default: | default | diff --git a/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.qlref b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.qlref new file mode 100644 index 0000000000..00e2e8aedf --- /dev/null +++ b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.qlref @@ -0,0 +1 @@ +rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-5/test.c b/c/misra/test/rules/RULE-16-5/test.c new file mode 100644 index 0000000000..37d96bb0af --- /dev/null +++ b/c/misra/test/rules/RULE-16-5/test.c @@ -0,0 +1,34 @@ +void f1(int p1) { + int i; + int j; + switch (p1) { + default: // COMPLIANT + i++; + break; + case 1: + j++; + break; + } + switch (p1) { + case 1: + i++; + break; + default: // NON_COMPLIANT + j++; + break; + case 2: + i++; + break; + } + switch (p1) { + case 1: + i++; + break; + case 2: + j++; + break; + default: // COMPLIANT + i++; + break; + } +} diff --git a/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.expected b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.expected new file mode 100644 index 0000000000..112d0bdd96 --- /dev/null +++ b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.expected @@ -0,0 +1,3 @@ +| test.c:3:3:6:3 | switch (...) ... | $@ statement has a single path. | test.c:3:3:6:3 | switch (...) ... | Switch | +| test.c:8:3:12:3 | switch (...) ... | $@ statement has a single path. | test.c:8:3:12:3 | switch (...) ... | Switch | +| test.c:14:3:19:3 | switch (...) ... | $@ statement has a single path. | test.c:14:3:19:3 | switch (...) ... | Switch | diff --git a/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.qlref b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.qlref new file mode 100644 index 0000000000..6502b855f9 --- /dev/null +++ b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.qlref @@ -0,0 +1 @@ +rules/RULE-16-6/SwitchClauseNumberCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-6/test.c b/c/misra/test/rules/RULE-16-6/test.c new file mode 100644 index 0000000000..38a1457a61 --- /dev/null +++ b/c/misra/test/rules/RULE-16-6/test.c @@ -0,0 +1,49 @@ +void f1(int p1) { + int i = 0; + switch (p1) { // NON_COMPLIANT + default: + break; + } + + switch (p1) { // NON_COMPLIANT + case 1: + default: + break; + } + + switch (p1) { // NON_COMPLIANT + case 1: + case 2: + default: + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + default: + i = 1; + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + case 2: + i = 2; + default: + i = 1; + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + case 2: + i = 2; + case 3: + default: + i = 1; + break; + } +} diff --git a/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.expected b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.expected new file mode 100644 index 0000000000..ac74217dc3 --- /dev/null +++ b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.expected @@ -0,0 +1,2 @@ +| test.c:16:11:16:17 | ... == ... | The condition of this $@ statement has boolean type | test.c:16:3:24:3 | switch (...) ... | switch | +| test.c:28:11:28:24 | ... == ... | The condition of this $@ statement has boolean type | test.c:28:3:36:3 | switch (...) ... | switch | diff --git a/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.qlref b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.qlref new file mode 100644 index 0000000000..dc86fab7e9 --- /dev/null +++ b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.qlref @@ -0,0 +1 @@ +rules/RULE-16-7/SwitchExpressionBoolCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-7/test.c b/c/misra/test/rules/RULE-16-7/test.c new file mode 100644 index 0000000000..74b394bc6d --- /dev/null +++ b/c/misra/test/rules/RULE-16-7/test.c @@ -0,0 +1,37 @@ + +void f1(int p1) { + + switch (p1) // COMPLIANT + { + case 1: + break; + case 2: + break; + default: + break; + } +} + +void f2(int p1) { + switch (p1 == 1) // NON_COMPLIANT + { + case 0: + break; + case 1: + break; + default: + break; + } +} + +void f3(char *p1) { + switch (p1 == "CODEQL") // NON_COMPLIANT + { + case 0: + break; + case 1: + break; + default: + break; + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.expected b/c/misra/test/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.expected new file mode 100644 index 0000000000..a94e37baa4 --- /dev/null +++ b/c/misra/test/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.expected @@ -0,0 +1,4 @@ +| test.c:6:15:6:16 | f4 | The function f4 is declared _noreturn but has a return type of int. | +| test.c:19:15:19:16 | f8 | The function f8 is declared _noreturn but has a return type of int. | +| test.c:24:17:24:18 | f9 | The function f9 is declared _noreturn but has a return type of void *. | +| test.c:26:31:26:33 | f10 | The function f10 is declared _noreturn but has a return type of int. | diff --git a/c/misra/test/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.qlref b/c/misra/test/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.qlref new file mode 100644 index 0000000000..6726b6957a --- /dev/null +++ b/c/misra/test/rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.qlref @@ -0,0 +1 @@ +rules/RULE-17-10/NonVoidReturnTypeOfNoreturnFunction.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-10/test.c b/c/misra/test/rules/RULE-17-10/test.c new file mode 100644 index 0000000000..b5fc988af2 --- /dev/null +++ b/c/misra/test/rules/RULE-17-10/test.c @@ -0,0 +1,26 @@ +#include "stdlib.h" + +void f1(); // COMPLIANT +int f2(); // COMPLIANT +_Noreturn void f3(); // COMPLIANT +_Noreturn int f4(); // NON-COMPLIANT + +void f5() { // COMPLIANT +} + +int f6() { // COMPLIANT + return 0; +} + +_Noreturn void f7() { // COMPLIANT + abort(); +} + +_Noreturn int f8() { // NON-COMPLIANT + abort(); + return 0; +} + +_Noreturn void *f9(); // NON-COMPLIANT + +__attribute__((noreturn)) int f10(); // NON-COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.expected b/c/misra/test/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.expected new file mode 100644 index 0000000000..ecb77a477c --- /dev/null +++ b/c/misra/test/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.expected @@ -0,0 +1,6 @@ +| test.c:7:6:7:21 | test_noreturn_f2 | The function test_noreturn_f2 cannot return and should be declared as _Noreturn. | +| test.c:18:6:18:21 | test_noreturn_f4 | The function test_noreturn_f4 cannot return and should be declared as _Noreturn. | +| test.c:47:6:47:21 | test_noreturn_f8 | The function test_noreturn_f8 cannot return and should be declared as _Noreturn. | +| test.c:63:6:63:22 | test_noreturn_f10 | The function test_noreturn_f10 cannot return and should be declared as _Noreturn. | +| test.c:97:6:97:22 | test_noreturn_f15 | The function test_noreturn_f15 cannot return and should be declared as _Noreturn. | +| test.c:101:6:101:22 | test_noreturn_f16 | The function test_noreturn_f16 cannot return and should be declared as _Noreturn. | diff --git a/c/misra/test/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.qlref b/c/misra/test/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.qlref new file mode 100644 index 0000000000..feb6f40804 --- /dev/null +++ b/c/misra/test/rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.qlref @@ -0,0 +1 @@ +rules/RULE-17-11/FunctionWithNoReturningBranchShouldBeNoreturn.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-11/test.c b/c/misra/test/rules/RULE-17-11/test.c new file mode 100644 index 0000000000..73227accb9 --- /dev/null +++ b/c/misra/test/rules/RULE-17-11/test.c @@ -0,0 +1,104 @@ +#include "stdlib.h" + +_Noreturn void test_noreturn_f1(int i) { // COMPLIANT + abort(); +} + +void test_noreturn_f2(int i) { // NON_COMPLIANT + abort(); +} + +_Noreturn void test_noreturn_f3(int i) { // COMPLIANT + if (i > 0) { + abort(); + } + exit(1); +} + +void test_noreturn_f4(int i) { // NON_COMPLIANT + if (i > 0) { + abort(); + } + exit(1); +} + +void test_noreturn_f5(int i) { // COMPLIANT + if (i > 0) { + return; + } + exit(1); +} + +void test_noreturn_f6(int i) { // COMPLIANT + if (i > 0) { + abort(); + } + if (i < 0) { + abort(); + } +} + +void test_noreturn_f7(int i) { // COMPLIANT + if (i > 0) { + abort(); + } +} + +void test_noreturn_f8(int i) { // NON_COMPLIANT + if (i > 0) { + abort(); + } else { + abort(); + } +} + +_Noreturn void test_noreturn_f9(int i) { // COMPLIANT + if (i > 0) { + abort(); + } else { + abort(); + } +} + +void test_noreturn_f10(int i) { // NON_COMPLIANT + if (i > 0) { + abort(); + } + while (1) { + i = 5; + } +} + +_Noreturn void test_noreturn_f11(int i) { // COMPLIANT + if (i > 0) { + abort(); + } + while (1) { + i = 5; + } +} + +void test_noreturn_f12(); // COMPLIANT + +__attribute__((noreturn)) void test_noreturn_f13(int i) { // COMPLIANT + abort(); +} + +// Allowed by exception. It is undefined behavior for main() to be declared with +// noreturn. +int main(int argc, char *argv[]) { // COMPLIANT + abort(); +} + +_Noreturn void test_noreturn_f14(int i) { // COMPLIANT + test_noreturn_f1(i); +} + +void test_noreturn_f15(int i) { // NON_COMPLIANT + test_noreturn_f1(i); +} + +void test_noreturn_f16(int i) { // NON_COMPLIANT + // Infinite tail recursion + test_noreturn_f16(i); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected new file mode 100644 index 0000000000..5a37cbd97e --- /dev/null +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected @@ -0,0 +1,13 @@ +| test.c:14:25:14:29 | func2 | The address of function func2 is taken without the & operator. | +| test.c:15:25:15:29 | func3 | The address of function func3 is taken without the & operator. | +| test.c:21:12:21:16 | func1 | The address of function func1 is taken without the & operator. | +| test.c:38:3:38:7 | func1 | The address of function func1 is taken without the & operator. | +| test.c:39:3:39:7 | func2 | The address of function func2 is taken without the & operator. | +| test.c:57:13:57:17 | func1 | The address of function func1 is taken without the & operator. | +| test.c:58:21:58:25 | func2 | The address of function func2 is taken without the & operator. | +| test.c:59:13:59:17 | func1 | The address of function func1 is taken without the & operator. | +| test.c:59:20:59:24 | func2 | The address of function func2 is taken without the & operator. | +| test.c:67:11:67:15 | func1 | The address of function func1 is taken without the & operator. | +| test.c:68:12:68:16 | func1 | The address of function func1 is taken without the & operator. | +| test.c:69:12:69:16 | func1 | The address of function func1 is taken without the & operator. | +| test.c:71:18:71:22 | func1 | The address of function func1 is taken without the & operator. | diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref new file mode 100644 index 0000000000..f0a4753620 --- /dev/null +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref @@ -0,0 +1 @@ +rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-12/test.c b/c/misra/test/rules/RULE-17-12/test.c new file mode 100644 index 0000000000..5ab5a4984d --- /dev/null +++ b/c/misra/test/rules/RULE-17-12/test.c @@ -0,0 +1,107 @@ +void func1() {} +void func2(int x, char *y) {} + +typedef struct { +} s; + +int func3() { return 0; } + +typedef void (*func_ptr_t1)(); +typedef void (*func_ptr_t2)(int x, char *y); +typedef s (*func_ptr_t3)(); + +func_ptr_t1 func_ptr1 = &func1; // COMPLIANT +func_ptr_t2 func_ptr2 = func2; // NON-COMPLIANT +func_ptr_t3 func_ptr3 = func3 + 0; // NON-COMPLIANT + +void take_func(func_ptr_t1 f1, func_ptr_t2 f2); + +func_ptr_t1 returns_func(int x) { + if (x == 0) { + return func1; // NON-COMPLIANT + } else if (x == 1) { + return &func1; // COMPLIANT + } + + return returns_func(0); // COMPLIANT +} + +#define MACRO_IDENTITY(f) (f) +#define MACRO_INVOKE_RISKY(f) (f()) +#define MACRO_INVOKE_IMPROVED(f) ((f)()) +#define MACRO_INVOKE_AND_USE_AS_TOKEN(f) f(0, #f) + +void test() { + func1(); // COMPLIANT + func2(1, "hello"); // COMPLIANT + + func1; // NON-COMPLIANT + func2; // NON-COMPLIANT + + &func1; // COMPLIANT + &func2; // COMPLIANT + + (func1)(); // COMPLIANT + (func2)(1, "hello"); // COMPLIANT + + &(func1); // COMPLIANT + &(func2); // COMPLIANT + + (&func1)(); // COMPLIANT + (&func2)(1, "hello"); // COMPLIANT + + (func1()); // COMPLIANT + (func2(1, "hello")); // COMPLIANT + + take_func(&func1, &func2); // COMPLIANT + take_func(func1, &func2); // NON-COMPLIANT + take_func(&func1, func2); // NON-COMPLIANT + take_func(func1, func2); // NON-COMPLIANT + + returns_func(0); // COMPLIANT + returns_func(0)(); // COMPLIANT + (returns_func(0))(); // COMPLIANT + + (void *)&func1; // COMPLIANT + (void *)(&func1); // COMPLIANT + (void *)func1; // NON-COMPLIANT + (void *)(func1); // NON-COMPLIANT + ((void *)func1); // NON-COMPLIANT + + MACRO_IDENTITY(func1); // NON-COMPLIANT + MACRO_IDENTITY(func1)(); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_IDENTITY(&func1); // COMPLIANT + MACRO_IDENTITY (&func1)(); // COMPLIANT + + MACRO_INVOKE_RISKY(func3); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_INVOKE_IMPROVED(func3); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_INVOKE_IMPROVED(&func3); // COMPLIANT + + MACRO_INVOKE_AND_USE_AS_TOKEN(func1); // COMPLIANT + + // Function pointers are exempt from this rule. + func_ptr1(); // COMPLIANT + func_ptr2(1, "hello"); // COMPLIANT + func_ptr1; // COMPLIANT + func_ptr2; // COMPLIANT + &func_ptr1; // COMPLIANT + &func_ptr2; // COMPLIANT + (func_ptr1)(); // COMPLIANT + (func_ptr2)(1, "hello"); // COMPLIANT + (*func_ptr1)(); // COMPLIANT + (*func_ptr2)(1, "hello"); // COMPLIANT + take_func(func_ptr1, func_ptr2); // COMPLIANT + (void *)func_ptr1; // COMPLIANT + (void *)&func_ptr1; // COMPLIANT + (void *)(&func_ptr1); // COMPLIANT + (void *)func_ptr1; // COMPLIANT + (void *)(func_ptr1); // COMPLIANT + ((void *)func_ptr1); // COMPLIANT + MACRO_IDENTITY(func_ptr1); // COMPLIANT + MACRO_IDENTITY(func_ptr1)(); // COMPLIANT + MACRO_IDENTITY(&func_ptr1); // COMPLIANT + (*MACRO_IDENTITY(&func_ptr1))(); // COMPLIANT + MACRO_INVOKE_RISKY(func_ptr3); // COMPLIANT + MACRO_INVOKE_IMPROVED(func_ptr3); // COMPLIANT + MACRO_INVOKE_IMPROVED(*&func_ptr3); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.expected b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.expected new file mode 100644 index 0000000000..06b8b5b762 --- /dev/null +++ b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.expected @@ -0,0 +1,3 @@ +| test.c:8:3:8:4 | call to f3 | f3 calls itself directly. | test.c:7:6:7:7 | f3 | f3 | +| test.c:15:3:15:4 | call to f2 | f5 is indirectly recursive via this call to $@. | test.c:17:6:17:7 | f2 | f2 | +| test.c:18:3:18:4 | call to f5 | f2 is indirectly recursive via this call to $@. | test.c:14:6:14:7 | f5 | f5 | diff --git a/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.qlref b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.qlref new file mode 100644 index 0000000000..da361b35f4 --- /dev/null +++ b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.qlref @@ -0,0 +1 @@ +rules/RULE-17-2/RecursiveFunctionCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-2/test.c b/c/misra/test/rules/RULE-17-2/test.c new file mode 100644 index 0000000000..800921c1e2 --- /dev/null +++ b/c/misra/test/rules/RULE-17-2/test.c @@ -0,0 +1,19 @@ +void f1(); +void f2(); +void f4(int p1) { // COMPLIANT + f1(); +} + +void f3() { + f3(); // NON_COMPLIANT +} +void f6() { + f3(); // NON_COMPLIANT +} + +void f5() { + f2(); // NON_COMPLIANT +} +void f2() { + f5(); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected new file mode 100644 index 0000000000..8b53c721bb --- /dev/null +++ b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected @@ -0,0 +1,2 @@ +| test.c:4:1:4:2 | declaration of f2 | Function declaration is implicit. | +| test.c:12:15:12:15 | declaration of f3 | Function declaration is implicit. | diff --git a/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.qlref b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.qlref new file mode 100644 index 0000000000..24df819bf7 --- /dev/null +++ b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.qlref @@ -0,0 +1 @@ +rules/RULE-17-3/FunctionDeclaredImplicitly.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-3/test.c b/c/misra/test/rules/RULE-17-3/test.c new file mode 100644 index 0000000000..6c9dd64836 --- /dev/null +++ b/c/misra/test/rules/RULE-17-3/test.c @@ -0,0 +1,13 @@ +// semmle-extractor-options:--clang -std=c11 -nostdinc +// -I../../../../common/test/includes/standard-library +double f1(double x); // COMPLIANT +f2(double x); // NON_COMPLIANT + +void f() { + double l = 1; + double l1 = f1(l); + + double l2 = f2(l); + + double l3 = f3(l); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-4/NonVoidFunctionReturnCondition.testref b/c/misra/test/rules/RULE-17-4/NonVoidFunctionReturnCondition.testref new file mode 100644 index 0000000000..6ddd134ce3 --- /dev/null +++ b/c/misra/test/rules/RULE-17-4/NonVoidFunctionReturnCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected b/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected new file mode 100644 index 0000000000..174c6aa40f --- /dev/null +++ b/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected @@ -0,0 +1,15 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArrayFunctionArgumentNumberOfElements.ql:48,36-44) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArrayFunctionArgumentNumberOfElements.ql:49,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArrayFunctionArgumentNumberOfElements.ql:51,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArrayFunctionArgumentNumberOfElements.ql:56,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArrayFunctionArgumentNumberOfElements.ql:72,28-36) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArrayFunctionArgumentNumberOfElements.ql:72,51-59) +| test.c:18:6:18:6 | 0 | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | +| test.c:19:6:19:7 | ar | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | +| test.c:21:6:21:9 | ar2p | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | +| test.c:26:9:26:9 | 0 | The function argument does not have a sufficient number or elements declared in the $@. | test.c:2:20:2:21 | ar | parameter | +| test.c:27:9:27:10 | ar | The function argument does not have a sufficient number or elements declared in the $@. | test.c:2:20:2:21 | ar | parameter | +| test.c:29:9:29:12 | ar2p | The function argument does not have a sufficient number or elements declared in the $@. | test.c:2:20:2:21 | ar | parameter | +| test.c:61:6:61:8 | ar2 | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | +| test.c:62:6:62:9 | ar2b | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | +| test.c:63:6:63:9 | ar2p | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | diff --git a/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.qlref b/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.qlref new file mode 100644 index 0000000000..41a893a32c --- /dev/null +++ b/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.qlref @@ -0,0 +1 @@ +rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-5/test.c b/c/misra/test/rules/RULE-17-5/test.c new file mode 100644 index 0000000000..8e76a55642 --- /dev/null +++ b/c/misra/test/rules/RULE-17-5/test.c @@ -0,0 +1,66 @@ +void f1(int ar[3]); +void f2(int a, int ar[3]); +void f3(int *ar); +void f4(int a, int *ar); + +void t1() { + int *ar; + + int ar2[3] = {1, 2}; + int *ar2p = ar2; + + int ar3[3] = {1, 2, 3}; + int *ar3p = ar3; + + int ar4[4] = {1, 2, 3}; + int *ar4p = ar4; + + f1(0); // NON_COMPLIANT + f1(ar); // NON_COMPLIANT + f1(ar2); // COMPLIANT + f1(ar2p); // NON_COMPLIANT + f1(ar3); // COMPLIANT + f1(ar3p); // COMPLIANT + f1(ar4); // COMPLIANT + + f2(0, 0); // NON_COMPLIANT + f2(0, ar); // NON_COMPLIANT + f2(0, ar2); // COMPLIANT + f2(0, ar2p); // NON_COMPLIANT + f2(0, ar3); // COMPLIANT + f2(0, ar3p); // COMPLIANT + f2(0, ar4); // COMPLIANT + + f3(0); // COMPLIANT + f3(ar); // COMPLIANT + f3(ar2); // COMPLIANT + f3(ar2p); // COMPLIANT + f3(ar3); // COMPLIANT + f3(ar3p); // COMPLIANT + f3(ar4); // COMPLIANT + + f4(0, 0); // COMPLIANT + f4(0, ar); // COMPLIANT + f4(0, ar2); // COMPLIANT + f4(0, ar2p); // COMPLIANT + f4(0, ar3); // COMPLIANT + f4(0, ar3p); // COMPLIANT + f4(0, ar4); // COMPLIANT +} + +void t2() { + int ar2[2] = {1, 2}; + int ar2b[2] = {1, 2, 3}; + int *ar2p = ar2; + int ar3[3]; + ar3[0] = 1; + ar3[1] = 2; + ar3[2] = 3; + int ar4[4] = {1, 2, 3, 4}; + + f1(ar2); // NON_COMPLIANT + f1(ar2b); // NON_COMPLIANT + f1(ar2p); // NON_COMPLIANT + f1(ar3); // COMPLIANT + f1(ar4); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-6/UseOfArrayStatic.expected b/c/misra/test/rules/RULE-17-6/UseOfArrayStatic.expected new file mode 100644 index 0000000000..ddf892a15c --- /dev/null +++ b/c/misra/test/rules/RULE-17-6/UseOfArrayStatic.expected @@ -0,0 +1,3 @@ +| test.c:2:33:2:36 | arr2 | Parameter arr2 is declared as an array type using the static keyword. | +| test.c:3:39:3:42 | arr3 | Parameter arr3 is declared as an array type using the static keyword. | +| test.c:5:9:5:12 | arr4 | Parameter arr4 is declared as an array type using the static keyword. | diff --git a/c/misra/test/rules/RULE-17-6/UseOfArrayStatic.qlref b/c/misra/test/rules/RULE-17-6/UseOfArrayStatic.qlref new file mode 100644 index 0000000000..ecb67b2dfb --- /dev/null +++ b/c/misra/test/rules/RULE-17-6/UseOfArrayStatic.qlref @@ -0,0 +1 @@ +rules/RULE-17-6/UseOfArrayStatic.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-6/test.c b/c/misra/test/rules/RULE-17-6/test.c new file mode 100644 index 0000000000..14f04b5a9f --- /dev/null +++ b/c/misra/test/rules/RULE-17-6/test.c @@ -0,0 +1,8 @@ +void test_array(int arr1[10]) {} // COMPLIANT +void test_array_uses_static(int arr2[static 11]) {} // NON_COMPLIANT +void test_array_uses_static_multi(int arr3[static 12][5]) {} // NON_COMPLIANT +void test_array_uses_static_again( + int arr4[11]) { // COMPLIANT[FALSE_POSITIVE] - apparently a CodeQL + // bug where the static is associated with the fixed + // size +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.expected b/c/misra/test/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.expected new file mode 100644 index 0000000000..95b54ed874 --- /dev/null +++ b/c/misra/test/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.expected @@ -0,0 +1,2 @@ +| test.c:6:3:6:4 | call to f2 | The value returned by this call shall be used or cast to `void`. | +| test.c:15:3:15:9 | call to expression | The value returned by this call shall be used or cast to `void`. | diff --git a/c/misra/test/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.qlref b/c/misra/test/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.qlref new file mode 100644 index 0000000000..a365eed3d8 --- /dev/null +++ b/c/misra/test/rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.qlref @@ -0,0 +1 @@ +rules/RULE-17-7/ValueReturnedByAFunctionNotUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-7/test.c b/c/misra/test/rules/RULE-17-7/test.c new file mode 100644 index 0000000000..1d31639275 --- /dev/null +++ b/c/misra/test/rules/RULE-17-7/test.c @@ -0,0 +1,18 @@ +void f1() {} +int f2() { return 0; } + +int t1() { + f1(); + f2(); // NON_COMPLIANT + (void)f2(); // COMPLIANT + int a = f2(); // COMPLIANT + a = f2(); // COMPLIANT + + void (*fp1)(void) = &f1; + int (*fp2)(void) = &f2; + + (*f1)(); // COMPLIANT + (*f2)(); // NON_COMPLIANT + (void)(*f2)(); // COMPLIANT + a = (*f2)(); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-9/ReturnStatementInNoreturnFunction.testref b/c/misra/test/rules/RULE-17-9/ReturnStatementInNoreturnFunction.testref new file mode 100644 index 0000000000..09a6d90538 --- /dev/null +++ b/c/misra/test/rules/RULE-17-9/ReturnStatementInNoreturnFunction.testref @@ -0,0 +1 @@ +c/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.expected b/c/misra/test/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.expected new file mode 100644 index 0000000000..76b3da5eb0 --- /dev/null +++ b/c/misra/test/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.expected @@ -0,0 +1,17 @@ +| test.c:17:11:17:12 | definition of p5 | Parameter p5 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int' | test.c:17:15:17:16 | p0 | p0 | +| test.c:18:11:18:12 | definition of p6 | Parameter p6 is declared with variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:18:18:18:19 | p0 | p0 | +| test.c:19:11:19:12 | definition of p7 | Parameter p7 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int[2]' | test.c:19:15:19:16 | p0 | p0 | +| test.c:20:11:20:12 | definition of p8 | Parameter p8 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int[]' | test.c:20:15:20:16 | p0 | p0 | +| test.c:20:11:20:12 | definition of p8 | Parameter p8 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int[]' | test.c:20:19:20:20 | p0 | p0 | +| test.c:24:12:24:13 | definition of p9 | Parameter p9 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int *' | test.c:24:16:24:17 | p0 | p0 | +| test.c:25:13:25:15 | definition of p10 | Parameter p10 is declared with variably-modified type, with inner variable length array of non constant size $@ and element type 'int *' | test.c:25:18:25:19 | p0 | p0 | +| test.c:28:12:28:14 | definition of p11 | Parameter p11 is adjusted to variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:28:21:28:22 | p0 | p0 | +| test.c:32:17:32:19 | definition of p13 | Parameter p13 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'const int' | test.c:32:22:32:23 | p0 | p0 | +| test.c:33:17:33:19 | definition of p14 | Parameter p14 is declared with variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:33:22:33:23 | p0 | p0 | +| test.c:40:12:40:14 | definition of p17 | Parameter p17 is declared with variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:40:24:40:25 | p0 | p0 | +| test.c:41:14:41:16 | definition of p18 | Parameter p18 is declared with variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:41:27:41:28 | p0 | p0 | +| test.c:68:9:68:11 | definition of p27 | Parameter p27 is adjusted to variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:68:13:68:14 | p0 | p0 | +| test.c:68:9:68:11 | definition of p27 | Parameter p27 is adjusted to variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:68:17:68:18 | p0 | p0 | +| test.c:74:8:74:9 | definition of l3 | Variable l3 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int' | test.c:74:12:74:13 | p0 | p0 | +| test.c:79:15:79:16 | definition of l4 | Variable l4 is declared with variably-modified type, pointer to variable length array of non constant size $@ and element type 'int' | test.c:79:19:79:20 | p0 | p0 | +| test.c:84:16:84:18 | declaration of td3 | Declaration td3 is declared with variably-modified type, with inner variable length array of non constant size $@ and element type 'int' | test.c:84:21:84:22 | p0 | p0 | diff --git a/c/misra/test/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.qlref b/c/misra/test/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.qlref new file mode 100644 index 0000000000..1a60cfacca --- /dev/null +++ b/c/misra/test/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.qlref @@ -0,0 +1 @@ +rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-10/test.c b/c/misra/test/rules/RULE-18-10/test.c new file mode 100644 index 0000000000..565b51e8de --- /dev/null +++ b/c/misra/test/rules/RULE-18-10/test.c @@ -0,0 +1,106 @@ +#define CONSTANT 1 + +int g1[3]; // COMPLIANT +int (*g2)[3]; // COMPLIANT +int (*g3)[CONSTANT]; // COMPLIANT + +void f1( + int p0, + + // Basic fixed length array types: + int p1[3], // COMPLIANT + int (*p2)[3], // COMPLIANT + int (*p3)[2][3], // COMPLIANT + int (*p4)[CONSTANT], // COMPLIANT + + // Basic pointers to VMTs: + int (*p5)[p0], // NON-COMPLIANT + int (*p6)[2][p0], // NON-COMPLIANT + int (*p7)[p0][2], // NON-COMPLIANT + int (*p8)[p0][p0], // NON-COMPLIANT + + // Types referring to pointers to VMTs: + // - pointer to pointer to VMT + int(*(*p9)[p0]), // NON-COMPLIANT + int(*(**p10)[p0]), // NON-COMPLIANT + + // - array of pointers to VMT + int (*(p11[3]))[p0], // NON-COMPLIANT + + // - const VMTs, const array-to-pointer adjustment + const int p12[p0], // COMPLIANT + const int (*p13)[p0], // NON-COMPLIANT + int (*const p14)[p0], // NON-COMPLIANT + + // - function types with argument that is a pointer to a VMT + int p15(int (*inner)[p0]), // NON-COMPLIANT[FALSE_NEGATIVE] + int (*p16)(int (*inner)[p0]), // NON-COMPLIANT[FALSE_NEGATIVE] + + // - function types that returns a pointer to a VMT + int (*(p17(void)))[p0], // NON-COMPLIANT + int (*((*p18)(void)))[p0], // NON-COMPLIANT + + // - structs cannot contain a VMT as a member. + struct { + int g1[3]; // COMPLIANT + int(*g2)[3]; // COMPLIANT + int(*g3)[CONSTANT]; // COMPLIANT + // Pointer to VMT (`int (*g4)[p0]`) is not allowed. + } p19, + + // - unions cannot contain a VMT as a member. + union { + int g1[3]; // COMPLIANT + int(*g2)[3]; // COMPLIANT + int(*g3)[CONSTANT]; // COMPLIANT + // Pointer to VMT (`int (*g4)[p0]`) is not allowed. + } p20, + + // Unknown array length types: + int p21[], // COMPLIANT + int p22[][2], // COMPLIANT + int (*p23)[], // COMPLIANT + // int (*p24)[2][], // doesn't compile + int (*p25)[][2], // COMPLIANT + + // VLA types that are rewritten as pointers: + int p26[p0], // COMPLIANT + int p27[p0][p0] // NON-COMPLIANT +) { + // Local variables may contain pointers to VMTs: + int l0[p0]; // COMPLIANT + int(*l1)[]; // COMPLIANT + int(*l2)[3]; // COMPLIANT + int(*l3)[p0]; // NON-COMPLIANT + + int l6[10]; + + // A pointer to a VMT may be declared `static`. + static int(*l4)[p0]; // NON-COMPLIANT + + // Block scope typedefs may refer to VMTs + typedef int(*td1)[3]; // COMPLIANT + typedef int(*td2)[]; // COMPLIANT + typedef int(*td3)[p0]; // NON-COMPLIANT + + td3 l5; // NON-COMPLIANT +} + +// Function prototypes may contain VMTs using '*' syntax: +void f2(int (*p1)[3], // COMPLIANT + int (*p2)[*], // NON-COMPLIANT[FALSE_NEGATIVE] + int (*p3)[2][*], // NON-COMPLIANT[FALSE_NEGATIVE] + int (*p4)[*][2], // NON-COMPLIANT[FALSE_NEGATIVE] + int (*p5)[*][*] // NON-COMPLIANT[FALSE_NEGATIVE] +); + +#define CONFUSING_MACRO() \ + int x; \ + int(*vla)[x]; \ + int(*not_vla)[]; + +void f3() { + // We cannot report `vla` in this macro without a false positive for + // `not_vla`. + CONFUSING_MACRO() // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.expected b/c/misra/test/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.expected new file mode 100644 index 0000000000..99c5a91645 --- /dev/null +++ b/c/misra/test/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.expected @@ -0,0 +1,4 @@ +| test.c:29:3:29:10 | ... = ... | Thread local object $@ address copied to static object $@. | test.c:8:19:8:20 | test.c:8:19:8:20 | t1 | test.c:12:6:12:7 | test.c:12:6:12:7 | g2 | +| test.c:55:3:55:14 | ... = ... | Thread local object $@ address copied to static object $@. | test.c:10:17:10:18 | test.c:10:17:10:18 | t3 | test.c:13:3:13:4 | test.c:13:3:13:4 | g3 | +| test.c:152:3:152:21 | ... = ... | Thread local object $@ address copied to static object $@. | test.c:152:16:152:20 | test.c:152:16:152:20 | & ... | test.c:12:6:12:7 | test.c:12:6:12:7 | g2 | +| test.c:155:3:155:23 | ... = ... | Thread local object $@ address copied to static object $@. | test.c:155:18:155:22 | test.c:155:18:155:22 | & ... | test.c:13:3:13:4 | test.c:13:3:13:4 | g3 | diff --git a/c/misra/test/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.qlref b/c/misra/test/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.qlref new file mode 100644 index 0000000000..90cdd7a43f --- /dev/null +++ b/c/misra/test/rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.qlref @@ -0,0 +1 @@ +rules/RULE-18-6/ThreadLocalObjectAddressCopiedToGlobalObject.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-6/test.c b/c/misra/test/rules/RULE-18-6/test.c new file mode 100644 index 0000000000..13b1070397 --- /dev/null +++ b/c/misra/test/rules/RULE-18-6/test.c @@ -0,0 +1,169 @@ +#include + +typedef struct { + int *p; + int m +} s; + +_Thread_local int t1; +_Thread_local int *t2; +_Thread_local s t3; +int g1; +int *g2; +s g3; + +void f1() { + // Regular object accesses + t1 = t1; // COMPLIANT + t1 = *t2; // COMPLIANT + t1 = g1; // COMPLIANT + t1 = *g2; // COMPLIANT + g1 = t1; // COMPLIANT + g1 = *t2; // COMPLIANT + g1 = g1; // COMPLIANT + g1 = *g2; // COMPLIANT + t2 = &t1; // COMPLIANT + t2 = t2; // COMPLIANT + t2 = &g1; // COMPLIANT + t2 = g2; // COMPLIANT + g2 = &t1; // NON-COMPLIANT + g2 = t2; // COMPLIANT + g2 = &g1; // COMPLIANT + g2 = g2; // COMPLIANT + *t2 = t1; // COMPLIANT + *t2 = *t2; // COMPLIANT + *t2 = g1; // COMPLIANT + *t2 = *g2; // COMPLIANT + *g2 = t1; // COMPLIANT + *g2 = *t2; // COMPLIANT + *g2 = g1; // COMPLIANT + *g2 = *g2; // COMPLIANT + + // Subobject accesses + t3.m = t3.m; // COMPLIANT + t3.m = *t3.p; // COMPLIANT + t3.m = g3.m; // COMPLIANT + t3.m = *g3.p; // COMPLIANT + g3.m = t3.m; // COMPLIANT + g3.m = *t3.p; // COMPLIANT + g3.m = g3.m; // COMPLIANT + g3.m = *g3.p; // COMPLIANT + t3.p = &t3.m; // COMPLIANT + t3.p = t3.p; // COMPLIANT + t3.p = &g3.m; // COMPLIANT + t3.p = g3.p; // COMPLIANT + g3.p = &t3.m; // NON-COMPLIANT + g3.p = t3.p; // COMPLIANT + g3.p = &g3.m; // COMPLIANT + g3.p = g3.p; // COMPLIANT + *t3.p = t3.m; // COMPLIANT + *t3.p = *t3.p; // COMPLIANT + *t3.p = g3.m; // COMPLIANT + *t3.p = *g3.p; // COMPLIANT + *g3.p = t3.m; // COMPLIANT + *g3.p = *t3.p; // COMPLIANT + *g3.p = g3.m; // COMPLIANT + *g3.p = *g3.p; // COMPLIANT + + // Storing values in locals (automatic storage duration) + int l1; + int *l2; + s l3; + + l1 = l1; // COMPLIANT + l1 = *l2; // COMPLIANT + l1 = l3.m; // COMPLIANT + l1 = *l3.p; // COMPLIANT + l1 = t1; // COMPLIANT + l1 = *t2; // COMPLIANT + l1 = t3.m; // COMPLIANT + l1 = *t3.p; // COMPLIANT + l1 = g1; // COMPLIANT + l1 = *g2; // COMPLIANT + l1 = g3.m; // COMPLIANT + l1 = *g3.p; // COMPLIANT + l2 = &l1; // COMPLIANT + l2 = l2; // COMPLIANT + l2 = &l3.m; // COMPLIANT + l2 = l3.p; // COMPLIANT + l2 = &t1; // COMPLIANT + l2 = t2; // COMPLIANT + l2 = &t3.m; // COMPLIANT + l2 = t3.p; // COMPLIANT + l2 = &g1; // COMPLIANT + l2 = g2; // COMPLIANT + l2 = &g3.m; // COMPLIANT + l2 = g3.p; // COMPLIANT + *l2 = l1; // COMPLIANT + *l2 = *l2; // COMPLIANT + *l2 = l3.m; // COMPLIANT + *l2 = *l3.p; // COMPLIANT + *l2 = t1; // COMPLIANT + *l2 = *t2; // COMPLIANT + *l2 = t3.m; // COMPLIANT + *l2 = *t3.p; // COMPLIANT + *l2 = g1; // COMPLIANT + *l2 = *g2; // COMPLIANT + *l2 = g3.m; // COMPLIANT + *l2 = *g3.p; // COMPLIANT + l3.m = l1; // COMPLIANT + l3.m = *l2; // COMPLIANT + l3.m = l3.m; // COMPLIANT + l3.m = *l3.p; // COMPLIANT + l3.m = t1; // COMPLIANT + l3.m = *t2; // COMPLIANT + l3.m = t3.m; // COMPLIANT + l3.m = *t3.p; // COMPLIANT + l3.m = g1; // COMPLIANT + l3.m = *g2; // COMPLIANT + l3.m = g3.m; // COMPLIANT + l3.m = *g3.p; // COMPLIANT + l3.p = &l1; // COMPLIANT + l3.p = l2; // COMPLIANT + l3.p = &l3.m; // COMPLIANT + l3.p = l3.p; // COMPLIANT + l3.p = &t1; // COMPLIANT + l3.p = t2; // COMPLIANT + l3.p = &t3.m; // COMPLIANT + l3.p = t3.p; // COMPLIANT + l3.p = &g1; // COMPLIANT + l3.p = g2; // COMPLIANT + l3.p = &g3.m; // COMPLIANT + l3.p = g3.p; // COMPLIANT + *l3.p = l1; // COMPLIANT + *l3.p = *l2; // COMPLIANT + *l3.p = l3.m; // COMPLIANT + *l3.p = *l3.p; // COMPLIANT + *l3.p = t1; // COMPLIANT + *l3.p = *t2; // COMPLIANT + *l3.p = t3.m; // COMPLIANT + *l3.p = *t3.p; // COMPLIANT + *l3.p = g1; // COMPLIANT + *l3.p = *g2; // COMPLIANT + *l3.p = g3.m; // COMPLIANT + *l3.p = *g3.p; // COMPLIANT + + // Storing local values in globals is covered by the shared query. +} + +tss_t tss1; +void f2() { + g1 = *(int *)tss_get(&tss1); // COMPLIANT + g2 = tss_get(&tss1); // NON-COMPLIANT + *g2 = *(int *)tss_get(&tss1); // COMPLIANT + g3.m = *(int *)tss_get(&tss1); // COMPLIANT + g3.p = tss_get(&tss1); // NON-COMPLIANT + *g3.p = *(int *)tss_get(&tss1); // COMPLIANT + g1 = ((s *)tss_get(&tss1))->m; // COMPLIANT + g1 = *((s *)tss_get(&tss1))->p; // COMPLIANT + g2 = &((s *)tss_get(&tss1))->m; // NON-COMPLIANT[false negative] + g2 = *((s *)tss_get(&tss1))->p; // COMPLIANT + *g2 = ((s *)tss_get(&tss1))->m; // COMPLIANT + *g2 = *((s *)tss_get(&tss1))->p; // COMPLIANT + g3.m = ((s *)tss_get(&tss1))->m; // COMPLIANT + g3.m = *((s *)tss_get(&tss1))->p; // COMPLIANT + g3.p = &((s *)tss_get(&tss1))->m; // NON-COMPLIANT[false negative] + g3.p = *((s *)tss_get(&tss1))->p; // COMPLIANT + *g3.p = ((s *)tss_get(&tss1))->m; // COMPLIANT + *g3.p = *((s *)tss_get(&tss1))->p; // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-7/FlexibleArrayMembersDeclared.expected b/c/misra/test/rules/RULE-18-7/FlexibleArrayMembersDeclared.expected new file mode 100644 index 0000000000..377a9ca487 --- /dev/null +++ b/c/misra/test/rules/RULE-18-7/FlexibleArrayMembersDeclared.expected @@ -0,0 +1 @@ +| test.c:8:7:8:7 | b | Flexible array member declared. | diff --git a/c/misra/test/rules/RULE-18-7/FlexibleArrayMembersDeclared.qlref b/c/misra/test/rules/RULE-18-7/FlexibleArrayMembersDeclared.qlref new file mode 100644 index 0000000000..b0f0bc00e6 --- /dev/null +++ b/c/misra/test/rules/RULE-18-7/FlexibleArrayMembersDeclared.qlref @@ -0,0 +1 @@ +rules/RULE-18-7/FlexibleArrayMembersDeclared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-7/test.c b/c/misra/test/rules/RULE-18-7/test.c new file mode 100644 index 0000000000..385a947090 --- /dev/null +++ b/c/misra/test/rules/RULE-18-7/test.c @@ -0,0 +1,20 @@ +struct s { + int a; + int b[1]; // COMPLIANT +}; + +struct s1 { + int a; + int b[]; // NON_COMPLIANT +}; + +struct s2 { + int a; + int b[2]; // COMPLIANT +}; + +struct s3 { + int a; + int b[1]; // COMPLIANT + int a1; +}; \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.expected b/c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.expected new file mode 100644 index 0000000000..af73daccfd --- /dev/null +++ b/c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.expected @@ -0,0 +1,8 @@ +| test.c:6:7:6:8 | a1 | Variable length array of element type 'int' with non-constant size $@. | test.c:6:10:6:14 | ... + ... | ... + ... | +| test.c:7:7:7:8 | a2 | Variable length array of element type 'int' with non-constant size $@. | test.c:7:10:7:10 | n | n | +| test.c:8:7:8:8 | a3 | Variable length array of element type 'int[]' with non-constant size $@. | test.c:8:13:8:13 | n | n | +| test.c:12:7:12:8 | a7 | Variable length array of element type 'int[1]' with non-constant size $@. | test.c:12:10:12:10 | n | n | +| test.c:20:14:20:15 | t1 | Variable length array of element type 'int' with non-constant size $@. | test.c:18:26:18:26 | n | n | +| test.c:21:14:21:15 | t2 | Variable length array of element type 'int' with non-constant size $@. | test.c:18:26:18:26 | n | n | +| test.c:22:14:22:15 | t3 | Variable length array of element type 'int' with non-constant size $@. | test.c:18:26:18:26 | n | n | +| test.c:22:14:22:15 | t3 | Variable length array of element type 'vlaTypedef' with non-constant size $@. | test.c:22:17:22:17 | x | x | diff --git a/c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.qlref b/c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.qlref new file mode 100644 index 0000000000..9193742acd --- /dev/null +++ b/c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.qlref @@ -0,0 +1 @@ +rules/RULE-18-8/VariableLengthArrayTypesUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-8/test.c b/c/misra/test/rules/RULE-18-8/test.c new file mode 100644 index 0000000000..ea639de271 --- /dev/null +++ b/c/misra/test/rules/RULE-18-8/test.c @@ -0,0 +1,41 @@ +#define TEST 1 + +void f(int n) { + int a[1]; // COMPLIANT + int x = 1; + int a1[1 + x]; // NON_COMPLIANT - not integer constant expr + int a2[n]; // NON_COMPLIANT + int a3[1][n]; // NON_COMPLIANT + int a4[] = {1}; // COMPLIANT - not a VLA + int a5[TEST]; // COMPLIANT + int a6[1 + 1]; // COMPLIANT + int a7[n][1]; // NON_COMPLIANT + int(*a8)[n]; // COMPLIANT - pointer to VLA, see RULE-18-10 + + extern int e1[]; // COMPLIANT + + // A typedef is not a VLA. + typedef int vlaTypedef[n]; // COMPLIANT + // The declarations using the typedef may or may not be VLAs. + vlaTypedef t1; // NON_COMPLIANT + vlaTypedef t2[1]; // NON_COMPLIANT + vlaTypedef t3[x]; // NON_COMPLIANT + vlaTypedef *t4; // COMPLIANT +} + +void f1(int n, + // Parameter array types are adjusted to pointers + int p1[n], // COMPLIANT + // Pointers to variably-modified types are not VLAs. + int p2[n][n], + int p3[], // array of unknown length is converted to pointer + int p4[][n] // array of unknown length are not VLAs. +) {} + +struct s { + // Structs must have at least one non-flexible array member. + int foo; + + // Flexible array members are not VLAs. + int flexibleArrayMember[]; // COMPLIANT +}; \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.expected b/c/misra/test/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.expected new file mode 100644 index 0000000000..cf741ed16c --- /dev/null +++ b/c/misra/test/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.expected @@ -0,0 +1,30 @@ +| test.c:45:3:45:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:45:3:45:8 | call to get_s1 | call to get_s1 | +| test.c:46:3:46:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:46:3:46:8 | call to get_s1 | call to get_s1 | +| test.c:47:7:47:24 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:47:7:47:12 | call to get_s1 | call to get_s1 | +| test.c:48:4:48:21 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:48:4:48:9 | call to get_s1 | call to get_s1 | +| test.c:49:4:49:21 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:49:4:49:9 | call to get_s1 | call to get_s1 | +| test.c:50:3:50:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:50:3:50:8 | call to get_s1 | call to get_s1 | +| test.c:51:3:51:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:51:3:51:8 | call to get_s1 | call to get_s1 | +| test.c:52:3:52:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:52:3:52:8 | call to get_s1 | call to get_s1 | +| test.c:53:3:53:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:53:3:53:8 | call to get_s1 | call to get_s1 | +| test.c:54:3:54:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:54:3:54:8 | call to get_s1 | call to get_s1 | +| test.c:55:8:55:25 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:55:8:55:13 | call to get_s1 | call to get_s1 | +| test.c:56:3:56:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:56:3:56:8 | call to get_s1 | call to get_s1 | +| test.c:57:8:57:25 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:57:8:57:13 | call to get_s1 | call to get_s1 | +| test.c:58:3:58:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:58:3:58:8 | call to get_s1 | call to get_s1 | +| test.c:59:3:59:20 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:59:3:59:8 | call to get_s1 | call to get_s1 | +| test.c:60:15:60:32 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:60:15:60:20 | call to get_s1 | call to get_s1 | +| test.c:61:16:61:33 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:61:16:61:21 | call to get_s1 | call to get_s1 | +| test.c:62:23:62:40 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:62:23:62:28 | call to get_s1 | call to get_s1 | +| test.c:63:7:63:24 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:63:7:63:12 | call to get_s1 | call to get_s1 | +| test.c:64:16:64:33 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:64:16:64:21 | call to get_s1 | call to get_s1 | +| test.c:65:15:65:32 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:65:15:65:20 | call to get_s1 | call to get_s1 | +| test.c:66:16:66:33 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:66:16:66:21 | call to get_s1 | call to get_s1 | +| test.c:67:23:67:40 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:67:23:67:28 | call to get_s1 | call to get_s1 | +| test.c:89:3:89:30 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:89:3:89:8 | call to get_s2 | call to get_s2 | +| test.c:90:3:90:36 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:90:3:90:8 | call to get_s2 | call to get_s2 | +| test.c:91:15:91:42 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:91:15:91:20 | call to get_s2 | call to get_s2 | +| test.c:92:15:92:48 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:92:15:92:20 | call to get_s2 | call to get_s2 | +| test.c:114:15:114:27 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:112:9:112:11 | arr | arr | test.c:114:16:114:22 | ... = ... | ... = ... | +| test.c:116:15:116:37 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:116:16:116:26 | ... ? ... : ... | ... ? ... : ... | +| test.c:117:15:117:31 | array to pointer conversion | Array to pointer conversion of array $@ from temporary object $@. | test.c:3:13:3:21 | const_arr | const_arr | test.c:117:16:117:20 | ... , ... | ... , ... | diff --git a/c/misra/test/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.qlref b/c/misra/test/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.qlref new file mode 100644 index 0000000000..d2db40e77c --- /dev/null +++ b/c/misra/test/rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.qlref @@ -0,0 +1 @@ +rules/RULE-18-9/ArrayToPointerConversionOfTemporaryObject.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.expected b/c/misra/test/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.expected new file mode 100644 index 0000000000..4c961ee994 --- /dev/null +++ b/c/misra/test/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.expected @@ -0,0 +1,15 @@ +| test.c:80:3:80:17 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:80:12:80:14 | arr | arr | test.c:80:3:80:8 | call to get_s1 | call to get_s1 | +| test.c:81:3:81:17 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:81:12:81:14 | arr | arr | test.c:81:3:81:8 | call to get_s1 | call to get_s1 | +| test.c:82:3:82:17 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:82:12:82:14 | arr | arr | test.c:82:3:82:8 | call to get_s1 | call to get_s1 | +| test.c:83:3:83:17 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:83:12:83:14 | arr | arr | test.c:83:3:83:8 | call to get_s1 | call to get_s1 | +| test.c:84:5:84:19 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:84:14:84:16 | arr | arr | test.c:84:5:84:10 | call to get_s1 | call to get_s1 | +| test.c:93:3:93:27 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:93:22:93:24 | arr | arr | test.c:93:3:93:8 | call to get_s2 | call to get_s2 | +| test.c:94:3:94:27 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:94:22:94:24 | arr | arr | test.c:94:3:94:8 | call to get_s2 | call to get_s2 | +| test.c:140:3:140:23 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:140:12:140:20 | arr_union | arr_union | test.c:140:3:140:8 | call to get_s3 | call to get_s3 | +| test.c:141:3:141:24 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:141:12:141:21 | arr_struct | arr_struct | test.c:141:3:141:8 | call to get_s3 | call to get_s3 | +| test.c:142:3:142:24 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:142:12:142:21 | arr_struct | arr_struct | test.c:142:3:142:8 | call to get_s3 | call to get_s3 | +| test.c:143:3:143:24 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:143:12:143:21 | arr_struct | arr_struct | test.c:143:3:143:8 | call to get_s3 | call to get_s3 | +| test.c:144:3:144:24 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:144:12:144:21 | arr_struct | arr_struct | test.c:144:3:144:8 | call to get_s3 | call to get_s3 | +| test.c:145:4:145:25 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:145:13:145:22 | arr_struct | arr_struct | test.c:145:4:145:9 | call to get_s3 | call to get_s3 | +| test.c:149:3:149:19 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:149:12:149:16 | arr2d | arr2d | test.c:149:3:149:8 | call to get_s3 | call to get_s3 | +| test.c:150:4:150:20 | access to array | Modifiable lvalue produced by subscripting array member $@ of temporary lifetime object $@ | test.c:150:13:150:17 | arr2d | arr2d | test.c:150:4:150:9 | call to get_s3 | call to get_s3 | diff --git a/c/misra/test/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.qlref b/c/misra/test/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.qlref new file mode 100644 index 0000000000..c1fb0bd2d4 --- /dev/null +++ b/c/misra/test/rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.qlref @@ -0,0 +1 @@ +rules/RULE-18-9/ModifiableLValueSubscriptedWithTemporaryLifetime.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-18-9/test.c b/c/misra/test/rules/RULE-18-9/test.c new file mode 100644 index 0000000000..d5eb5ec35e --- /dev/null +++ b/c/misra/test/rules/RULE-18-9/test.c @@ -0,0 +1,154 @@ +struct s1 { + int m1; + const int const_arr[10]; + int arr[10]; +}; + +struct s1 get_s1(); + +struct s2 { + struct s1 member_s1; + struct s1 const const_s1_arr[10]; + struct s1 *s1ptr; + struct s1 s1_arr[10]; +}; + +struct s2 get_s2(); +struct s2 *get_s2_ptr(); + +void use_int(int x) {} +void use_int_ptr(int *x) {} + +void f(void) { + struct s1 l1; + + // Auto lifetime, allowed: + l1.const_arr + 1; // COMPLIANT + l1.const_arr - 1; // COMPLIANT + &l1.const_arr; // COMPLIANT + use_int_ptr(l1.const_arr); // COMPLIANT + l1.arr[0] = 1; // COMPLIANT + + // Extern lifetime, allowed: + extern struct s1 g1; + g1.const_arr + 1; // COMPLIANT + g1.const_arr - 1; // COMPLIANT + &g1.const_arr; // COMPLIANT + use_int_ptr(g1.const_arr); // COMPLIANT + g1.arr[0] = 1; // COMPLIANT + + // Temporary lifetime, no conversion: + get_s1().const_arr; // COMPLIANT - not used as a value. + get_s1().m1 + 1; // COMPLIANT - not an array. + + // Temporary lifetime, array to pointer conversions: + get_s1().const_arr + 1; // NON-COMPLIANT + get_s1().const_arr - 1; // NON-COMPLIANT + 1 + get_s1().const_arr; // NON-COMPLIANT + *get_s1().const_arr; // NON-COMPLIANT + !get_s1().const_arr; // NON-COMPLIANT + get_s1().const_arr < 1; // NON-COMPLIANT + get_s1().const_arr <= 1; // NON-COMPLIANT + get_s1().const_arr > 1; // NON-COMPLIANT + get_s1().const_arr >= 1; // NON-COMPLIANT + get_s1().const_arr == 1; // NON-COMPLIANT + 1 == get_s1().const_arr; // NON-COMPLIANT + get_s1().const_arr && 1; // NON-COMPLIANT + 1 && get_s1().const_arr; // NON-COMPLIANT + get_s1().const_arr || 1; // NON-COMPLIANT + get_s1().const_arr ? 1 : 1; // NON-COMPLIANT + use_int_ptr(get_s1().const_arr); // NON-COMPLIANT + use_int_ptr((get_s1().const_arr)); // NON-COMPLIANT + use_int_ptr((void *)get_s1().const_arr); // NON-COMPLIANT + (1, get_s1().const_arr) + 1; // NON-COMPLIANT + int *local = get_s1().const_arr; // NON-COMPLIANT + (struct s1){get_s1().const_arr}; // NON-COMPLIANT + (struct s2){{get_s1().const_arr}}; // NON-COMPLIANT + struct s1 local2 = {get_s1().const_arr}; // NON-COMPLIANT + + // Results are not 'used' as a value. + (void *)get_s1().const_arr; // COMPLIANT + sizeof(get_s1().const_arr); // COMPLIANT + get_s1().const_arr, 1; // COMPLIANT + 1, get_s1().const_arr; // COMPLIANT + (get_s1().const_arr); // COMPLIANT + + get_s1().const_arr[0]; // COMPLIANT - subscripted value not modifiable + get_s1().arr[0]; // COMPLIANT - subscripted value not used as modifiable + use_int(get_s1().const_arr[0]); // COMPLIANT + use_int(get_s1().arr[0]); // COMPLIANT + get_s1().arr[0] = 1; // NON-COMPLIANT + get_s1().arr[0] -= 1; // NON-COMPLIANT + get_s1().arr[0]--; // NON-COMPLIANT + get_s1().arr[0]++; // NON-COMPLIANT + &(get_s1().arr[0]); // NON-COMPLIANT + + struct s2 l2; + + // Deeper accesses: + get_s2().member_s1.const_arr + 1; // NON-COMPLIANT + get_s2().const_s1_arr[0].const_arr + 1; // NON-COMPLIANT + use_int_ptr(get_s2().member_s1.const_arr); // NON-COMPLIANT + use_int_ptr(get_s2().const_s1_arr[0].const_arr); // NON-COMPLIANT + get_s2().member_s1.arr[0] = 1; // NON-COMPLIANT + get_s2().s1_arr[0].arr[0] = 1; // NON-COMPLIANT + get_s2().member_s1.const_arr[0]; // COMPLIANT + get_s2().const_s1_arr[0].const_arr[0]; // COMPLIANT + get_s2().s1_arr[0].const_arr[0]; // COMPLIANT + get_s2().s1ptr->const_arr[0]; // COMPLIANT + use_int(get_s2().member_s1.const_arr[0]); // COMPLIANT + use_int(get_s2().const_s1_arr[0].const_arr[0]); // COMPLIANT + use_int(get_s2().s1ptr->const_arr[0]); // COMPLIANT + + // Pointer members of a struct don't have temporary lifetime. + get_s2().s1ptr->const_arr + 1; // COMPLIANT + use_int_ptr(get_s2().s1ptr->const_arr); // COMPLIANT + get_s2().s1ptr->arr[0] = 1; // COMPLIANT + get_s2_ptr()->member_s1.const_arr + 1; // COMPLIANT + get_s2_ptr()->member_s1.arr[0] = 1; // COMPLIANT + + // Other types of non-lvalue types + struct { + int arr[10]; + } l3; + use_int_ptr((l3 = l3).arr); // NON-COMPLIANT + use_int_ptr(((struct s1)l1).const_arr); // NON-COMPLIANT[FALSE_NEGATIVE] + use_int_ptr((1 ? l1 : l1).const_arr); // NON-COMPLIANT + use_int_ptr((0, l1).const_arr); // NON-COMPLIANT + use_int_ptr((l2.s1ptr++)->const_arr); // COMPLIANT + use_int_ptr((--l2.s1ptr)->const_arr); // COMPLIANT +} + +// Additional modifiable lvalue tests +struct s3 { + struct s4 { + struct s5 { + struct s6 { + int x; + } m1; + } m1; + } arr_struct[1]; + + union u1 { + int x; + } arr_union[1]; + + int arr2d[1][1]; +} get_s3(); + +void f2(void) { + get_s3().arr_union[0].x = 1; // NON_COMPLIANT + get_s3().arr_struct[0] = (struct s4){0}; // NON_COMPLIANT + get_s3().arr_struct[0].m1 = (struct s5){0}; // NON_COMPLIANT + get_s3().arr_struct[0].m1.m1 = (struct s6){0}; // NON_COMPLIANT + get_s3().arr_struct[0].m1.m1.x = 1; // NON_COMPLIANT + &get_s3().arr_struct[0].m1.m1.x; // NON_COMPLIANT + get_s3().arr_struct[0].m1.m1.x + 1; // COMPLIANT + + get_s3().arr2d[1][1] + 1; // COMPLIANT + get_s3().arr2d[1][1] = 1; // NON_COMPLIANT + &get_s3().arr2d[1]; // NON_COMPLIANT + // The following cases are missing an ArrayToPointerConversion + use_int_ptr(get_s3().arr2d[1]); // NON_COMPLIANT[FALSE NEGATIVE] + get_s3().arr2d[1] + 1; // NON_COMPLIANT[FALSE NEGATIVE] +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.expected b/c/misra/test/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.expected new file mode 100644 index 0000000000..bc8f4461ec --- /dev/null +++ b/c/misra/test/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.expected @@ -0,0 +1 @@ +| test.c:55:3:55:18 | ... = ... | An object $@ assigned to overlapping object $@. | test.c:55:9:55:10 | m2 | m2 | test.c:55:17:55:18 | m1 | m1 | diff --git a/c/misra/test/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.qlref b/c/misra/test/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.qlref new file mode 100644 index 0000000000..088eafa869 --- /dev/null +++ b/c/misra/test/rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.qlref @@ -0,0 +1 @@ +rules/RULE-19-1/ObjectAssignedToAnOverlappingObject.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.expected b/c/misra/test/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.expected new file mode 100644 index 0000000000..fe2db5318c --- /dev/null +++ b/c/misra/test/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.expected @@ -0,0 +1,5 @@ +| test.c:8:3:8:8 | call to memcpy | The object to copy $@ overlaps the object to copy $@. | test.c:8:17:8:21 | & ... | from | test.c:8:10:8:14 | & ... | to | +| test.c:10:3:10:8 | call to memcpy | The object to copy $@ overlaps the object to copy $@. | test.c:10:17:10:21 | & ... | from | test.c:10:10:10:14 | & ... | to | +| test.c:11:3:11:8 | call to memcpy | The object to copy $@ overlaps the object to copy $@. | test.c:11:17:11:17 | o | from | test.c:11:10:11:14 | ... + ... | to | +| test.c:13:3:13:8 | call to memcpy | The object to copy $@ overlaps the object to copy $@. | test.c:13:17:13:21 | ... + ... | from | test.c:13:10:13:14 | ... + ... | to | +| test.c:57:3:57:8 | call to memcpy | The object to copy $@ overlaps the object to copy $@. | test.c:57:21:57:26 | & ... | from | test.c:57:10:57:18 | & ... | to | diff --git a/c/misra/test/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.qlref b/c/misra/test/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.qlref new file mode 100644 index 0000000000..c371b9bad2 --- /dev/null +++ b/c/misra/test/rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.qlref @@ -0,0 +1 @@ +rules/RULE-19-1/ObjectCopiedToAnOverlappingObject.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-19-1/test.c b/c/misra/test/rules/RULE-19-1/test.c new file mode 100644 index 0000000000..7f445993cc --- /dev/null +++ b/c/misra/test/rules/RULE-19-1/test.c @@ -0,0 +1,59 @@ +#include + +int o[10]; +void g(void) { + + o[2] = o[0]; // COMPLIANT + + memcpy(&o[1], &o[0], 2); // NON_COMPLIANT + memcpy(&o[2], &o[0], 2); // COMPLIANT + memcpy(&o[2], &o[1], 2); // NON_COMPLIANT + memcpy(o + 1, o, 2); // NON_COMPLIANT + memcpy(o + 2, o, 2); // COMPLIANT + memcpy(o + 2, o + 1, 2); // NON_COMPLIANT + + // Exception 1 + int *p = &o[0]; + int *q = &o[0]; + + *p = *q; // COMPLIANT + memcpy(&o[0], &o[0], 2); // COMPLIANT + memcpy(o, o, 2); // COMPLIANT + + // Exception 2 + memmove(&o[1], &o[0], 2u * sizeof(o[0])); // COMPLIANT +} + +struct s1 { + int m1[10]; +}; +struct s2 { + int m1; + struct s1 m2; +}; +union u { + struct s1 m1; + struct s2 m2; +} u1; + +typedef struct { + char buf[8]; +} Union_t; +union { + unsigned char uc[24]; + struct { + Union_t prefix; + Union_t suffix; + } fnv; + struct { + unsigned char padding[16]; + Union_t suffix; + } diff; +} u2; + +void test_unions() { + u1.m2.m2 = u1.m1; // NON_COMPLIANT + + memcpy(&u1.m2.m2, &u1.m1, sizeof(u1.m1)); // NON_COMPLIANT + memcpy(&u2.diff.suffix, &u2.fnv.suffix, sizeof(u2.fnv.suffix)); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.expected b/c/misra/test/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.expected index f7dbb31393..4070091ca2 100644 --- a/c/misra/test/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.expected +++ b/c/misra/test/rules/RULE-19-2/UnionKeywordShouldNotBeUsed.expected @@ -1 +1 @@ -| test.c:2:9:2:9 | union | Use of banned 'union' keyword. | +| test.c:2:9:2:9 | (unnamed class/struct/union) | Use of banned 'union' keyword. | diff --git a/c/misra/test/rules/RULE-2-1/UnreachableCode.testref b/c/misra/test/rules/RULE-2-1/UnreachableCode.testref new file mode 100644 index 0000000000..882b3a6e7c --- /dev/null +++ b/c/misra/test/rules/RULE-2-1/UnreachableCode.testref @@ -0,0 +1 @@ +c/common/test/rules/unreachablecode/UnreachableCode.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-2/DeadCode.expected b/c/misra/test/rules/RULE-2-2/DeadCode.expected new file mode 100644 index 0000000000..e25a5a97ef --- /dev/null +++ b/c/misra/test/rules/RULE-2-2/DeadCode.expected @@ -0,0 +1,9 @@ +| test.c:15:3:15:11 | ... = ... | Assignment to dead1 is unused and has no side effects. | test.c:15:3:15:11 | ... = ... | | +| test.c:16:3:16:11 | ... = ... | Assignment to dead2 is unused and has no side effects. | test.c:16:3:16:11 | ... = ... | | +| test.c:19:3:19:7 | ... + ... | Result of operation is unused and has no side effects. | test.c:19:3:19:7 | ... + ... | | +| test.c:21:3:21:17 | call to no_side_effects | Result of operation is unused and has no side effects from call to function $@. | test.c:2:5:2:19 | no_side_effects | no_side_effects | +| test.c:23:3:23:30 | (int)... | Cast operation is unused. | test.c:23:3:23:30 | (int)... | | +| test.c:24:3:24:25 | (int)... | Cast operation is unused. | test.c:24:3:24:25 | (int)... | | +| test.c:27:4:27:18 | call to no_side_effects | Result of operation is unused and has no side effects from call to function $@. | test.c:2:5:2:19 | no_side_effects | no_side_effects | +| test.c:37:3:37:27 | call to no_side_effects | Result of operation is unused and has no side effects from call to function $@. | test.c:2:5:2:19 | no_side_effects | no_side_effects | +| test.c:38:7:38:31 | call to no_side_effects | Result of operation is unused and has no side effects from call to function $@. | test.c:2:5:2:19 | no_side_effects | no_side_effects | diff --git a/c/misra/test/rules/RULE-2-2/DeadCode.qlref b/c/misra/test/rules/RULE-2-2/DeadCode.qlref new file mode 100644 index 0000000000..761e04d51b --- /dev/null +++ b/c/misra/test/rules/RULE-2-2/DeadCode.qlref @@ -0,0 +1 @@ +rules/RULE-2-2/DeadCode.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-2/test.c b/c/misra/test/rules/RULE-2-2/test.c new file mode 100644 index 0000000000..f8248c52d2 --- /dev/null +++ b/c/misra/test/rules/RULE-2-2/test.c @@ -0,0 +1,42 @@ +int may_have_side_effects(); +int no_side_effects(int x) { return 1 + 2; } +int no_side_effects_nondeterministic(); + +int test_dead_code(int x) { + int live1 = may_have_side_effects(), + live2 = may_have_side_effects(); // COMPLIANT + int live3 = 0, + live4 = may_have_side_effects(); // COMPLIANT + int live5 = 0, live6 = 0; // COMPLIANT + live5 = 1; // COMPLIANT + live6 = 2; // COMPLIANT + + int dead1 = 0, dead2 = 0; // COMPLIANT - init not considered by this rule + dead1 = 1; // NON_COMPLIANT - useless assignment + dead2 = 1; // NON_COMPLIANT - useless assignment + + may_have_side_effects(); // COMPLIANT + 1 + 2; // NON_COMPLIANT + + no_side_effects(x); // NON_COMPLIANT + + (int)may_have_side_effects(); // NON_COMPLIANT + (int)no_side_effects(x); // NON_COMPLIANT + (void)no_side_effects(x); // COMPLIANT + (may_have_side_effects()); // COMPLIANT + (no_side_effects(x)); // NON_COMPLIANT + +#define FULL_STMT_NO_SIDE_EFFECTS no_side_effects(1); +#define PART_STMT_NO_SIDE_EFFECTS no_side_effects(1) +#define BLOCK_SOME_SIDE_EFFECTS \ + { \ + may_have_side_effects(); \ + no_side_effects(1); \ + } + + FULL_STMT_NO_SIDE_EFFECTS // NON_COMPLIANT + PART_STMT_NO_SIDE_EFFECTS; // NON_COMPLIANT + BLOCK_SOME_SIDE_EFFECTS; // COMPLIANT + + return live5 + live6; // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-3/UnusedTypeDeclarations.testref b/c/misra/test/rules/RULE-2-3/UnusedTypeDeclarations.testref new file mode 100644 index 0000000000..d07c9f7df2 --- /dev/null +++ b/c/misra/test/rules/RULE-2-3/UnusedTypeDeclarations.testref @@ -0,0 +1 @@ +c/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-4/UnusedTagDeclaration.expected b/c/misra/test/rules/RULE-2-4/UnusedTagDeclaration.expected new file mode 100644 index 0000000000..abd602e9c8 --- /dev/null +++ b/c/misra/test/rules/RULE-2-4/UnusedTagDeclaration.expected @@ -0,0 +1,7 @@ +| test.c:4:8:4:9 | S2 | struct S2 has an unused tag. | +| test.c:7:16:7:17 | S3 | struct S3 has an unused tag. | +| test.c:17:6:17:7 | E1 | struct E1 has an unused tag. | +| test.c:31:10:31:11 | S7 | struct S7 has an unused tag. | +| test.c:50:8:50:10 | S10 | struct S10 has an unused tag. | +| test.c:66:3:66:14 | S13 | struct S13 has an unused tag. | +| test.c:79:8:79:10 | s14 | struct s14 has an unused tag. | diff --git a/c/misra/test/rules/RULE-2-4/UnusedTagDeclaration.qlref b/c/misra/test/rules/RULE-2-4/UnusedTagDeclaration.qlref new file mode 100644 index 0000000000..26753b3e85 --- /dev/null +++ b/c/misra/test/rules/RULE-2-4/UnusedTagDeclaration.qlref @@ -0,0 +1 @@ +rules/RULE-2-4/UnusedTagDeclaration.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-4/test.c b/c/misra/test/rules/RULE-2-4/test.c new file mode 100644 index 0000000000..64d05a1cc2 --- /dev/null +++ b/c/misra/test/rules/RULE-2-4/test.c @@ -0,0 +1,84 @@ +struct S1 { // COMPLIANT + int x; +}; +struct S2 { // NON_COMPLIANT + int x; +}; +typedef struct S3 { // NON_COMPLIANT + int x; +} T1; +typedef struct S4 { // COMPLIANT + int x; +} T2; +struct S5 { // COMPLIANT + int x; +}; +typedef struct S5 T3; +enum E1 { state1, state2 }; // NON_COMPLIANT +enum E2 { state3, state4 }; // COMPLIANT +struct { // COMPLIANT - no tag + int x; +} s6; + +void test() { + struct S1 s1; + T1 t1; + t1.x = 0; // Field access on struct S3 + T2 t2; + struct S4 s4; + int x = state1; // enum access on E1 + enum E2 e2; + struct S7 { // NON_COMPLIANT + int x; + } s7; + struct S8 { // COMPLIANT + int x; + } s8; + struct S8 s8_2; + + struct S11 { // COMPLIANT + int x; + } foo(struct S11 s); +} + +struct S9 { // COMPLIANT + int x; +} test_2() { + return (struct S9){0}; +} + +struct S10 { // NON_COMPLIANT + int x; +} * test_3() { + return 0; +} + +struct S12 { // COMPLIANT + int x; +} foo2(struct S12 s); + +#define STRUCT_MACRO \ + struct S13 { \ + int x; \ + }; + +void testMacroNameUsed() { + STRUCT_MACRO // COMPLIANT[FALSE_POSITIVE] - although the struct generated by + // the macro is never used in this expansion, it may be used in + // other expansions, so we don't want to report it as unused +} + +void testMacroNameNotUsed() { + STRUCT_MACRO // COMPLIANT - S13 is used in this expansion + struct S13 s13_2; +} + +#define PARTIAL \ + { int x; } + +struct s14 PARTIAL; // NON_COMPLIANT - affected by macro, but not fully + // generated, so fair to report as unused + +typedef struct { + int x; +} S15; // COMPLIANT - not a tag \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-5/UnusedMacroDeclaration.expected b/c/misra/test/rules/RULE-2-5/UnusedMacroDeclaration.expected new file mode 100644 index 0000000000..ead04877ec --- /dev/null +++ b/c/misra/test/rules/RULE-2-5/UnusedMacroDeclaration.expected @@ -0,0 +1,2 @@ +| test.c:4:1:4:16 | #define MACRO3 3 | Macro MACRO3 is unused. | +| test.h:3:1:3:21 | #define HEADER_MACRO3 | Macro HEADER_MACRO3 is unused. | diff --git a/c/misra/test/rules/RULE-2-5/UnusedMacroDeclaration.qlref b/c/misra/test/rules/RULE-2-5/UnusedMacroDeclaration.qlref new file mode 100644 index 0000000000..d4ad7325a8 --- /dev/null +++ b/c/misra/test/rules/RULE-2-5/UnusedMacroDeclaration.qlref @@ -0,0 +1 @@ +rules/RULE-2-5/UnusedMacroDeclaration.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-5/test.c b/c/misra/test/rules/RULE-2-5/test.c new file mode 100644 index 0000000000..15930f68d1 --- /dev/null +++ b/c/misra/test/rules/RULE-2-5/test.c @@ -0,0 +1,67 @@ +#include "test.h" +#define MACRO1 1 // COMPLIANT +#define MACRO2 2 // COMPLIANT +#define MACRO3 3 // NON_COMPLIANT + +#undef MACRO1 + +// This case is not captured by the query +#define MACRO1 1 // NON_COMPLIANT[FALSE_NEGATIVE] + +#undef HEADER_MACRO1 + +void test() { + MACRO2; + HEADER_MACRO2; +} + +#define CHECKED_MACRO_1 // COMPLIANT - used in branch +#define CHECKED_MACRO_2 // COMPLIANT - used in branch +#define CHECKED_MACRO_3 // COMPLIANT - used in branch + +#ifdef CHECKED_MACRO_1 +#endif + +#ifndef CHECKED_MACRO_2 +#endif + +#if defined(CHECKED_MACRO_3) +#endif + +// In the case above, the extractor will identify macro accesses with each use +// of the macro. In the case above, the extractor does not tie them together, +// but the standard considers this acceptable usage. Notably, this type of +// pattern occurs for header guards. + +#ifdef CHECKED_MACRO_BEFORE_1 +#endif + +#ifndef CHECKED_MACRO_BEFORE_2 +#endif + +#if defined(CHECKED_MACRO_BEFORE_3) +#endif + +// clang-format off + +#if defined (CHECKED_MACRO_BEFORE_4) +#endif + +#if defined( CHECKED_MACRO_BEFORE_5 ) +#endif + +#if defined ( CHECKED_MACRO_BEFORE_6 ) +#endif + +#if defined CHECKED_MACRO_BEFORE_7 +#endif + +// clang-format on + +#define CHECKED_MACRO_BEFORE_1 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_2 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_3 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_4 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_5 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_6 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_7 // COMPLIANT - used in branch \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-5/test.h b/c/misra/test/rules/RULE-2-5/test.h new file mode 100644 index 0000000000..3003ff9498 --- /dev/null +++ b/c/misra/test/rules/RULE-2-5/test.h @@ -0,0 +1,3 @@ +#define HEADER_MACRO1 // COMPLIANT +#define HEADER_MACRO2 // COMPLIANT +#define HEADER_MACRO3 // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-6/UnusedLabelDeclaration.expected b/c/misra/test/rules/RULE-2-6/UnusedLabelDeclaration.expected new file mode 100644 index 0000000000..1c6d5541ff --- /dev/null +++ b/c/misra/test/rules/RULE-2-6/UnusedLabelDeclaration.expected @@ -0,0 +1,3 @@ +| test.c:2:1:2:13 | label ...: | Label dead_label_1 is unused. | +| test.c:6:1:6:13 | label ...: | Label dead_label_2 is unused. | +| test.c:8:1:8:13 | label ...: | Label dead_label_3 is unused. | diff --git a/c/misra/test/rules/RULE-2-6/UnusedLabelDeclaration.qlref b/c/misra/test/rules/RULE-2-6/UnusedLabelDeclaration.qlref new file mode 100644 index 0000000000..2093fef0fe --- /dev/null +++ b/c/misra/test/rules/RULE-2-6/UnusedLabelDeclaration.qlref @@ -0,0 +1 @@ +rules/RULE-2-6/UnusedLabelDeclaration.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-6/test.c b/c/misra/test/rules/RULE-2-6/test.c new file mode 100644 index 0000000000..e358cdcb07 --- /dev/null +++ b/c/misra/test/rules/RULE-2-6/test.c @@ -0,0 +1,17 @@ +void test1(int p1) { +dead_label_1: // NON_COMPLIANT +live_label_1: // COMPLIANT + p1 + 1; +live_label_2: // COMPLIANT +dead_label_2: // NON_COMPLIANT + p1 + 2; +dead_label_3: // NON_COMPLIANT + p1 + 3; + + if (p1 > 1) { + goto live_label_1; + } + + // Taking the address of a label is sufficient to make it "live" + void *label_ptr = &&live_label_2; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-7/UnusedParameter.testref b/c/misra/test/rules/RULE-2-7/UnusedParameter.testref new file mode 100644 index 0000000000..852d3c4eb2 --- /dev/null +++ b/c/misra/test/rules/RULE-2-7/UnusedParameter.testref @@ -0,0 +1 @@ +c/common/test/rules/unusedparameter/UnusedParameter.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected new file mode 100644 index 0000000000..9a373a644c --- /dev/null +++ b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected @@ -0,0 +1,12 @@ +| test.c:6:5:6:6 | definition of g2 | Unused object 'g2'. | test.c:6:5:6:6 | definition of g2 | (ignored) | +| test.c:9:5:9:6 | definition of g3 | Unused object 'g3'. | test.c:9:5:9:6 | definition of g3 | (ignored) | +| test.c:20:7:20:8 | definition of l2 | Unused object 'l2'. | test.c:20:7:20:8 | definition of l2 | (ignored) | +| test.c:27:7:27:8 | definition of l5 | Unused object 'l5'. | test.c:27:7:27:8 | definition of l5 | (ignored) | +| test.c:37:10:37:11 | definition of g5 | Unused object 'g5'. | test.c:37:10:37:11 | definition of g5 | (ignored) | +| test.c:45:9:45:10 | definition of g6 | Unused object 'g6'. | test.c:45:9:45:10 | definition of g6 | (ignored) | +| test.c:51:5:51:6 | definition of g7 | Unused object 'g7'. | test.c:51:5:51:6 | definition of g7 | (ignored) | +| test.c:64:3:64:18 | ONLY_DEF_VAR(x) | Invocation of macro '$@' defines unused object 'l2'. | test.c:60:1:60:34 | #define ONLY_DEF_VAR(x) int x = 0; | ONLY_DEF_VAR | +| test.c:68:1:71:5 | #define ALSO_DEF_VAR(x) int x = 0; while (1) ; | Macro 'ALSO_DEF_VAR' defines unused object with an invocation-dependent name, for example, '$@'. | test.c:73:16:73:17 | definition of l1 | l1 | +| test.c:77:1:82:3 | #define DEF_UNUSED_INNER_VAR() { int _v = 0; while (1) ; } | Macro 'DEF_UNUSED_INNER_VAR' defines unused object '_v'. | test.c:77:1:82:3 | #define DEF_UNUSED_INNER_VAR() { int _v = 0; while (1) ; } | (ignored) | +| test.c:119:11:119:13 | definition of g10 | Unused object 'g10'. | test.c:119:11:119:13 | definition of g10 | (ignored) | +| test.c:124:13:124:14 | definition of l2 | Unused object 'l2'. | test.c:124:13:124:14 | definition of l2 | (ignored) | diff --git a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.qlref b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.qlref new file mode 100644 index 0000000000..096c4c64f1 --- /dev/null +++ b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.qlref @@ -0,0 +1 @@ +rules/RULE-2-8/UnusedObjectDefinition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected new file mode 100644 index 0000000000..fa191e5d68 --- /dev/null +++ b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected @@ -0,0 +1,4 @@ +| test.c:87:29:87:30 | definition of g8 | Unused object 'g8'. | test.c:87:29:87:30 | definition of g8 | (ignored) | +| test.c:92:3:92:30 | ONLY_DEF_ATTR_UNUSED_VAR(x) | Invocation of macro '$@' defines unused object 'l2'. | test.c:88:1:88:70 | #define ONLY_DEF_ATTR_UNUSED_VAR(x) __attribute__((unused)) int x = 0; | ONLY_DEF_ATTR_UNUSED_VAR | +| test.c:96:1:99:5 | #define ALSO_DEF_ATTR_UNUSED_VAR(x) __attribute__((unused)) int x = 0; while (1) ; | Macro 'ALSO_DEF_ATTR_UNUSED_VAR' defines unused object with an invocation-dependent name, for example, '$@'. | test.c:101:28:101:29 | definition of l1 | l1 | +| test.c:106:1:111:3 | #define DEF_ATTR_UNUSED_INNER_VAR() { __attribute__((unused)) int _v = 0; while (1) ; } | Macro 'DEF_ATTR_UNUSED_INNER_VAR' defines unused object '_v'. | test.c:106:1:111:3 | #define DEF_ATTR_UNUSED_INNER_VAR() { __attribute__((unused)) int _v = 0; while (1) ; } | (ignored) | diff --git a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.qlref b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.qlref new file mode 100644 index 0000000000..4aa7269881 --- /dev/null +++ b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.qlref @@ -0,0 +1 @@ +rules/RULE-2-8/UnusedObjectDefinitionStrict.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-2-8/test.c b/c/misra/test/rules/RULE-2-8/test.c new file mode 100644 index 0000000000..e35bf15567 --- /dev/null +++ b/c/misra/test/rules/RULE-2-8/test.c @@ -0,0 +1,133 @@ +// Not a definition, only a declaration: +extern int g1; // COMPLIANT + +// Both declared + defined: +extern int g2; // COMPLIANT +int g2 = 1; // NON_COMPLIANT + +// Definition is only declaration: +int g3 = 1; // NON_COMPLIANT + +// Definition, but value is required for program to compile: +int g4 = 1; // COMPLIANT +void f1() { g4; } + +// Local variables: +void f2() { + int l1; // COMPLIANT + l1; + + int l2; // NON-COMPLIANT + + // Value is required for the program to compile: + int l3; // COMPLIANT + sizeof(l3); + + int l4, // COMPLIANT + l5; // NON-COMPLIANT + l4; +} + +// Struct fields are not objects: +struct s { + int x; // COMPLIANT +}; + +// Declaration of type struct is an object: +struct s g5; // NON-COMPLIANT + +// Struct fields are not objects: +union u { + int x; // COMPLIANT +}; + +// Declaration of type union is an object: +union u g6; // NON-COMPLIANT + +// Typedefs are not objects: +typedef int td1; // COMPLIANT + +// Declaration of typedef type object: +td1 g7; // NON-COMPLIANT + +// Function parameters are not objects: +void f3(int p) {} // COMPLIANT + +// Function type parameters are not objects: +typedef int td2(int x); // COMPLIANT + +// Macros that define unused vars tests: +#define ONLY_DEF_VAR(x) int x = 0; +void f4() { + ONLY_DEF_VAR(l1); // COMPLIANT + l1; + ONLY_DEF_VAR(l2); // NON-COMPLIANT +} + +// NON-COMPLIANT +#define ALSO_DEF_VAR(x) \ + int x = 0; \ + while (1) \ + ; +void f5() { + ALSO_DEF_VAR(l1); // COMPLIANT + ALSO_DEF_VAR(l2); // COMPLIANT +} + +#define DEF_UNUSED_INNER_VAR() \ + { \ + int _v = 0; \ + while (1) \ + ; \ + } // NON-COMPLIANT +void f6() { + DEF_UNUSED_INNER_VAR(); // COMPLIANT +} + +__attribute__((unused)) int g8 = 1; // NON-COMPLIANT +#define ONLY_DEF_ATTR_UNUSED_VAR(x) __attribute__((unused)) int x = 0; +void f7() { + ONLY_DEF_ATTR_UNUSED_VAR(l1); // COMPLIANT + l1; + ONLY_DEF_ATTR_UNUSED_VAR(l2); // NON-COMPLIANT +} + +// NON-COMPLIANT +#define ALSO_DEF_ATTR_UNUSED_VAR(x) \ + __attribute__((unused)) int x = 0; \ + while (1) \ + ; +void f8() { + ALSO_DEF_ATTR_UNUSED_VAR(l1); // COMPLIANT + ALSO_DEF_ATTR_UNUSED_VAR(l2); // COMPLIANT +} + +// NON-COMPLIANT +#define DEF_ATTR_UNUSED_INNER_VAR() \ + { \ + __attribute__((unused)) int _v = 0; \ + while (1) \ + ; \ + } + +void f9() { + DEF_ATTR_UNUSED_INNER_VAR(); // COMPLIANT +} + +// Const variable tests: +const int g9 = 1; // COMPLIANT +const int g10 = 1; // NON-COMPLIANT + +void f10() { + g9; + const int l1 = 1; // COMPLIANT + const int l2 = 1; // NON-COMPLIANT + l1; +} + +// Side effects should not disable this rule: +void f11() { + int l1 = 1; // COMPLIANT + int l2 = l1++; // COMPLIANT + l2; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.expected b/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.expected deleted file mode 100644 index 406010428c..0000000000 --- a/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.expected +++ /dev/null @@ -1 +0,0 @@ -| test.c:25:1:25:29 | #define MACROTHIRTEEN(X) #X ## X | Macro definition uses an # operator followed by a ## operator. | diff --git a/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.qlref b/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.qlref deleted file mode 100644 index 35ef457cac..0000000000 --- a/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.testref b/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.testref new file mode 100644 index 0000000000..be7ebf2815 --- /dev/null +++ b/c/misra/test/rules/RULE-20-11/MoreThanOneHashOperatorInMacroDefinition.testref @@ -0,0 +1 @@ +c/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-11/test.c b/c/misra/test/rules/RULE-20-11/test.c deleted file mode 100644 index ad2c205970..0000000000 --- a/c/misra/test/rules/RULE-20-11/test.c +++ /dev/null @@ -1,27 +0,0 @@ -#define MACROONE 1 // COMPLIANT - -#define MACROTWO '#\'-#' + '#' // COMPLIANT - -#define MACROTHREE "##" // COMPLIANT - -#define MACROFOUR "##" + "#" // COMPLIANT - -#define MACROFIVE(X) #X // COMPLIANT - -#define MACROSIX(X, Y) X##Y // COMPLIANT - -#define MACROSEVEN "##'" #"#" // COMPLIANT - -#define MACROEIGHT '##' #"#" // COMPLIANT - -#define MACRONINE "##\"\"" + "#" // COMPLIANT - -#define MACROTEN "##\"\"'" + "#" // COMPLIANT - -#define MACROELEVEN(X) X #X #X // COMPLIANT - -#define MACROTWELVE(X) X##X##X // COMPLIANT - -#define MACROTHIRTEEN(X) #X##X // NON_COMPLIANT - -#define MACROFOURTEEN '#\'-#' + 1 #1 #1 + '#' // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.expected b/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.expected deleted file mode 100644 index be347218b3..0000000000 --- a/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.c:4:1:4:41 | #define BAD_MACRO_WITH_ARG(x) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG contains use of parameter x used in multiple contexts. | -| test.c:5:1:5:48 | #define BAD_MACRO_WITH_ARG_TWO(x,y) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG_TWO contains use of parameter x used in multiple contexts. | diff --git a/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.qlref b/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.qlref deleted file mode 100644 index a2edc3acc4..0000000000 --- a/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-20-12/MacroParameterUsedAsHashOperand.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.testref b/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.testref new file mode 100644 index 0000000000..d1cc5971c7 --- /dev/null +++ b/c/misra/test/rules/RULE-20-12/MacroParameterUsedAsHashOperand.testref @@ -0,0 +1 @@ +c/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-12/test.c b/c/misra/test/rules/RULE-20-12/test.c deleted file mode 100644 index 768238f36d..0000000000 --- a/c/misra/test/rules/RULE-20-12/test.c +++ /dev/null @@ -1,25 +0,0 @@ - -#define GOOD_MACRO_WITH_ARG(X) ((X)*X##_scale) // COMPLIANT -#define MACRO 1 -#define BAD_MACRO_WITH_ARG(x) (x) + wow##x // NON_COMPLIANT -#define BAD_MACRO_WITH_ARG_TWO(x, y) (x) + wow##x // NON_COMPLIANT -#define MACROONE(x) #x // COMPLIANT -#define MACROTWO(x) x *x // COMPLIANT -#define MACROTHREE(x) "##\"\"'" + (x) // COMPLIANT -#define FOO(x) #x MACROONE(x) // COMPLIANT - no further arg expansion - -void f() { - - int x; - int x_scale; - int y; - int wowMACRO = 0; - - y = GOOD_MACRO_WITH_ARG(x); - wowMACRO = BAD_MACRO_WITH_ARG(MACRO); - wowMACRO = BAD_MACRO_WITH_ARG_TWO(MACRO, 1); - char s[] = MACROONE(MACRO); - y = MACROTWO(MACRO); - MACROTHREE(MACRO); - FOO(x); -} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected b/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected index 299626d6fc..ef9700d8d3 100644 --- a/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected +++ b/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected @@ -1,4 +1,3 @@ | test.c:1:1:1:17 | #define _NOT_OKAY | Reserved identifier '_NOT_OKAY' has been undefined or redefined. | | test.c:2:1:2:16 | #undef _NOT_OKAY | Reserved identifier '_NOT_OKAY' has been undefined or redefined. | -| test.c:4:1:4:15 | #define defined | Reserved identifier 'defined' has been undefined or redefined. | | test.c:5:1:5:13 | #define errno | Reserved identifier 'errno' has been undefined or redefined. | diff --git a/c/misra/test/rules/RULE-21-1/test.c b/c/misra/test/rules/RULE-21-1/test.c index 380679d84a..dc709ca220 100644 --- a/c/misra/test/rules/RULE-21-1/test.c +++ b/c/misra/test/rules/RULE-21-1/test.c @@ -1,7 +1,7 @@ #define _NOT_OKAY // NON_COMPLIANT #undef _NOT_OKAY // NON_COMPLIANT -#define defined // NON_COMPLIANT -#define errno // NON_COMPLIANT +// #define defined // NON_COMPILABLE +#define errno // NON_COMPLIANT #define NDEBUG 1 // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.expected b/c/misra/test/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.expected index 0ffd5eed7a..5f790df8e7 100644 --- a/c/misra/test/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.expected +++ b/c/misra/test/rules/RULE-21-10/StandardLibraryTimeAndDateFunctionsUsed.expected @@ -1,5 +1,5 @@ -| test.c:6:18:6:21 | call to time | Call to banned function $@. | time.h:53:8:53:11 | time | time | -| test.c:9:19:9:23 | call to ctime | Call to banned function $@. | time.h:60:7:60:11 | ctime | ctime | -| test.c:17:3:17:6 | call to time | Call to banned function $@. | time.h:53:8:53:11 | time | time | -| test.c:18:8:18:16 | call to localtime | Call to banned function $@. | time.h:58:12:58:20 | localtime | localtime | -| test.c:19:3:19:10 | call to wcsftime | Call to banned function $@. | wchar.h:139:8:139:15 | wcsftime | wcsftime | +| test.c:6:18:6:21 | call to time | Call to banned function time. | +| test.c:9:19:9:23 | call to ctime | Call to banned function ctime. | +| test.c:17:3:17:6 | call to time | Call to banned function time. | +| test.c:18:8:18:16 | call to localtime | Call to banned function localtime. | +| test.c:19:3:19:10 | call to wcsftime | Call to banned function wcsftime. | diff --git a/c/misra/test/rules/RULE-21-10/test.c b/c/misra/test/rules/RULE-21-10/test.c index fb028300a4..69b5e9cfb0 100644 --- a/c/misra/test/rules/RULE-21-10/test.c +++ b/c/misra/test/rules/RULE-21-10/test.c @@ -1,5 +1,5 @@ -#include "time.h" -#include "wchar.h" +#include +#include void f1() { time_t current_time; char *c_time_string; diff --git a/c/misra/test/rules/RULE-21-10/time.h b/c/misra/test/rules/RULE-21-10/time.h deleted file mode 100644 index 5494df1836..0000000000 --- a/c/misra/test/rules/RULE-21-10/time.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef _TIME_H -#define _TIME_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - - -#define __NEED_size_t -#define __NEED_time_t -#define __NEED_clock_t -#define __NEED_struct_timespec - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -#define __NEED_clockid_t -#define __NEED_timer_t -#define __NEED_pid_t -#define __NEED_locale_t -#endif - -#include - -#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) -#define __tm_gmtoff tm_gmtoff -#define __tm_zone tm_zone -#endif - -struct tm { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; - long __tm_gmtoff; - const char *__tm_zone; -}; - -clock_t clock (void); -time_t time (time_t *); -double difftime (time_t, time_t); -time_t mktime (struct tm *); -size_t strftime (char *__restrict, size_t, const char *__restrict, const struct tm *__restrict); -struct tm *gmtime (const time_t *); -struct tm *localtime (const time_t *); -char *asctime (const struct tm *); -char *ctime (const time_t *); -int timespec_get(struct timespec *, int); - -#define CLOCKS_PER_SEC 1000000L - -#define TIME_UTC 1 - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) - -size_t strftime_l (char * __restrict, size_t, const char * __restrict, const struct tm * __restrict, locale_t); - -struct tm *gmtime_r (const time_t *__restrict, struct tm *__restrict); -struct tm *localtime_r (const time_t *__restrict, struct tm *__restrict); -char *asctime_r (const struct tm *__restrict, char *__restrict); -char *ctime_r (const time_t *, char *); - -void tzset (void); - -struct itimerspec { - struct timespec it_interval; - struct timespec it_value; -}; - -#define CLOCK_REALTIME 0 -#define CLOCK_MONOTONIC 1 -#define CLOCK_PROCESS_CPUTIME_ID 2 -#define CLOCK_THREAD_CPUTIME_ID 3 -#define CLOCK_MONOTONIC_RAW 4 -#define CLOCK_REALTIME_COARSE 5 -#define CLOCK_MONOTONIC_COARSE 6 -#define CLOCK_BOOTTIME 7 -#define CLOCK_REALTIME_ALARM 8 -#define CLOCK_BOOTTIME_ALARM 9 -#define CLOCK_SGI_CYCLE 10 -#define CLOCK_TAI 11 - -#define TIMER_ABSTIME 1 - -int nanosleep (const struct timespec *, struct timespec *); -int clock_getres (clockid_t, struct timespec *); -int clock_gettime (clockid_t, struct timespec *); -int clock_settime (clockid_t, const struct timespec *); -int clock_nanosleep (clockid_t, int, const struct timespec *, struct timespec *); -int clock_getcpuclockid (pid_t, clockid_t *); - -struct sigevent; -int timer_create (clockid_t, struct sigevent *__restrict, timer_t *__restrict); -int timer_delete (timer_t); -int timer_settime (timer_t, int, const struct itimerspec *__restrict, struct itimerspec *__restrict); -int timer_gettime (timer_t, struct itimerspec *); -int timer_getoverrun (timer_t); - -extern char *tzname[2]; - -#endif - - -#if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE) -char *strptime (const char *__restrict, const char *__restrict, struct tm *__restrict); -extern int daylight; -extern long timezone; -extern int getdate_err; -struct tm *getdate (const char *); -#endif - - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -int stime(const time_t *); -time_t timegm(struct tm *); -#endif - -#if _REDIR_TIME64 -__REDIR(time, __time64); -__REDIR(difftime, __difftime64); -__REDIR(mktime, __mktime64); -__REDIR(gmtime, __gmtime64); -__REDIR(localtime, __localtime64); -__REDIR(ctime, __ctime64); -__REDIR(timespec_get, __timespec_get_time64); -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -__REDIR(gmtime_r, __gmtime64_r); -__REDIR(localtime_r, __localtime64_r); -__REDIR(ctime_r, __ctime64_r); -__REDIR(nanosleep, __nanosleep_time64); -__REDIR(clock_getres, __clock_getres_time64); -__REDIR(clock_gettime, __clock_gettime64); -__REDIR(clock_settime, __clock_settime64); -__REDIR(clock_nanosleep, __clock_nanosleep_time64); -__REDIR(timer_settime, __timer_settime64); -__REDIR(timer_gettime, __timer_gettime64); -#endif -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -__REDIR(stime, __stime64); -__REDIR(timegm, __timegm_time64); -#endif -#endif - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/c/misra/test/rules/RULE-21-10/wchar.h b/c/misra/test/rules/RULE-21-10/wchar.h deleted file mode 100644 index 88eb55b18c..0000000000 --- a/c/misra/test/rules/RULE-21-10/wchar.h +++ /dev/null @@ -1,205 +0,0 @@ -#ifndef _WCHAR_H -#define _WCHAR_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#define __NEED_FILE -#define __NEED___isoc_va_list -#define __NEED_size_t -#define __NEED_wchar_t -#define __NEED_wint_t -#define __NEED_mbstate_t - -#if __STDC_VERSION__ < 201112L -#define __NEED_struct__IO_FILE -#endif - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define __NEED_locale_t -#define __NEED_va_list -#endif - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define __NEED_wctype_t -#endif - -#include - -#if L'\0'-1 > 0 -#define WCHAR_MAX (0xffffffffu+L'\0') -#define WCHAR_MIN (0+L'\0') -#else -#define WCHAR_MAX (0x7fffffff+L'\0') -#define WCHAR_MIN (-1-0x7fffffff+L'\0') -#endif - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - -#undef WEOF -#define WEOF 0xffffffffU - -wchar_t *wcscpy (wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcsncpy (wchar_t *__restrict, const wchar_t *__restrict, size_t); - -wchar_t *wcscat (wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcsncat (wchar_t *__restrict, const wchar_t *__restrict, size_t); - -int wcscmp (const wchar_t *, const wchar_t *); -int wcsncmp (const wchar_t *, const wchar_t *, size_t); - -int wcscoll(const wchar_t *, const wchar_t *); -size_t wcsxfrm (wchar_t *__restrict, const wchar_t *__restrict, size_t); - -wchar_t *wcschr (const wchar_t *, wchar_t); -wchar_t *wcsrchr (const wchar_t *, wchar_t); - -size_t wcscspn (const wchar_t *, const wchar_t *); -size_t wcsspn (const wchar_t *, const wchar_t *); -wchar_t *wcspbrk (const wchar_t *, const wchar_t *); - -wchar_t *wcstok (wchar_t *__restrict, const wchar_t *__restrict, wchar_t **__restrict); - -size_t wcslen (const wchar_t *); - -wchar_t *wcsstr (const wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcswcs (const wchar_t *, const wchar_t *); - -wchar_t *wmemchr (const wchar_t *, wchar_t, size_t); -int wmemcmp (const wchar_t *, const wchar_t *, size_t); -wchar_t *wmemcpy (wchar_t *__restrict, const wchar_t *__restrict, size_t); -wchar_t *wmemmove (wchar_t *, const wchar_t *, size_t); -wchar_t *wmemset (wchar_t *, wchar_t, size_t); - -wint_t btowc (int); -int wctob (wint_t); - -int mbsinit (const mbstate_t *); -size_t mbrtowc (wchar_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict); -size_t wcrtomb (char *__restrict, wchar_t, mbstate_t *__restrict); - -size_t mbrlen (const char *__restrict, size_t, mbstate_t *__restrict); - -size_t mbsrtowcs (wchar_t *__restrict, const char **__restrict, size_t, mbstate_t *__restrict); -size_t wcsrtombs (char *__restrict, const wchar_t **__restrict, size_t, mbstate_t *__restrict); - -float wcstof (const wchar_t *__restrict, wchar_t **__restrict); -double wcstod (const wchar_t *__restrict, wchar_t **__restrict); -long double wcstold (const wchar_t *__restrict, wchar_t **__restrict); - -long wcstol (const wchar_t *__restrict, wchar_t **__restrict, int); -unsigned long wcstoul (const wchar_t *__restrict, wchar_t **__restrict, int); - -long long wcstoll (const wchar_t *__restrict, wchar_t **__restrict, int); -unsigned long long wcstoull (const wchar_t *__restrict, wchar_t **__restrict, int); - - - -int fwide (FILE *, int); - - -int wprintf (const wchar_t *__restrict, ...); -int fwprintf (FILE *__restrict, const wchar_t *__restrict, ...); -int swprintf (wchar_t *__restrict, size_t, const wchar_t *__restrict, ...); - -int vwprintf (const wchar_t *__restrict, __isoc_va_list); -int vfwprintf (FILE *__restrict, const wchar_t *__restrict, __isoc_va_list); -int vswprintf (wchar_t *__restrict, size_t, const wchar_t *__restrict, __isoc_va_list); - -int wscanf (const wchar_t *__restrict, ...); -int fwscanf (FILE *__restrict, const wchar_t *__restrict, ...); -int swscanf (const wchar_t *__restrict, const wchar_t *__restrict, ...); - -int vwscanf (const wchar_t *__restrict, __isoc_va_list); -int vfwscanf (FILE *__restrict, const wchar_t *__restrict, __isoc_va_list); -int vswscanf (const wchar_t *__restrict, const wchar_t *__restrict, __isoc_va_list); - -wint_t fgetwc (FILE *); -wint_t getwc (FILE *); -wint_t getwchar (void); - -wint_t fputwc (wchar_t, FILE *); -wint_t putwc (wchar_t, FILE *); -wint_t putwchar (wchar_t); - -wchar_t *fgetws (wchar_t *__restrict, int, FILE *__restrict); -int fputws (const wchar_t *__restrict, FILE *__restrict); - -wint_t ungetwc (wint_t, FILE *); - -struct tm; -size_t wcsftime (wchar_t *__restrict, size_t, const wchar_t *__restrict, const struct tm *__restrict); - -#undef iswdigit - -#if defined(_GNU_SOURCE) -wint_t fgetwc_unlocked (FILE *); -wint_t getwc_unlocked (FILE *); -wint_t getwchar_unlocked (void); -wint_t fputwc_unlocked (wchar_t, FILE *); -wint_t putwc_unlocked (wchar_t, FILE *); -wint_t putwchar_unlocked (wchar_t); -wchar_t *fgetws_unlocked (wchar_t *__restrict, int, FILE *__restrict); -int fputws_unlocked (const wchar_t *__restrict, FILE *__restrict); -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -size_t wcsftime_l (wchar_t *__restrict, size_t, const wchar_t *__restrict, const struct tm *__restrict, locale_t); -#endif - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -FILE *open_wmemstream(wchar_t **, size_t *); -size_t mbsnrtowcs(wchar_t *__restrict, const char **__restrict, size_t, size_t, mbstate_t *__restrict); -size_t wcsnrtombs(char *__restrict, const wchar_t **__restrict, size_t, size_t, mbstate_t *__restrict); -wchar_t *wcsdup(const wchar_t *); -size_t wcsnlen (const wchar_t *, size_t); -wchar_t *wcpcpy (wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcpncpy (wchar_t *__restrict, const wchar_t *__restrict, size_t); -int wcscasecmp(const wchar_t *, const wchar_t *); -int wcscasecmp_l(const wchar_t *, const wchar_t *, locale_t); -int wcsncasecmp(const wchar_t *, const wchar_t *, size_t); -int wcsncasecmp_l(const wchar_t *, const wchar_t *, size_t, locale_t); -int wcscoll_l(const wchar_t *, const wchar_t *, locale_t); -size_t wcsxfrm_l(wchar_t *__restrict, const wchar_t *__restrict, size_t, locale_t); -#endif - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -int wcwidth (wchar_t); -int wcswidth (const wchar_t *, size_t); -int iswalnum(wint_t); -int iswalpha(wint_t); -int iswblank(wint_t); -int iswcntrl(wint_t); -int iswdigit(wint_t); -int iswgraph(wint_t); -int iswlower(wint_t); -int iswprint(wint_t); -int iswpunct(wint_t); -int iswspace(wint_t); -int iswupper(wint_t); -int iswxdigit(wint_t); -int iswctype(wint_t, wctype_t); -wint_t towlower(wint_t); -wint_t towupper(wint_t); -wctype_t wctype(const char *); - -#ifndef __cplusplus -#undef iswdigit -#define iswdigit(a) (0 ? iswdigit(a) : ((unsigned)(a)-'0') < 10) -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected b/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected index db2a41bb06..2800d13160 100644 --- a/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected +++ b/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected @@ -1,74 +1,5 @@ -| test.c:5:3:5:9 | __DBLCX(x) | Call to banned macro $@. | tgmath.h:24:1:24:70 | #define __DBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(double complex)) | __DBLCX | -| test.c:5:3:5:9 | __FLT(x) | Call to banned macro $@. | tgmath.h:20:1:20:61 | #define __FLT(x) (__IS_REAL(x) && sizeof(x) == sizeof(float)) | __FLT | -| test.c:5:3:5:9 | __FLTCX(x) | Call to banned macro $@. | tgmath.h:23:1:23:69 | #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) | __FLTCX | -| test.c:5:3:5:9 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:5:3:5:9 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:5:3:5:9 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:5:3:5:9 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:5:3:5:9 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:5:3:5:9 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:5:3:5:9 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:5:3:5:9 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:5:3:5:9 | __IS_REAL(x) | Call to banned macro $@. | tgmath.h:18:1:18:65 | #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) | __IS_REAL | -| test.c:5:3:5:9 | __IS_REAL(x) | Call to banned macro $@. | tgmath.h:18:1:18:65 | #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) | __IS_REAL | -| test.c:5:3:5:9 | __LDBL(x) | Call to banned macro $@. | tgmath.h:21:1:21:109 | #define __LDBL(x) (__IS_REAL(x) && sizeof(x) == sizeof(long double) && sizeof(long double) != sizeof(double)) | __LDBL | -| test.c:5:3:5:9 | __LDBLCX(x) | Call to banned macro $@. | tgmath.h:25:1:25:117 | #define __LDBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(long double complex) && sizeof(long double) != sizeof(double)) | __LDBLCX | -| test.c:5:3:5:9 | __RETCAST(x) | Call to banned macro $@. | tgmath.h:71:1:71:20 | #define __RETCAST(x) | __RETCAST | -| test.c:5:3:5:9 | __tg_real_complex(fun,x) | Call to banned macro $@. | tgmath.h:107:1:113:10 | #define __tg_real_complex(fun,x) (__RETCAST(x)( __FLTCX(x) ? c ## fun ## f (x) : __DBLCX(x) ? c ## fun (x) : __LDBLCX(x) ? c ## fun ## l (x) : __FLT(x) ? fun ## f (x) : __LDBL(x) ? fun ## l (x) : fun(x) )) | __tg_real_complex | -| test.c:5:3:5:9 | sqrt(x) | Call to banned macro $@. | tgmath.h:264:1:264:52 | #define sqrt(x) __tg_real_complex(sqrt, (x)) | sqrt | -| test.c:7:3:7:8 | __DBLCX(x) | Call to banned macro $@. | tgmath.h:24:1:24:70 | #define __DBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(double complex)) | __DBLCX | -| test.c:7:3:7:8 | __FLT(x) | Call to banned macro $@. | tgmath.h:20:1:20:61 | #define __FLT(x) (__IS_REAL(x) && sizeof(x) == sizeof(float)) | __FLT | -| test.c:7:3:7:8 | __FLTCX(x) | Call to banned macro $@. | tgmath.h:23:1:23:69 | #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) | __FLTCX | -| test.c:7:3:7:8 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:7:3:7:8 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:7:3:7:8 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:7:3:7:8 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:7:3:7:8 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:7:3:7:8 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:7:3:7:8 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:7:3:7:8 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:7:3:7:8 | __IS_REAL(x) | Call to banned macro $@. | tgmath.h:18:1:18:65 | #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) | __IS_REAL | -| test.c:7:3:7:8 | __IS_REAL(x) | Call to banned macro $@. | tgmath.h:18:1:18:65 | #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) | __IS_REAL | -| test.c:7:3:7:8 | __LDBL(x) | Call to banned macro $@. | tgmath.h:21:1:21:109 | #define __LDBL(x) (__IS_REAL(x) && sizeof(x) == sizeof(long double) && sizeof(long double) != sizeof(double)) | __LDBL | -| test.c:7:3:7:8 | __LDBLCX(x) | Call to banned macro $@. | tgmath.h:25:1:25:117 | #define __LDBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(long double complex) && sizeof(long double) != sizeof(double)) | __LDBLCX | -| test.c:7:3:7:8 | __RETCAST(x) | Call to banned macro $@. | tgmath.h:71:1:71:20 | #define __RETCAST(x) | __RETCAST | -| test.c:7:3:7:8 | __tg_real_complex(fun,x) | Call to banned macro $@. | tgmath.h:107:1:113:10 | #define __tg_real_complex(fun,x) (__RETCAST(x)( __FLTCX(x) ? c ## fun ## f (x) : __DBLCX(x) ? c ## fun (x) : __LDBLCX(x) ? c ## fun ## l (x) : __FLT(x) ? fun ## f (x) : __LDBL(x) ? fun ## l (x) : fun(x) )) | __tg_real_complex | -| test.c:7:3:7:8 | sin(x) | Call to banned macro $@. | tgmath.h:262:1:262:51 | #define sin(x) __tg_real_complex(sin, (x)) | sin | -| test.c:10:21:10:28 | __DBLCX(x) | Call to banned macro $@. | tgmath.h:24:1:24:70 | #define __DBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(double complex)) | __DBLCX | -| test.c:10:21:10:28 | __FLT(x) | Call to banned macro $@. | tgmath.h:20:1:20:61 | #define __FLT(x) (__IS_REAL(x) && sizeof(x) == sizeof(float)) | __FLT | -| test.c:10:21:10:28 | __FLTCX(x) | Call to banned macro $@. | tgmath.h:23:1:23:69 | #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) | __FLTCX | -| test.c:10:21:10:28 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:10:21:10:28 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:10:21:10:28 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:10:21:10:28 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:10:21:10:28 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:10:21:10:28 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:10:21:10:28 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:10:21:10:28 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:10:21:10:28 | __IS_REAL(x) | Call to banned macro $@. | tgmath.h:18:1:18:65 | #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) | __IS_REAL | -| test.c:10:21:10:28 | __IS_REAL(x) | Call to banned macro $@. | tgmath.h:18:1:18:65 | #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) | __IS_REAL | -| test.c:10:21:10:28 | __LDBL(x) | Call to banned macro $@. | tgmath.h:21:1:21:109 | #define __LDBL(x) (__IS_REAL(x) && sizeof(x) == sizeof(long double) && sizeof(long double) != sizeof(double)) | __LDBL | -| test.c:10:21:10:28 | __LDBLCX(x) | Call to banned macro $@. | tgmath.h:25:1:25:117 | #define __LDBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(long double complex) && sizeof(long double) != sizeof(double)) | __LDBLCX | -| test.c:10:21:10:28 | __RETCAST(x) | Call to banned macro $@. | tgmath.h:71:1:71:20 | #define __RETCAST(x) | __RETCAST | -| test.c:10:21:10:28 | __tg_real_complex(fun,x) | Call to banned macro $@. | tgmath.h:107:1:113:10 | #define __tg_real_complex(fun,x) (__RETCAST(x)( __FLTCX(x) ? c ## fun ## f (x) : __DBLCX(x) ? c ## fun (x) : __LDBLCX(x) ? c ## fun ## l (x) : __FLT(x) ? fun ## f (x) : __LDBL(x) ? fun ## l (x) : fun(x) )) | __tg_real_complex | -| test.c:10:21:10:28 | sqrt(x) | Call to banned macro $@. | tgmath.h:264:1:264:52 | #define sqrt(x) __tg_real_complex(sqrt, (x)) | sqrt | -| test.c:11:3:11:10 | __FLTCX(x) | Call to banned macro $@. | tgmath.h:23:1:23:69 | #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) | __FLTCX | -| test.c:11:3:11:10 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:11:3:11:10 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:11:3:11:10 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:11:3:11:10 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:11:3:11:10 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:11:3:11:10 | __LDBLCX(x) | Call to banned macro $@. | tgmath.h:25:1:25:117 | #define __LDBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(long double complex) && sizeof(long double) != sizeof(double)) | __LDBLCX | -| test.c:11:3:11:10 | __RETCAST_REAL(x) | Call to banned macro $@. | tgmath.h:74:1:74:25 | #define __RETCAST_REAL(x) | __RETCAST_REAL | -| test.c:11:3:11:10 | __tg_complex_retreal(fun,x) | Call to banned macro $@. | tgmath.h:102:1:105:10 | #define __tg_complex_retreal(fun,x) (__RETCAST_REAL(x)( __FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : __LDBLCX((x)+I) ? fun ## l (x) : fun(x) )) | __tg_complex_retreal | -| test.c:11:3:11:10 | creal(x) | Call to banned macro $@. | tgmath.h:225:1:225:56 | #define creal(x) __tg_complex_retreal(creal, (x)) | creal | -| test.c:12:3:12:10 | __FLTCX(x) | Call to banned macro $@. | tgmath.h:23:1:23:69 | #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) | __FLTCX | -| test.c:12:3:12:10 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:12:3:12:10 | __IS_CX(x) | Call to banned macro $@. | tgmath.h:17:1:17:61 | #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) | __IS_CX | -| test.c:12:3:12:10 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:12:3:12:10 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:12:3:12:10 | __IS_FP(x) | Call to banned macro $@. | tgmath.h:16:1:16:57 | #define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) | __IS_FP | -| test.c:12:3:12:10 | __LDBLCX(x) | Call to banned macro $@. | tgmath.h:25:1:25:117 | #define __LDBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(long double complex) && sizeof(long double) != sizeof(double)) | __LDBLCX | -| test.c:12:3:12:10 | __RETCAST_REAL(x) | Call to banned macro $@. | tgmath.h:74:1:74:25 | #define __RETCAST_REAL(x) | __RETCAST_REAL | -| test.c:12:3:12:10 | __tg_complex_retreal(fun,x) | Call to banned macro $@. | tgmath.h:102:1:105:10 | #define __tg_complex_retreal(fun,x) (__RETCAST_REAL(x)( __FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : __LDBLCX((x)+I) ? fun ## l (x) : fun(x) )) | __tg_complex_retreal | -| test.c:12:3:12:10 | cimag(x) | Call to banned macro $@. | tgmath.h:219:1:219:56 | #define cimag(x) __tg_complex_retreal(cimag, (x)) | cimag | +| test.c:5:3:5:9 | sqrt(x) | Call to banned macro sqrt. | +| test.c:7:3:7:8 | sin(x) | Call to banned macro sin. | +| test.c:10:21:10:28 | sqrt(x) | Call to banned macro sqrt. | +| test.c:11:3:11:10 | creal(x) | Call to banned macro creal. | +| test.c:12:3:12:10 | cimag(x) | Call to banned macro cimag. | diff --git a/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected.clang b/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected.clang new file mode 100644 index 0000000000..42a740200a --- /dev/null +++ b/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected.clang @@ -0,0 +1,5 @@ +| test.c:5:3:5:9 | sqrt(__x) | Call to banned macro sqrt. | +| test.c:7:3:7:8 | sin(__x) | Call to banned macro sin. | +| test.c:10:21:10:28 | sqrt(__x) | Call to banned macro sqrt. | +| test.c:11:3:11:10 | creal(__x) | Call to banned macro creal. | +| test.c:12:3:12:10 | cimag(__x) | Call to banned macro cimag. | diff --git a/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected.gcc b/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected.gcc new file mode 100644 index 0000000000..db1d5718bd --- /dev/null +++ b/c/misra/test/rules/RULE-21-11/StandardHeaderFileTgmathhUsed.expected.gcc @@ -0,0 +1,5 @@ +| test.c:5:3:5:9 | sqrt(Val) | Call to banned macro sqrt. | +| test.c:7:3:7:8 | sin(Val) | Call to banned macro sin. | +| test.c:10:21:10:28 | sqrt(Val) | Call to banned macro sqrt. | +| test.c:11:3:11:10 | creal(Val) | Call to banned macro creal. | +| test.c:12:3:12:10 | cimag(Val) | Call to banned macro cimag. | diff --git a/c/misra/test/rules/RULE-21-11/test.c b/c/misra/test/rules/RULE-21-11/test.c index 95b432d231..7e81bf3c57 100644 --- a/c/misra/test/rules/RULE-21-11/test.c +++ b/c/misra/test/rules/RULE-21-11/test.c @@ -1,4 +1,4 @@ -#include "tgmath.h" +#include void f2(); void f1() { int i = 2; diff --git a/c/misra/test/rules/RULE-21-11/tgmath.h b/c/misra/test/rules/RULE-21-11/tgmath.h deleted file mode 100644 index e41ccac9ec..0000000000 --- a/c/misra/test/rules/RULE-21-11/tgmath.h +++ /dev/null @@ -1,270 +0,0 @@ -#ifndef _TGMATH_H -#define _TGMATH_H - -/* -the return types are only correct with gcc (__GNUC__) -otherwise they are long double or long double complex - -the long double version of a function is never chosen when -sizeof(double) == sizeof(long double) -(but the return type is set correctly with gcc) -*/ - -#include -#include - -#define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f)) -#define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I)) -#define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I)) - -#define __FLT(x) (__IS_REAL(x) && sizeof(x) == sizeof(float)) -#define __LDBL(x) (__IS_REAL(x) && sizeof(x) == sizeof(long double) && sizeof(long double) != sizeof(double)) - -#define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) -#define __DBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(double complex)) -#define __LDBLCX(x) (__IS_CX(x) && sizeof(x) == sizeof(long double complex) && sizeof(long double) != sizeof(double)) - -/* return type */ - -#ifdef __GNUC__ -/* -the result must be casted to the right type -(otherwise the result type is determined by the conversion -rules applied to all the function return types so it is long -double or long double complex except for integral functions) - -this cannot be done in c99, so the typeof gcc extension is -used and that the type of ?: depends on wether an operand is -a null pointer constant or not -(in c11 _Generic can be used) - -the c arguments below must be integer constant expressions -so they can be in null pointer constants -(__IS_FP above was carefully chosen this way) -*/ -/* if c then t else void */ -#define __type1(c,t) __typeof__(*(0?(t*)0:(void*)!(c))) -/* if c then t1 else t2 */ -#define __type2(c,t1,t2) __typeof__(*(0?(__type1(c,t1)*)0:(__type1(!(c),t2)*)0)) -/* cast to double when x is integral, otherwise use typeof(x) */ -#define __RETCAST(x) ( \ - __type2(__IS_FP(x), __typeof__(x), double)) -/* 2 args case, should work for complex types (cpow) */ -#define __RETCAST_2(x, y) ( \ - __type2(__IS_FP(x) && __IS_FP(y), \ - __typeof__((x)+(y)), \ - __typeof__((x)+(y)+1.0))) -/* 3 args case (fma only) */ -#define __RETCAST_3(x, y, z) ( \ - __type2(__IS_FP(x) && __IS_FP(y) && __IS_FP(z), \ - __typeof__((x)+(y)+(z)), \ - __typeof__((x)+(y)+(z)+1.0))) -/* drop complex from the type of x */ -/* TODO: wrong when sizeof(long double)==sizeof(double) */ -#define __RETCAST_REAL(x) ( \ - __type2(__IS_FP(x) && sizeof((x)+I) == sizeof(float complex), float, \ - __type2(sizeof((x)+1.0+I) == sizeof(double complex), double, \ - long double))) -/* add complex to the type of x */ -#define __RETCAST_CX(x) (__typeof__(__RETCAST(x)0+I)) -#else -#define __RETCAST(x) -#define __RETCAST_2(x, y) -#define __RETCAST_3(x, y, z) -#define __RETCAST_REAL(x) -#define __RETCAST_CX(x) -#endif - -/* function selection */ - -#define __tg_real_nocast(fun, x) ( \ - __FLT(x) ? fun ## f (x) : \ - __LDBL(x) ? fun ## l (x) : \ - fun(x) ) - -#define __tg_real(fun, x) (__RETCAST(x)__tg_real_nocast(fun, x)) - -#define __tg_real_2_1(fun, x, y) (__RETCAST(x)( \ - __FLT(x) ? fun ## f (x, y) : \ - __LDBL(x) ? fun ## l (x, y) : \ - fun(x, y) )) - -#define __tg_real_2(fun, x, y) (__RETCAST_2(x, y)( \ - __FLT(x) && __FLT(y) ? fun ## f (x, y) : \ - __LDBL((x)+(y)) ? fun ## l (x, y) : \ - fun(x, y) )) - -#define __tg_complex(fun, x) (__RETCAST_CX(x)( \ - __FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : \ - __LDBLCX((x)+I) ? fun ## l (x) : \ - fun(x) )) - -#define __tg_complex_retreal(fun, x) (__RETCAST_REAL(x)( \ - __FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : \ - __LDBLCX((x)+I) ? fun ## l (x) : \ - fun(x) )) - -#define __tg_real_complex(fun, x) (__RETCAST(x)( \ - __FLTCX(x) ? c ## fun ## f (x) : \ - __DBLCX(x) ? c ## fun (x) : \ - __LDBLCX(x) ? c ## fun ## l (x) : \ - __FLT(x) ? fun ## f (x) : \ - __LDBL(x) ? fun ## l (x) : \ - fun(x) )) - -/* special cases */ - -#define __tg_real_remquo(x, y, z) (__RETCAST_2(x, y)( \ - __FLT(x) && __FLT(y) ? remquof(x, y, z) : \ - __LDBL((x)+(y)) ? remquol(x, y, z) : \ - remquo(x, y, z) )) - -#define __tg_real_fma(x, y, z) (__RETCAST_3(x, y, z)( \ - __FLT(x) && __FLT(y) && __FLT(z) ? fmaf(x, y, z) : \ - __LDBL((x)+(y)+(z)) ? fmal(x, y, z) : \ - fma(x, y, z) )) - -#define __tg_real_complex_pow(x, y) (__RETCAST_2(x, y)( \ - __FLTCX((x)+(y)) && __IS_FP(x) && __IS_FP(y) ? cpowf(x, y) : \ - __FLTCX((x)+(y)) ? cpow(x, y) : \ - __DBLCX((x)+(y)) ? cpow(x, y) : \ - __LDBLCX((x)+(y)) ? cpowl(x, y) : \ - __FLT(x) && __FLT(y) ? powf(x, y) : \ - __LDBL((x)+(y)) ? powl(x, y) : \ - pow(x, y) )) - -#define __tg_real_complex_fabs(x) (__RETCAST_REAL(x)( \ - __FLTCX(x) ? cabsf(x) : \ - __DBLCX(x) ? cabs(x) : \ - __LDBLCX(x) ? cabsl(x) : \ - __FLT(x) ? fabsf(x) : \ - __LDBL(x) ? fabsl(x) : \ - fabs(x) )) - -/* suppress any macros in math.h or complex.h */ - -#undef acos -#undef acosh -#undef asin -#undef asinh -#undef atan -#undef atan2 -#undef atanh -#undef carg -#undef cbrt -#undef ceil -#undef cimag -#undef conj -#undef copysign -#undef cos -#undef cosh -#undef cproj -#undef creal -#undef erf -#undef erfc -#undef exp -#undef exp2 -#undef expm1 -#undef fabs -#undef fdim -#undef floor -#undef fma -#undef fmax -#undef fmin -#undef fmod -#undef frexp -#undef hypot -#undef ilogb -#undef ldexp -#undef lgamma -#undef llrint -#undef llround -#undef log -#undef log10 -#undef log1p -#undef log2 -#undef logb -#undef lrint -#undef lround -#undef nearbyint -#undef nextafter -#undef nexttoward -#undef pow -#undef remainder -#undef remquo -#undef rint -#undef round -#undef scalbln -#undef scalbn -#undef sin -#undef sinh -#undef sqrt -#undef tan -#undef tanh -#undef tgamma -#undef trunc - -/* tg functions */ - -#define acos(x) __tg_real_complex(acos, (x)) -#define acosh(x) __tg_real_complex(acosh, (x)) -#define asin(x) __tg_real_complex(asin, (x)) -#define asinh(x) __tg_real_complex(asinh, (x)) -#define atan(x) __tg_real_complex(atan, (x)) -#define atan2(x,y) __tg_real_2(atan2, (x), (y)) -#define atanh(x) __tg_real_complex(atanh, (x)) -#define carg(x) __tg_complex_retreal(carg, (x)) -#define cbrt(x) __tg_real(cbrt, (x)) -#define ceil(x) __tg_real(ceil, (x)) -#define cimag(x) __tg_complex_retreal(cimag, (x)) -#define conj(x) __tg_complex(conj, (x)) -#define copysign(x,y) __tg_real_2(copysign, (x), (y)) -#define cos(x) __tg_real_complex(cos, (x)) -#define cosh(x) __tg_real_complex(cosh, (x)) -#define cproj(x) __tg_complex(cproj, (x)) -#define creal(x) __tg_complex_retreal(creal, (x)) -#define erf(x) __tg_real(erf, (x)) -#define erfc(x) __tg_real(erfc, (x)) -#define exp(x) __tg_real_complex(exp, (x)) -#define exp2(x) __tg_real(exp2, (x)) -#define expm1(x) __tg_real(expm1, (x)) -#define fabs(x) __tg_real_complex_fabs(x) -#define fdim(x,y) __tg_real_2(fdim, (x), (y)) -#define floor(x) __tg_real(floor, (x)) -#define fma(x,y,z) __tg_real_fma((x), (y), (z)) -#define fmax(x,y) __tg_real_2(fmax, (x), (y)) -#define fmin(x,y) __tg_real_2(fmin, (x), (y)) -#define fmod(x,y) __tg_real_2(fmod, (x), (y)) -#define frexp(x,y) __tg_real_2_1(frexp, (x), (y)) -#define hypot(x,y) __tg_real_2(hypot, (x), (y)) -#define ilogb(x) __tg_real_nocast(ilogb, (x)) -#define ldexp(x,y) __tg_real_2_1(ldexp, (x), (y)) -#define lgamma(x) __tg_real(lgamma, (x)) -#define llrint(x) __tg_real_nocast(llrint, (x)) -#define llround(x) __tg_real_nocast(llround, (x)) -#define log(x) __tg_real_complex(log, (x)) -#define log10(x) __tg_real(log10, (x)) -#define log1p(x) __tg_real(log1p, (x)) -#define log2(x) __tg_real(log2, (x)) -#define logb(x) __tg_real(logb, (x)) -#define lrint(x) __tg_real_nocast(lrint, (x)) -#define lround(x) __tg_real_nocast(lround, (x)) -#define nearbyint(x) __tg_real(nearbyint, (x)) -#define nextafter(x,y) __tg_real_2(nextafter, (x), (y)) -#define nexttoward(x,y) __tg_real_2(nexttoward, (x), (y)) -#define pow(x,y) __tg_real_complex_pow((x), (y)) -#define remainder(x,y) __tg_real_2(remainder, (x), (y)) -#define remquo(x,y,z) __tg_real_remquo((x), (y), (z)) -#define rint(x) __tg_real(rint, (x)) -#define round(x) __tg_real(round, (x)) -#define scalbln(x,y) __tg_real_2_1(scalbln, (x), (y)) -#define scalbn(x,y) __tg_real_2_1(scalbn, (x), (y)) -#define sin(x) __tg_real_complex(sin, (x)) -#define sinh(x) __tg_real_complex(sinh, (x)) -#define sqrt(x) __tg_real_complex(sqrt, (x)) -#define tan(x) __tg_real_complex(tan, (x)) -#define tanh(x) __tg_real_complex(tanh, (x)) -#define tgamma(x) __tg_real(tgamma, (x)) -#define trunc(x) __tg_real(trunc, (x)) - -#endif diff --git a/c/misra/test/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.expected b/c/misra/test/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.expected index 833c2f664f..8032bf38cc 100644 --- a/c/misra/test/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.expected +++ b/c/misra/test/rules/RULE-21-12/ExceptionHandlingFeaturesOfFenvhUsed.expected @@ -1,12 +1,16 @@ -| test.c:4:11:4:23 | call to feclearexcept | Call to banned function $@. | fenv.h:43:5:43:17 | feclearexcept | feclearexcept | -| test.c:4:25:4:34 | FE_INVALID | Call to banned macro $@. | fenv.h:8:1:8:20 | #define FE_INVALID 1 | FE_INVALID | -| test.c:6:3:6:17 | call to fegetexceptflag | Call to banned function $@. | fenv.h:44:5:44:19 | fegetexceptflag | fegetexceptflag | -| test.c:6:24:6:36 | FE_ALL_EXCEPT | Call to banned macro $@. | fenv.h:15:1:15:24 | #define FE_ALL_EXCEPT 63 | FE_ALL_EXCEPT | -| test.c:7:3:7:15 | call to feraiseexcept | Call to banned function $@. | fenv.h:45:5:45:17 | feraiseexcept | feraiseexcept | -| test.c:7:17:7:28 | FE_DIVBYZERO | Call to banned macro $@. | fenv.h:10:1:10:22 | #define FE_DIVBYZERO 4 | FE_DIVBYZERO | -| test.c:8:3:8:15 | call to feraiseexcept | Call to banned function $@. | fenv.h:45:5:45:17 | feraiseexcept | feraiseexcept | -| test.c:8:17:8:27 | FE_OVERFLOW | Call to banned macro $@. | fenv.h:11:1:11:21 | #define FE_OVERFLOW 8 | FE_OVERFLOW | -| test.c:9:3:9:17 | call to fesetexceptflag | Call to banned function $@. | fenv.h:46:5:46:19 | fesetexceptflag | fesetexceptflag | -| test.c:9:24:9:36 | FE_ALL_EXCEPT | Call to banned macro $@. | fenv.h:15:1:15:24 | #define FE_ALL_EXCEPT 63 | FE_ALL_EXCEPT | -| test.c:10:3:10:14 | call to fetestexcept | Call to banned function $@. | fenv.h:47:5:47:16 | fetestexcept | fetestexcept | -| test.c:10:16:10:27 | FE_UNDERFLOW | Call to banned macro $@. | fenv.h:12:1:12:23 | #define FE_UNDERFLOW 16 | FE_UNDERFLOW | +| test.c:2:1:2:17 | #include | Include of banned header 'fenv.h'. | +| test.c:6:11:6:23 | call to feclearexcept | Call to banned function 'feclearexcept'. | +| test.c:6:25:6:34 | FE_INVALID | Expansion of banned macro 'FE_INVALID'. | +| test.c:8:3:8:17 | call to fegetexceptflag | Call to banned function 'fegetexceptflag'. | +| test.c:8:24:8:36 | FE_ALL_EXCEPT | Expansion of banned macro 'FE_ALL_EXCEPT'. | +| test.c:9:3:9:15 | call to feraiseexcept | Call to banned function 'feraiseexcept'. | +| test.c:9:17:9:28 | FE_DIVBYZERO | Expansion of banned macro 'FE_DIVBYZERO'. | +| test.c:10:3:10:15 | call to feraiseexcept | Call to banned function 'feraiseexcept'. | +| test.c:10:17:10:27 | FE_OVERFLOW | Expansion of banned macro 'FE_OVERFLOW'. | +| test.c:11:3:11:17 | call to fesetexceptflag | Call to banned function 'fesetexceptflag'. | +| test.c:11:24:11:36 | FE_ALL_EXCEPT | Expansion of banned macro 'FE_ALL_EXCEPT'. | +| test.c:12:3:12:14 | call to fetestexcept | Call to banned function 'fetestexcept'. | +| test.c:12:16:12:27 | FE_UNDERFLOW | Expansion of banned macro 'FE_UNDERFLOW'. | +| test.c:15:3:15:10 | call to fesetenv | Call to banned function 'fesetenv'. | +| test.c:16:3:16:13 | call to feupdateenv | Call to banned function 'feupdateenv'. | +| test.c:17:3:17:12 | call to fesetround | Call to banned function 'fesetround'. | diff --git a/c/misra/test/rules/RULE-21-12/fenv.h b/c/misra/test/rules/RULE-21-12/fenv.h deleted file mode 100644 index 4240535db9..0000000000 --- a/c/misra/test/rules/RULE-21-12/fenv.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _FENV_H -#define _FENV_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define FE_INVALID 1 -#define __FE_DENORM 2 -#define FE_DIVBYZERO 4 -#define FE_OVERFLOW 8 -#define FE_UNDERFLOW 16 -#define FE_INEXACT 32 - -#define FE_ALL_EXCEPT 63 - -#define FE_TONEAREST 0 -#define FE_DOWNWARD 0x400 -#define FE_UPWARD 0x800 -#define FE_TOWARDZERO 0xc00 - -typedef unsigned short fexcept_t; - -typedef struct { - unsigned short __control_word; - unsigned short __unused1; - unsigned short __status_word; - unsigned short __unused2; - unsigned short __tags; - unsigned short __unused3; - unsigned int __eip; - unsigned short __cs_selector; - unsigned int __opcode : 11; - unsigned int __unused4 : 5; - unsigned int __data_offset; - unsigned short __data_selector; - unsigned short __unused5; - unsigned int __mxcsr; -} fenv_t; - -#define FE_DFL_ENV ((const fenv_t *)-1) - -int feclearexcept(int); -int fegetexceptflag(fexcept_t *, int); -int feraiseexcept(int); -int fesetexceptflag(const fexcept_t *, int); -int fetestexcept(int); - -int fegetround(void); -int fesetround(int); - -int fegetenv(fenv_t *); -int feholdexcept(fenv_t *); -int fesetenv(const fenv_t *); -int feupdateenv(const fenv_t *); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/c/misra/test/rules/RULE-21-12/test.c b/c/misra/test/rules/RULE-21-12/test.c index d6cda837a6..9a049c9ed8 100644 --- a/c/misra/test/rules/RULE-21-12/test.c +++ b/c/misra/test/rules/RULE-21-12/test.c @@ -1,4 +1,6 @@ -#include "fenv.h" +// NON_COMPLIANT: Cannot #include fenv.h. +#include + void f2(); void f1() { int i = feclearexcept(FE_INVALID); // NON_COMPLIANT @@ -8,5 +10,10 @@ void f1() { feraiseexcept(FE_OVERFLOW); // NON_COMPLIANT fesetexceptflag(&i2, FE_ALL_EXCEPT); // NON_COMPLIANT fetestexcept(FE_UNDERFLOW); // NON_COMPLIANT - f2(); // COMPLIANT + fenv_t env; + fegetenv(&env); + fesetenv(&env); // NON_COMPLIANT + feupdateenv(&env); // NON_COMPLIANT + fesetround(0); // NON_COMPLIANT + f2(); // COMPLIANT } diff --git a/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected new file mode 100644 index 0000000000..0a8d568dec --- /dev/null +++ b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected @@ -0,0 +1,2 @@ +| test.c:14:7:14:13 | call to isalnum | The function call to isalnum accepts an argument c3 that is not an unsigned char nor an EOF. | +| test.c:20:7:20:13 | call to isalnum | The function call to isalnum accepts an argument c4 that is not an unsigned char nor an EOF. | \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.clang b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.clang new file mode 100644 index 0000000000..6af28a74db --- /dev/null +++ b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.clang @@ -0,0 +1,2 @@ +| test.c:14:7:14:17 | isalnum(c) | The function isalnum(c) accepts an argument (...) that is not an unsigned char nor an EOF. | +| test.c:20:7:20:17 | isalnum(c) | The function isalnum(c) accepts an argument (...) that is not an unsigned char nor an EOF. | \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.gcc b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.gcc new file mode 100644 index 0000000000..6af28a74db --- /dev/null +++ b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.gcc @@ -0,0 +1,2 @@ +| test.c:14:7:14:17 | isalnum(c) | The function isalnum(c) accepts an argument (...) that is not an unsigned char nor an EOF. | +| test.c:20:7:20:17 | isalnum(c) | The function isalnum(c) accepts an argument (...) that is not an unsigned char nor an EOF. | \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.qcc b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.qcc new file mode 100644 index 0000000000..6af28a74db --- /dev/null +++ b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.expected.qcc @@ -0,0 +1,2 @@ +| test.c:14:7:14:17 | isalnum(c) | The function isalnum(c) accepts an argument (...) that is not an unsigned char nor an EOF. | +| test.c:20:7:20:17 | isalnum(c) | The function isalnum(c) accepts an argument (...) that is not an unsigned char nor an EOF. | \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.qlref b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.qlref new file mode 100644 index 0000000000..be454538c7 --- /dev/null +++ b/c/misra/test/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.qlref @@ -0,0 +1 @@ +rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-13/test.c b/c/misra/test/rules/RULE-21-13/test.c new file mode 100644 index 0000000000..727ac8ad65 --- /dev/null +++ b/c/misra/test/rules/RULE-21-13/test.c @@ -0,0 +1,39 @@ +#include +#include + +void sample() { + unsigned char c1 = 'c'; + int r1 = isalnum( + c1); // COMPLIANT: ASCII 99 is within unsigned char range of [0, 255] + int r2 = isalnum(EOF); // COMPLIANT: EOF (-1) + + int x3 = 256; + int x4 = x3; + int c3 = x4; + int r3 = + isalnum(c3); // NON_COMPLIANT: is outside unsigned char range of [0, 255] + + unsigned char x5 = EOF; + unsigned char x6 = x5; + int c4 = x6 + 10000; + int r4 = + isalnum(c4); // NON_COMPLIANT: is outside unsigned char range of [0, 255] + + int c5 = getchar(); + int r5 = isalnum( + c5); // COMPLIANT: source functions like getchar are modelled + + unsigned char x7; + int c6; + if (x7 == 1) { + c6 = EOF; + } else { + c6 = 'c'; + } + int r6 = + isalnum(c6); // COMPLIANT: either control branch make this call compliant + + int r7 = isalnum(EOF); // COMPLIANT: EOF (-1) +} + +int main() { return 0; } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.expected b/c/misra/test/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.expected new file mode 100644 index 0000000000..5ae49919a9 --- /dev/null +++ b/c/misra/test/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.expected @@ -0,0 +1,38 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (MemcmpUsedToCompareNullTerminatedStrings.ql:23,54-62) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (MemcmpUsedToCompareNullTerminatedStrings.ql:24,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (MemcmpUsedToCompareNullTerminatedStrings.ql:50,20-28) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (MemcmpUsedToCompareNullTerminatedStrings.ql:58,43-56) +edges +| test.c:12:13:12:15 | a | test.c:14:10:14:10 | a | provenance | | +| test.c:12:13:12:15 | a | test.c:23:13:23:13 | a | provenance | | +| test.c:12:13:12:15 | a | test.c:24:10:24:10 | a | provenance | | +| test.c:13:13:13:15 | b | test.c:14:13:14:13 | b | provenance | | +| test.c:18:15:18:28 | {...} | test.c:21:10:21:10 | e | provenance | | +| test.c:19:15:19:28 | {...} | test.c:21:13:21:13 | f | provenance | | +nodes +| test.c:10:10:10:12 | a | semmle.label | a | +| test.c:10:15:10:17 | b | semmle.label | b | +| test.c:12:13:12:15 | a | semmle.label | a | +| test.c:13:13:13:15 | b | semmle.label | b | +| test.c:14:10:14:10 | a | semmle.label | a | +| test.c:14:13:14:13 | b | semmle.label | b | +| test.c:16:10:16:10 | c | semmle.label | c | +| test.c:16:13:16:13 | d | semmle.label | d | +| test.c:18:15:18:28 | {...} | semmle.label | {...} | +| test.c:19:15:19:28 | {...} | semmle.label | {...} | +| test.c:21:10:21:10 | e | semmle.label | e | +| test.c:21:13:21:13 | f | semmle.label | f | +| test.c:23:13:23:13 | a | semmle.label | a | +| test.c:24:10:24:10 | a | semmle.label | a | +| test.c:26:13:26:13 | c | semmle.label | c | +| test.c:27:10:27:10 | c | semmle.label | c | +subpaths +#select +| test.c:10:3:10:8 | call to memcmp | test.c:10:10:10:12 | a | test.c:10:10:10:12 | a | memcmp used to compare $@ with $@. | test.c:10:10:10:12 | a | null-terminated string | test.c:10:15:10:17 | b | null-terminated string | +| test.c:10:3:10:8 | call to memcmp | test.c:10:15:10:17 | b | test.c:10:15:10:17 | b | memcmp used to compare $@ with $@. | test.c:10:10:10:12 | a | null-terminated string | test.c:10:15:10:17 | b | null-terminated string | +| test.c:14:3:14:8 | call to memcmp | test.c:12:13:12:15 | a | test.c:14:10:14:10 | a | memcmp used to compare $@ with $@. | test.c:12:13:12:15 | a | null-terminated string | test.c:13:13:13:15 | b | null-terminated string | +| test.c:14:3:14:8 | call to memcmp | test.c:13:13:13:15 | b | test.c:14:13:14:13 | b | memcmp used to compare $@ with $@. | test.c:12:13:12:15 | a | null-terminated string | test.c:13:13:13:15 | b | null-terminated string | +| test.c:16:3:16:8 | call to memcmp | test.c:16:10:16:10 | c | test.c:16:10:16:10 | c | memcmp used to compare $@ with $@. | test.c:16:10:16:10 | c | null-terminated string | test.c:16:13:16:13 | d | null-terminated string | +| test.c:16:3:16:8 | call to memcmp | test.c:16:13:16:13 | d | test.c:16:13:16:13 | d | memcmp used to compare $@ with $@. | test.c:16:10:16:10 | c | null-terminated string | test.c:16:13:16:13 | d | null-terminated string | +| test.c:21:3:21:8 | call to memcmp | test.c:18:15:18:28 | {...} | test.c:21:10:21:10 | e | memcmp used to compare $@ with $@. | test.c:18:15:18:28 | {...} | null-terminated string | test.c:19:15:19:28 | {...} | null-terminated string | +| test.c:21:3:21:8 | call to memcmp | test.c:19:15:19:28 | {...} | test.c:21:13:21:13 | f | memcmp used to compare $@ with $@. | test.c:18:15:18:28 | {...} | null-terminated string | test.c:19:15:19:28 | {...} | null-terminated string | diff --git a/c/misra/test/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.qlref b/c/misra/test/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.qlref new file mode 100644 index 0000000000..99017569aa --- /dev/null +++ b/c/misra/test/rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.qlref @@ -0,0 +1 @@ +rules/RULE-21-14/MemcmpUsedToCompareNullTerminatedStrings.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-14/test.c b/c/misra/test/rules/RULE-21-14/test.c new file mode 100644 index 0000000000..6ca988242e --- /dev/null +++ b/c/misra/test/rules/RULE-21-14/test.c @@ -0,0 +1,28 @@ +#include + +extern char a[10]; +extern char b[10]; + +char c[10] = {'a', 'b', 0}; +char d[10] = {'a', 'b', 0}; + +void testCmp(int *p) { + memcmp("a", "b", 1); // NON_COMPLIANT + + strcpy(a, "a"); + strcpy(b, "b"); + memcmp(a, b, 1); // NON_COMPLIANT + + memcmp(c, d, 1); // NON_COMPLIANT + + char e[10] = {'a', 'b', 0}; + char f[10] = {'a', 'b', 0}; + + memcmp(e, f, 1); // NON_COMPLIANT + + memcmp(p, a, 1); // COMPLIANT + memcmp(a, p, 1); // COMPLIANT + + memcmp(p, c, 1); // COMPLIANT + memcmp(c, p, 1); // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.expected b/c/misra/test/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.expected new file mode 100644 index 0000000000..67bb52d079 --- /dev/null +++ b/c/misra/test/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.expected @@ -0,0 +1,2 @@ +| test.c:6:3:6:8 | call to memcpy | The dest type int * and src type char * of function memcpy are not compatible. | +| test.c:18:3:18:9 | call to memmove | The dest type char[9] and src type int[2] of function memmove are not compatible. | \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.qlref b/c/misra/test/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.qlref new file mode 100644 index 0000000000..8acf3deee7 --- /dev/null +++ b/c/misra/test/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.qlref @@ -0,0 +1 @@ +rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-15/test.c b/c/misra/test/rules/RULE-21-15/test.c new file mode 100644 index 0000000000..4a3f233c11 --- /dev/null +++ b/c/misra/test/rules/RULE-21-15/test.c @@ -0,0 +1,25 @@ +#include + +void sample() { + int from1 = 1000000; + char to1; + memcpy(&from1, &to1, 1); // NON_COMPLIANT, the types are not compatible + + int from2 = 1000000; + int to2; + memcpy(&from2, &to2, 2); // COMPLIANT + + char from3[] = "string"; + char to3[7]; + memmove(from3, to3, 7); // COMPLIANT + + char from4[] = "sstringg"; + int to4[2]; + memmove(from4, to4, 8); // NON_COMPLIANT, despite being equal in byte counts + + char from5[] = "STRING"; + char to5[] = "string"; + memcmp(from5, to5, 2); // COMPLIANT +} + +int main() { return 0; } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.expected b/c/misra/test/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.expected new file mode 100644 index 0000000000..d774a833f2 --- /dev/null +++ b/c/misra/test/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.expected @@ -0,0 +1,10 @@ +| test.c:23:10:23:10 | p | Argument is a pointer to float. | +| test.c:23:13:23:13 | q | Argument is a pointer to float. | +| test.c:35:10:35:10 | p | Argument is a pointer to S1. | +| test.c:35:13:35:13 | q | Argument is a pointer to S1. | +| test.c:41:10:41:10 | p | Argument is a pointer to U. | +| test.c:41:13:41:13 | q | Argument is a pointer to U. | +| test.c:45:10:45:10 | p | Argument is a pointer to char. | +| test.c:45:13:45:13 | q | Argument is a pointer to char. | +| test.c:49:10:49:10 | p | Argument is a pointer to char. | +| test.c:49:13:49:13 | q | Argument is a pointer to char. | diff --git a/c/misra/test/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.qlref b/c/misra/test/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.qlref new file mode 100644 index 0000000000..cf550d800a --- /dev/null +++ b/c/misra/test/rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.qlref @@ -0,0 +1 @@ +rules/RULE-21-16/MemcmpOnInappropriateEssentialTypeArgs.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-16/test.c b/c/misra/test/rules/RULE-21-16/test.c new file mode 100644 index 0000000000..f7c4c96707 --- /dev/null +++ b/c/misra/test/rules/RULE-21-16/test.c @@ -0,0 +1,54 @@ +#include +#include + +void testMemcmpSignedInt(signed int *p, signed int *q, size_t size) { + memcmp(p, q, size); // COMPLIANT +} + +void testMemcmpUnsignedInt(unsigned int *p, unsigned int *q, size_t size) { + memcmp(p, q, size); // COMPLIANT +} + +enum E1 { E1_1, E1_2 }; + +void testMemcmpEnum(enum E1 *p, enum E1 *q, size_t size) { + memcmp(p, q, size); // COMPLIANT +} + +void testMemcmpBool(bool *p, bool *q, size_t size) { + memcmp(p, q, size); // COMPLIANT +} + +void testMemcmpFloat(float *p, float *q, size_t size) { + memcmp(p, q, size); // NON_COMPLIANT +} + +void testMemcmpPointerToPointer(void **p, void **q, size_t size) { + memcmp(p, q, size); // COMPLIANT +} + +struct S1 { + int i; +}; + +void testMemcmpStruct(struct S1 *p, struct S1 *q, size_t size) { + memcmp(p, q, size); // NON_COMPLIANT +} + +union U; + +void testMemcmpUnion(union U *p, union U *q, size_t size) { + memcmp(p, q, size); // NON_COMPLIANT +} + +void testMemcmpChar(char *p, char *q, size_t size) { + memcmp(p, q, size); // NON_COMPLIANT +} + +void testMemcmpCharArray(char p[10], char q[10], size_t size) { + memcmp(p, q, size); // NON_COMPLIANT +} + +void testMemcmpIntArray(int p[10], int q[10], size_t size) { + memcmp(p, q, size); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.expected b/c/misra/test/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.expected new file mode 100644 index 0000000000..a7e269e292 --- /dev/null +++ b/c/misra/test/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.expected @@ -0,0 +1,26 @@ +| test.c:31:5:31:10 | call to strcat | The $@ passed to strcat might not be null-terminated. | test.c:31:12:31:15 | buf1 | argument | test.c:31:12:31:15 | buf1 | | +| test.c:36:5:36:10 | call to strcat | The size of the $@ passed to strcat is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:36:24:36:30 | 12345 | read buffer | test.c:36:12:36:19 | call to get_ca_5 | write buffer | +| test.c:38:5:38:10 | call to strcat | The size of the $@ passed to strcat is 5 bytes, but the size of the $@ is only 4 bytes. | test.c:38:28:38:33 | 1234 | read buffer | test.c:38:12:38:25 | ... + ... | write buffer | +| test.c:43:5:43:10 | call to strchr | The $@ passed to strchr might not be null-terminated. | test.c:43:12:43:18 | ca5_bad | argument | test.c:43:12:43:18 | ca5_bad | | +| test.c:45:5:45:10 | call to strchr | The $@ passed to strchr is 5 bytes, but an offset of 5 bytes is used to access it. | test.c:45:12:45:23 | ... + ... | read buffer | test.c:45:12:45:23 | ... + ... | | +| test.c:47:5:47:11 | call to strrchr | The $@ passed to strrchr might not be null-terminated. | test.c:47:13:47:19 | ca5_bad | argument | test.c:47:13:47:19 | ca5_bad | | +| test.c:49:5:49:11 | call to strrchr | The $@ passed to strrchr is 5 bytes, but an offset of 5 bytes is used to access it. | test.c:49:13:49:24 | ... + ... | read buffer | test.c:49:13:49:24 | ... + ... | | +| test.c:53:5:53:10 | call to strcmp | The $@ passed to strcmp might not be null-terminated. | test.c:53:22:53:28 | ca5_bad | argument | test.c:53:22:53:28 | ca5_bad | | +| test.c:55:5:55:10 | call to strcmp | The $@ passed to strcmp might not be null-terminated. | test.c:55:12:55:18 | ca5_bad | argument | test.c:55:12:55:18 | ca5_bad | | +| test.c:58:5:58:11 | call to strcoll | The $@ passed to strcoll might not be null-terminated. | test.c:58:23:58:29 | ca5_bad | argument | test.c:58:23:58:29 | ca5_bad | | +| test.c:60:5:60:11 | call to strcoll | The $@ passed to strcoll might not be null-terminated. | test.c:60:13:60:19 | ca5_bad | argument | test.c:60:13:60:19 | ca5_bad | | +| test.c:66:5:66:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:66:22:66:28 | test1 | read buffer | test.c:66:12:66:19 | ca5_good | write buffer | +| test.c:70:5:70:10 | call to strcpy | The $@ passed to strcpy might not be null-terminated. | test.c:70:24:70:30 | ca5_bad | argument | test.c:70:24:70:30 | ca5_bad | | +| test.c:71:5:71:10 | call to strcpy | The size of the $@ passed to strcpy is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:71:24:71:31 | ca6_good | read buffer | test.c:71:12:71:19 | call to get_ca_5 | write buffer | +| test.c:76:5:76:11 | call to strcspn | The $@ passed to strcspn might not be null-terminated. | test.c:76:13:76:19 | ca5_bad | argument | test.c:76:13:76:19 | ca5_bad | | +| test.c:78:5:78:11 | call to strcspn | The $@ passed to strcspn is null. | test.c:78:13:78:16 | 0 | argument | test.c:78:13:78:16 | 0 | | +| test.c:80:5:80:10 | call to strspn | The $@ passed to strspn might not be null-terminated. | test.c:80:12:80:18 | ca5_bad | argument | test.c:80:12:80:18 | ca5_bad | | +| test.c:82:5:82:10 | call to strspn | The $@ passed to strspn is null. | test.c:82:12:82:15 | 0 | argument | test.c:82:12:82:15 | 0 | | +| test.c:86:5:86:10 | call to strlen | The $@ passed to strlen might not be null-terminated. | test.c:86:12:86:18 | ca5_bad | argument | test.c:86:12:86:18 | ca5_bad | | +| test.c:88:5:88:10 | call to strlen | The $@ passed to strlen is 5 bytes, but an offset of 5 bytes is used to access it. | test.c:88:12:88:23 | ... + ... | read buffer | test.c:88:12:88:23 | ... + ... | | +| test.c:93:5:93:11 | call to strpbrk | The $@ passed to strpbrk might not be null-terminated. | test.c:93:13:93:19 | ca5_bad | argument | test.c:93:13:93:19 | ca5_bad | | +| test.c:95:5:95:11 | call to strpbrk | The $@ passed to strpbrk is null. | test.c:95:13:95:16 | 0 | argument | test.c:95:13:95:16 | 0 | | +| test.c:102:5:102:10 | call to strstr | The $@ passed to strstr might not be null-terminated. | test.c:102:12:102:18 | ca5_bad | argument | test.c:102:12:102:18 | ca5_bad | | +| test.c:111:5:111:10 | call to strtok | The $@ passed to strtok is null. | test.c:111:18:111:21 | 0 | argument | test.c:111:18:111:21 | 0 | | +| test.c:113:5:113:10 | call to strtok | The $@ passed to strtok might not be null-terminated. | test.c:113:12:113:18 | ca5_bad | argument | test.c:113:12:113:18 | ca5_bad | | +| test.c:117:5:117:10 | call to strtok | The $@ passed to strtok might not be null-terminated. | test.c:117:22:117:28 | ca6_bad | argument | test.c:117:22:117:28 | ca6_bad | | diff --git a/c/misra/test/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.qlref b/c/misra/test/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.qlref new file mode 100644 index 0000000000..001582cdd8 --- /dev/null +++ b/c/misra/test/rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.qlref @@ -0,0 +1 @@ +rules/RULE-21-17/StringFunctionPointerArgumentOutOfBounds.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-17/test.c b/c/misra/test/rules/RULE-21-17/test.c new file mode 100644 index 0000000000..900cb05eda --- /dev/null +++ b/c/misra/test/rules/RULE-21-17/test.c @@ -0,0 +1,119 @@ +// test partially copied from CERT-C ARR38-C test +#include +#include + +char *get_ca_5(void) { + void *ptr = malloc(5 * sizeof(char)); + memset(ptr, 0, 5 * sizeof(char)); + return (char *)ptr; +} + +void test(void) { + char ca5_good[5] = "test"; // ok + char ca5_bad[5] = "test1"; // no null terminator + char ca6_good[6] = "test1"; // ok + char ca6_bad[6] = "test12"; // no null terminator + + // strcat + { + char buf0[10]; // memset after first use + char buf1[10]; // no memset + char buf2[10]; // memset before first use + char buf3[10] = {'\0'}; + char buf4[10] = "12345"; + + strcat(buf0, " "); // NON_COMPLIANT[FALSE_NEGATIVE] - not null terminated at + // initialization + + memset(buf0, 0, sizeof(buf0)); // COMPLIANT + memset(buf2, 0, sizeof(buf2)); // COMPLIANT + + strcat(buf1, " "); // NON_COMPLIANT - not null terminated + strcat(buf2, " "); // COMPLIANT + strcat(buf3, " "); // COMPLIANT + strcat(buf4, "12345"); // NON_COMPLIANT[FALSE_NEGATIVE] + + strcat(get_ca_5(), "12345"); // NON_COMPLIANT + strcat(get_ca_5(), "1234"); // COMPLIANT + strcat(get_ca_5() + 1, "1234"); // NON_COMPLIANT + } + // strchr and strrchr + { + strchr(ca5_good, 't'); // COMPLIANT + strchr(ca5_bad, 't'); // NON_COMPLIANT + strchr(ca5_good + 4, 't'); // COMPLIANT + strchr(ca5_good + 5, 't'); // NON_COMPLIANT + strrchr(ca5_good, 1); // COMPLIANT + strrchr(ca5_bad, 1); // NON_COMPLIANT + strrchr(ca5_good + 4, 1); // COMPLIANT + strrchr(ca5_good + 5, 1); // NON_COMPLIANT + } + // strcmp and strcoll + { + strcmp(ca5_good, ca5_bad); // NON_COMPLIANT + strcmp(ca5_good, ca5_good); // COMPLIANT + strcmp(ca5_bad, ca5_good); // NON_COMPLIANT + strcmp(ca5_good, ca6_good); // COMPLIANT + strcmp(ca6_good, ca5_good); // COMPLIANT + strcoll(ca5_good, ca5_bad); // NON_COMPLIANT + strcoll(ca5_good, ca5_good); // COMPLIANT + strcoll(ca5_bad, ca5_good); // NON_COMPLIANT + strcoll(ca5_good, ca6_good); // COMPLIANT + strcoll(ca6_good, ca5_good); // COMPLIANT + } + // strcpy + { + strcpy(ca5_good, "test1"); // NON_COMPLIANT + strcpy(ca5_bad, "test"); // COMPLIANT + // strcpy to char buffer indirect + strcpy(get_ca_5(), ca5_good); // COMPLIANT + strcpy(get_ca_5(), ca5_bad); // NON_COMPLIANT + strcpy(get_ca_5(), ca6_good); // NON_COMPLIANT + } + // strcspn and strspn + { + strcspn(ca5_good, "test"); // COMPLIANT + strcspn(ca5_bad, "test"); // NON_COMPLIANT - not null-terminated + strcspn(ca5_good, "1234567890"); // COMPLIANT + strcspn(NULL, "12345"); // NON_COMPLIANT + strspn(ca5_good, "test"); // COMPLIANT + strspn(ca5_bad, "test"); // NON_COMPLIANT - not null-terminated + strspn(ca5_good, "1234567890"); // COMPLIANT + strspn(NULL, "12345"); // NON_COMPLIANT + } + // strlen + { + strlen(ca5_bad); // NON_COMPLIANT + strlen(ca5_good + 4); // COMPLIANT + strlen(ca5_good + 5); // NON_COMPLIANT + } + // strpbrk + { + strpbrk(ca5_good, "test"); // COMPLIANT + strpbrk(ca5_bad, "test"); // NON_COMPLIANT - not null-terminated + strpbrk(ca5_good, "1234567890"); // COMPLIANT + strpbrk(NULL, "12345"); // NON_COMPLIANT + } + // strstr + { + strstr("12345", "123"); // COMPLIANT + strstr("123", "12345"); // COMPLIANT + strstr(ca5_good, "test"); // COMPLIANT + strstr(ca5_bad, "test"); // NON_COMPLIANT - not null-terminated + strstr(ca5_good, "1234567890"); // COMPLIANT + } + // strtok + { + char ca5_good[5] = "test"; // ok + char ca5_bad[5] = "test1"; // no null terminator + char ca6_good[6] = "test1"; // ok + char ca6_bad[6] = "test12"; // no null terminator + strtok(NULL, NULL); // NON_COMPLIANT - 2nd arg null + strtok(NULL, ""); // COMPLIANT + strtok(ca5_bad, ""); // NON_COMPLIANT - 1st arg not null-terminated + strtok(ca5_good, ""); // COMPLIANT + strtok(ca6_good, ca5_good); // COMPLIANT + strtok(ca6_good + 4, ca6_good); // COMPLIANT + strtok(ca6_good, ca6_bad); // NON_COMPLIANT - 2nd arg not null-terminated + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.expected b/c/misra/test/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.expected new file mode 100644 index 0000000000..fe3cbba947 --- /dev/null +++ b/c/misra/test/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.expected @@ -0,0 +1,35 @@ +| test.c:16:5:16:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the $@ is 65 bytes. | test.c:16:12:16:15 | buf1 | write buffer | test.c:16:24:16:39 | ... + ... | size argument | +| test.c:16:5:16:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the $@ is 65 bytes. | test.c:16:18:16:21 | buf2 | read buffer | test.c:16:24:16:39 | ... + ... | size argument | +| test.c:18:5:18:10 | call to memcpy | The size of the $@ passed to memcpy is 63 bytes, but the $@ is 64 bytes. | test.c:18:12:18:19 | ... + ... | write buffer | test.c:18:28:18:39 | sizeof() | size argument | +| test.c:18:5:18:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the size of the $@ is only 63 bytes. | test.c:18:22:18:25 | buf2 | read buffer | test.c:18:12:18:19 | ... + ... | write buffer | +| test.c:19:5:19:10 | call to memcpy | The size of the $@ passed to memcpy is 63 bytes, but the $@ is 128 bytes. | test.c:19:18:19:25 | ... + ... | read buffer | test.c:19:28:19:43 | ... * ... | size argument | +| test.c:19:5:19:10 | call to memcpy | The size of the $@ passed to memcpy is 64 bytes, but the $@ is 128 bytes. | test.c:19:12:19:15 | buf1 | write buffer | test.c:19:28:19:43 | ... * ... | size argument | +| test.c:25:5:25:10 | call to memcmp | The size of the $@ passed to memcmp is 64 bytes, but the $@ is 65 bytes. | test.c:25:12:25:15 | buf1 | write buffer | test.c:25:24:25:39 | ... + ... | size argument | +| test.c:25:5:25:10 | call to memcmp | The size of the $@ passed to memcmp is 64 bytes, but the $@ is 65 bytes. | test.c:25:18:25:21 | buf2 | read buffer | test.c:25:24:25:39 | ... + ... | size argument | +| test.c:27:5:27:10 | call to memcmp | The size of the $@ passed to memcmp is 63 bytes, but the $@ is 64 bytes. | test.c:27:12:27:19 | ... + ... | write buffer | test.c:27:28:27:39 | sizeof() | size argument | +| test.c:27:5:27:10 | call to memcmp | The size of the $@ passed to memcmp is 64 bytes, but the size of the $@ is only 63 bytes. | test.c:27:22:27:25 | buf2 | read buffer | test.c:27:12:27:19 | ... + ... | write buffer | +| test.c:28:5:28:10 | call to memcmp | The size of the $@ passed to memcmp is 63 bytes, but the $@ is 128 bytes. | test.c:28:18:28:25 | ... + ... | read buffer | test.c:28:28:28:43 | ... * ... | size argument | +| test.c:28:5:28:10 | call to memcmp | The size of the $@ passed to memcmp is 64 bytes, but the $@ is 128 bytes. | test.c:28:12:28:15 | buf1 | write buffer | test.c:28:28:28:43 | ... * ... | size argument | +| test.c:33:5:33:10 | call to memchr | The size of the $@ passed to memchr is 128 bytes, but the $@ is 129 bytes. | test.c:33:12:33:14 | buf | read buffer | test.c:33:20:33:34 | ... + ... | size argument | +| test.c:34:5:34:10 | call to memchr | The size of the $@ passed to memchr is 128 bytes, but the $@ is 129 bytes. | test.c:34:12:34:14 | buf | read buffer | test.c:34:20:34:34 | ... + ... | size argument | +| test.c:36:5:36:10 | call to memchr | The $@ passed to memchr is null. | test.c:36:12:36:15 | 0 | argument | test.c:36:12:36:15 | 0 | | +| test.c:41:5:41:10 | call to memset | The size of the $@ passed to memset is 128 bytes, but the $@ is 129 bytes. | test.c:41:12:41:14 | buf | write buffer | test.c:41:20:41:34 | ... + ... | size argument | +| test.c:42:5:42:10 | call to memset | The size of the $@ passed to memset is 128 bytes, but the $@ is 129 bytes. | test.c:42:12:42:14 | buf | write buffer | test.c:42:20:42:34 | ... + ... | size argument | +| test.c:44:5:44:10 | call to memset | The $@ passed to memset is null. | test.c:44:12:44:15 | 0 | argument | test.c:44:12:44:15 | 0 | | +| test.c:50:5:50:11 | call to memmove | The size of the $@ passed to memmove is 128 bytes, but the $@ is 129 bytes. | test.c:50:13:50:16 | buf1 | write buffer | test.c:50:25:50:40 | ... + ... | size argument | +| test.c:50:5:50:11 | call to memmove | The size of the $@ passed to memmove is 256 bytes, but the size of the $@ is only 128 bytes. | test.c:50:19:50:22 | buf2 | read buffer | test.c:50:13:50:16 | buf1 | write buffer | +| test.c:52:5:52:11 | call to memmove | The size of the $@ passed to memmove is 127 bytes, but the $@ is 128 bytes. | test.c:52:13:52:20 | ... + ... | write buffer | test.c:52:29:52:40 | sizeof() | size argument | +| test.c:52:5:52:11 | call to memmove | The size of the $@ passed to memmove is 256 bytes, but the size of the $@ is only 127 bytes. | test.c:52:23:52:26 | buf2 | read buffer | test.c:52:13:52:20 | ... + ... | write buffer | +| test.c:54:5:54:11 | call to memmove | The size of the $@ passed to memmove is 128 bytes, but the $@ is 256 bytes. | test.c:54:19:54:22 | buf1 | read buffer | test.c:54:25:54:36 | sizeof() | size argument | +| test.c:62:5:62:11 | call to strncpy | The size of the $@ passed to strncpy is 128 bytes, but the $@ is 129 bytes. | test.c:62:13:62:16 | buf1 | write buffer | test.c:62:25:62:40 | ... + ... | size argument | +| test.c:62:5:62:11 | call to strncpy | The size of the $@ passed to strncpy is 256 bytes, but the size of the $@ is only 128 bytes. | test.c:62:19:62:22 | buf2 | read buffer | test.c:62:13:62:16 | buf1 | write buffer | +| test.c:64:5:64:11 | call to strncpy | The size of the $@ passed to strncpy is 127 bytes, but the $@ is 128 bytes. | test.c:64:13:64:20 | ... + ... | write buffer | test.c:64:29:64:40 | sizeof() | size argument | +| test.c:64:5:64:11 | call to strncpy | The size of the $@ passed to strncpy is 256 bytes, but the size of the $@ is only 127 bytes. | test.c:64:23:64:26 | buf2 | read buffer | test.c:64:13:64:20 | ... + ... | write buffer | +| test.c:77:5:77:11 | call to strncat | The $@ passed to strncat might not be null-terminated. | test.c:77:13:77:16 | buf1 | argument | test.c:77:13:77:16 | buf1 | | +| test.c:81:5:81:11 | call to strncat | The size of the $@ passed to strncat is 6 bytes, but the size of the $@ is only 5 bytes. | test.c:81:25:81:31 | 12345 | read buffer | test.c:81:13:81:20 | call to get_ca_5 | write buffer | +| test.c:83:5:83:11 | call to strncat | The size of the $@ passed to strncat is 5 bytes, but the size of the $@ is only 4 bytes. | test.c:83:29:83:34 | 1234 | read buffer | test.c:83:13:83:26 | ... + ... | write buffer | +| test.c:93:5:93:11 | call to strncmp | The size of the $@ passed to strncmp is 5 bytes, but the $@ is 6 bytes. | test.c:93:23:93:30 | ca5_good | read buffer | test.c:93:33:93:33 | 6 | size argument | +| test.c:94:5:94:11 | call to strncmp | The size of the $@ passed to strncmp is 5 bytes, but the $@ is 6 bytes. | test.c:94:13:94:20 | ca5_good | write buffer | test.c:94:32:94:32 | 6 | size argument | +| test.c:94:5:94:11 | call to strncmp | The size of the $@ passed to strncmp is 5 bytes, but the $@ is 6 bytes. | test.c:94:23:94:29 | ca5_bad | read buffer | test.c:94:32:94:32 | 6 | size argument | +| test.c:101:5:101:11 | call to strxfrm | The size of the $@ passed to strxfrm is 64 bytes, but the $@ is 65 bytes. | test.c:101:13:101:15 | buf | write buffer | test.c:101:25:101:39 | ... + ... | size argument | +| test.c:103:5:103:11 | call to strxfrm | The $@ passed to strxfrm might not be null-terminated. | test.c:103:22:103:25 | buf2 | argument | test.c:103:22:103:25 | buf2 | | diff --git a/c/misra/test/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.qlref b/c/misra/test/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.qlref new file mode 100644 index 0000000000..9d3cdd6f64 --- /dev/null +++ b/c/misra/test/rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.qlref @@ -0,0 +1 @@ +rules/RULE-21-18/StringLibrarySizeArgumentOutOfBounds.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-18/test.c b/c/misra/test/rules/RULE-21-18/test.c new file mode 100644 index 0000000000..d1668a774b --- /dev/null +++ b/c/misra/test/rules/RULE-21-18/test.c @@ -0,0 +1,106 @@ +// test partially copied from CERT-C ARR38-C test +#include +#include + +char *get_ca_5(void) { + void *ptr = malloc(5 * sizeof(char)); + memset(ptr, 0, 5 * sizeof(char)); + return (char *)ptr; +} + +void test(void) { + { + char buf1[64]; + char buf2[64]; + memcpy(buf1, buf2, sizeof(buf1)); // COMPLIANT + memcpy(buf1, buf2, sizeof(buf1) + 1); // NON_COMPLIANT + memcpy(buf1, buf2, sizeof(buf1) - 1); // COMPLIANT + memcpy(buf1 + 1, buf2, sizeof(buf1)); // NON_COMPLIANT + memcpy(buf1, buf2 + 1, sizeof(buf1) * 2); // NON_COMPLIANT + } + { + char buf1[64]; + char buf2[64]; + memcmp(buf1, buf2, sizeof(buf1)); // COMPLIANT + memcmp(buf1, buf2, sizeof(buf1) + 1); // NON_COMPLIANT + memcmp(buf1, buf2, sizeof(buf1) - 1); // COMPLIANT + memcmp(buf1 + 1, buf2, sizeof(buf1)); // NON_COMPLIANT + memcmp(buf1, buf2 + 1, sizeof(buf1) * 2); // NON_COMPLIANT + } + { + char buf[128]; + memchr(buf, 0, sizeof(buf)); // COMPLIANT + memchr(buf, 0, sizeof(buf) + 1); // NON_COMPLIANT + memchr(buf, 0, sizeof(buf) + 1); // NON_COMPLIANT + memchr(buf, 0, sizeof(buf) - 1); // COMPLIANT + memchr(NULL, 0, sizeof(buf)); // NON_COMPLIANT + } + { + char buf[128]; + memset(buf, 0, sizeof(buf)); // COMPLIANT + memset(buf, 0, sizeof(buf) + 1); // NON_COMPLIANT + memset(buf, 0, sizeof(buf) + 1); // NON_COMPLIANT + memset(buf, 0, sizeof(buf) - 1); // COMPLIANT + memset(NULL, 0, sizeof(buf)); // NON_COMPLIANT + } + { + char buf1[128]; + char buf2[256]; + memmove(buf1, buf2, sizeof(buf1)); // COMPLIANT + memmove(buf1, buf2, sizeof(buf1) + 1); // NON_COMPLIANT + memmove(buf1, buf2, sizeof(buf1) - 1); // COMPLIANT + memmove(buf1 + 1, buf2, sizeof(buf1)); // NON_COMPLIANT + memmove(buf1, buf2 + 1, sizeof(buf1)); // COMPLIANT + memmove(buf2, buf1, sizeof(buf2)); // NON_COMPLIANT + memmove(buf2, buf1, sizeof(buf1)); // COMPLIANT + } + { + char buf1[128]; + char buf2[256] = {0}; + strncpy(buf2, buf1, sizeof(buf1)); // COMPLIANT + strncpy(buf1, buf2, sizeof(buf1)); // COMPLIANT + strncpy(buf1, buf2, sizeof(buf1) + 1); // NON_COMPLIANT + strncpy(buf1, buf2, sizeof(buf1) - 1); // COMPLIANT + strncpy(buf1 + 1, buf2, sizeof(buf1)); // NON_COMPLIANT + } + { + char buf0[10]; // memset after first use + char buf1[10]; // no memset + char buf2[10]; // memset before first use + char buf3[10] = {'\0'}; + char buf4[10] = "12345"; + + strncat(buf0, " ", + 1); // NON_COMPLIANT[FALSE_NEGATIVE] - buf0 not null-terminated + memset(buf0, 0, sizeof(buf0)); // COMPLIANT + memset(buf2, 0, sizeof(buf2)); // COMPLIANT + strncat(buf1, " ", 1); // NON_COMPLIANT - not null-terminated + strncat(buf2, " ", 1); // COMPLIANT + strncat(buf3, " ", 1); // COMPLIANT + strncat(buf4, "12345", 5); // NON_COMPLIANT[FALSE_NEGATIVE] + strncat(get_ca_5(), "12345", 5); // NON_COMPLIANT - null-terminator past end + strncat(get_ca_5(), "1234", 4); // COMPLIANT + strncat(get_ca_5() + 1, "1234", 4); // NON_COMPLIANT + strncat(get_ca_5(), "12", 2); // COMPLIANT + } + { + char ca5_good[5] = "test"; // ok + char ca5_bad[5] = "test1"; // no null terminator + char ca6_good[6] = "test1"; // ok + strncmp(ca5_good, ca5_bad, 4); // COMPLIANT + strncmp(ca5_good, ca5_bad, 5); // COMPLIANT + strncmp(ca6_good, ca5_bad, 5); // COMPLIANT + strncmp(ca6_good, ca5_good, 6); // COMPLIANT[FALSE_POSITIVE] + strncmp(ca5_good, ca5_bad, 6); // NON_COMPLIANT + } + // strxfrm + { + char buf[64]; + char buf2[128]; + strxfrm(buf, "abc", sizeof(buf)); // COMPLIANT + strxfrm(buf, "abc", sizeof(buf) + 1); // NON_COMPLIANT + strxfrm(buf, "abc", sizeof(buf) - 1); // COMPLIANT + strxfrm(buf + 1, buf2, + sizeof(buf) - 1); // NON_COMPLIANT - not null-terminated + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.expected index 88d809ef37..f3fc1204c4 100644 --- a/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.expected @@ -1,5 +1,5 @@ -| test.c:10:3:10:8 | call to system | Call to banned function $@. | test.c:4:5:4:10 | system | system | -| test.c:18:3:18:8 | call to system | Call to banned function $@. | test.c:4:5:4:10 | system | system | -| test.c:19:3:19:8 | call to system | Call to banned function $@. | test.c:4:5:4:10 | system | system | -| test.c:20:3:20:8 | call to system | Call to banned function $@. | test.c:4:5:4:10 | system | system | -| test.c:21:3:21:8 | call to system | Call to banned function $@. | test.c:4:5:4:10 | system | system | +| test.c:6:3:6:8 | call to system | Call to banned function system. | +| test.c:14:3:14:8 | call to system | Call to banned function system. | +| test.c:15:3:15:8 | call to system | Call to banned function system. | +| test.c:16:3:16:8 | call to system | Call to banned function system. | +| test.c:17:3:17:8 | call to system | Call to banned function system. | diff --git a/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.testref b/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.testref deleted file mode 100644 index a71ee90500..0000000000 --- a/c/misra/test/rules/RULE-21-21/SystemOfStdlibhUsed.testref +++ /dev/null @@ -1 +0,0 @@ -c/common/test/rules/systemused/SystemUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-21/test.c b/c/misra/test/rules/RULE-21-21/test.c index 5040bba006..ff8522cb28 100644 --- a/c/misra/test/rules/RULE-21-21/test.c +++ b/c/misra/test/rules/RULE-21-21/test.c @@ -1,9 +1,5 @@ -typedef struct _FILE FILE; -#define NULL (void *)0 - -int system(const char *); -void abort(void); -FILE *popen(const char *, const char *); +#include +#include void f1(const char *p1) { FILE *l1; diff --git a/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected new file mode 100644 index 0000000000..03dddb8dfe --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected @@ -0,0 +1,134 @@ +| test.c:29:7:29:7 | c | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:40:7:40:7 | e | Argument 1 provided to type-generic macro 'cos' has essentially enum type, which is not essentially signed, unsigned, or floating type. | +| test.c:41:7:41:7 | b | Argument 1 provided to type-generic macro 'cos' has essentially boolean type, which is not essentially signed, unsigned, or floating type. | +| test.c:156:8:156:8 | c | Argument 1 provided to type-generic macro 'acos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:157:9:157:9 | c | Argument 1 provided to type-generic macro 'acosh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:158:8:158:8 | c | Argument 1 provided to type-generic macro 'asin' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:159:9:159:9 | c | Argument 1 provided to type-generic macro 'asinh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:160:9:160:9 | c | Argument 1 provided to type-generic macro 'atan2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:161:12:161:12 | c | Argument 2 provided to type-generic macro 'atan2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:162:9:162:9 | c | Argument 1 provided to type-generic macro 'atanh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:163:8:163:8 | c | Argument 1 provided to type-generic macro 'carg' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:164:8:164:8 | c | Argument 1 provided to type-generic macro 'ceil' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:165:9:165:9 | c | Argument 1 provided to type-generic macro 'cimag' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:166:8:166:8 | c | Argument 1 provided to type-generic macro 'conj' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:167:15:167:15 | c | Argument 2 provided to type-generic macro 'copysign' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:168:12:168:12 | c | Argument 1 provided to type-generic macro 'copysign' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:169:8:169:8 | c | Argument 1 provided to type-generic macro 'cosh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:170:9:170:9 | c | Argument 1 provided to type-generic macro 'cproj' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:171:9:171:9 | c | Argument 1 provided to type-generic macro 'creal' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:172:7:172:7 | c | Argument 1 provided to type-generic macro 'erf' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:173:8:173:8 | c | Argument 1 provided to type-generic macro 'erfc' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:174:7:174:7 | c | Argument 1 provided to type-generic macro 'exp' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:175:8:175:8 | c | Argument 1 provided to type-generic macro 'exp2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:176:9:176:9 | c | Argument 1 provided to type-generic macro 'expm1' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:177:8:177:8 | c | Argument 1 provided to type-generic macro 'fabs' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:178:8:178:8 | c | Argument 1 provided to type-generic macro 'fdim' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:179:11:179:11 | c | Argument 2 provided to type-generic macro 'fdim' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:180:9:180:9 | c | Argument 1 provided to type-generic macro 'floor' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:181:7:181:7 | c | Argument 1 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:182:10:182:10 | c | Argument 2 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:183:13:183:13 | c | Argument 3 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:184:8:184:8 | c | Argument 1 provided to type-generic macro 'fmax' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:185:11:185:11 | c | Argument 2 provided to type-generic macro 'fmax' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:186:8:186:8 | c | Argument 1 provided to type-generic macro 'fmin' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:187:11:187:11 | c | Argument 2 provided to type-generic macro 'fmin' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:188:8:188:8 | c | Argument 1 provided to type-generic macro 'fmod' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:189:11:189:11 | c | Argument 2 provided to type-generic macro 'fmod' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:190:9:190:9 | c | Argument 1 provided to type-generic macro 'frexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:191:9:191:9 | c | Argument 1 provided to type-generic macro 'hypot' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:192:12:192:12 | c | Argument 2 provided to type-generic macro 'hypot' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:193:9:193:9 | c | Argument 1 provided to type-generic macro 'ilogb' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:194:9:194:9 | c | Argument 1 provided to type-generic macro 'ldexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:195:12:195:12 | c | Argument 2 provided to type-generic macro 'ldexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:196:10:196:10 | c | Argument 1 provided to type-generic macro 'lgamma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:197:10:197:10 | c | Argument 1 provided to type-generic macro 'llrint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:198:11:198:11 | c | Argument 1 provided to type-generic macro 'llround' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:199:7:199:7 | c | Argument 1 provided to type-generic macro 'log' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:200:9:200:9 | c | Argument 1 provided to type-generic macro 'log10' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:201:9:201:9 | c | Argument 1 provided to type-generic macro 'log1p' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:202:8:202:8 | c | Argument 1 provided to type-generic macro 'log2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:203:8:203:8 | c | Argument 1 provided to type-generic macro 'logb' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:204:9:204:9 | c | Argument 1 provided to type-generic macro 'lrint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:205:10:205:10 | c | Argument 1 provided to type-generic macro 'lround' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:206:13:206:13 | c | Argument 1 provided to type-generic macro 'nearbyint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:207:13:207:13 | c | Argument 1 provided to type-generic macro 'nextafter' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:208:16:208:16 | c | Argument 2 provided to type-generic macro 'nextafter' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:209:14:209:14 | c | Argument 1 provided to type-generic macro 'nexttoward' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:210:17:210:17 | c | Argument 2 provided to type-generic macro 'nexttoward' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:211:7:211:7 | c | Argument 1 provided to type-generic macro 'pow' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:212:10:212:10 | c | Argument 2 provided to type-generic macro 'pow' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:213:13:213:13 | c | Argument 1 provided to type-generic macro 'remainder' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:214:16:214:16 | c | Argument 2 provided to type-generic macro 'remainder' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:215:10:215:10 | c | Argument 1 provided to type-generic macro 'remquo' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:216:13:216:13 | c | Argument 2 provided to type-generic macro 'remquo' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:217:8:217:8 | c | Argument 1 provided to type-generic macro 'rint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:218:9:218:9 | c | Argument 1 provided to type-generic macro 'round' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:219:10:219:10 | c | Argument 1 provided to type-generic macro 'scalbn' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:220:13:220:13 | c | Argument 2 provided to type-generic macro 'scalbn' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:221:11:221:11 | c | Argument 1 provided to type-generic macro 'scalbln' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:222:14:222:14 | c | Argument 2 provided to type-generic macro 'scalbln' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:223:7:223:7 | c | Argument 1 provided to type-generic macro 'sin' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:224:8:224:8 | c | Argument 1 provided to type-generic macro 'sinh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:225:8:225:8 | c | Argument 1 provided to type-generic macro 'sqrt' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:226:7:226:7 | c | Argument 1 provided to type-generic macro 'tan' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:227:8:227:8 | c | Argument 1 provided to type-generic macro 'tanh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:228:10:228:10 | c | Argument 1 provided to type-generic macro 'tgamma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:229:9:229:9 | c | Argument 1 provided to type-generic macro 'trunc' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:239:9:239:10 | cf | Argument 1 provided to type-generic macro 'atan2' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:240:12:240:13 | cf | Argument 2 provided to type-generic macro 'atan2' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:243:8:243:9 | cf | Argument 1 provided to type-generic macro 'cbrt' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:244:8:244:9 | cf | Argument 1 provided to type-generic macro 'ceil' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:247:15:247:16 | cf | Argument 2 provided to type-generic macro 'copysign' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:248:12:248:13 | cf | Argument 1 provided to type-generic macro 'copysign' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:249:15:249:16 | cf | Argument 2 provided to type-generic macro 'copysign' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:254:7:254:8 | cf | Argument 1 provided to type-generic macro 'erf' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:255:8:255:9 | cf | Argument 1 provided to type-generic macro 'erfc' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:257:8:257:9 | cf | Argument 1 provided to type-generic macro 'exp2' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:258:9:258:10 | cf | Argument 1 provided to type-generic macro 'expm1' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:260:8:260:9 | cf | Argument 1 provided to type-generic macro 'fdim' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:261:11:261:12 | cf | Argument 2 provided to type-generic macro 'fdim' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:262:9:262:10 | cf | Argument 1 provided to type-generic macro 'floor' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:263:7:263:8 | cf | Argument 1 provided to type-generic macro 'fma' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:264:10:264:11 | cf | Argument 2 provided to type-generic macro 'fma' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:265:13:265:14 | cf | Argument 3 provided to type-generic macro 'fma' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:266:8:266:9 | cf | Argument 1 provided to type-generic macro 'fmax' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:267:11:267:12 | cf | Argument 2 provided to type-generic macro 'fmax' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:268:8:268:9 | cf | Argument 1 provided to type-generic macro 'fmin' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:269:11:269:12 | cf | Argument 2 provided to type-generic macro 'fmin' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:270:8:270:9 | cf | Argument 1 provided to type-generic macro 'fmod' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:271:11:271:12 | cf | Argument 2 provided to type-generic macro 'fmod' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:272:9:272:10 | cf | Argument 1 provided to type-generic macro 'frexp' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:273:9:273:10 | cf | Argument 1 provided to type-generic macro 'hypot' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:274:12:274:13 | cf | Argument 2 provided to type-generic macro 'hypot' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:275:9:275:10 | cf | Argument 1 provided to type-generic macro 'ilogb' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:276:9:276:10 | cf | Argument 1 provided to type-generic macro 'ldexp' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:277:12:277:13 | cf | Argument 2 provided to type-generic macro 'ldexp' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:278:10:278:11 | cf | Argument 1 provided to type-generic macro 'lgamma' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:279:10:279:11 | cf | Argument 1 provided to type-generic macro 'llrint' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:280:11:280:12 | cf | Argument 1 provided to type-generic macro 'llround' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:282:9:282:10 | cf | Argument 1 provided to type-generic macro 'log10' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:283:9:283:10 | cf | Argument 1 provided to type-generic macro 'log1p' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:284:8:284:9 | cf | Argument 1 provided to type-generic macro 'log2' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:285:8:285:9 | cf | Argument 1 provided to type-generic macro 'logb' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:286:9:286:10 | cf | Argument 1 provided to type-generic macro 'lrint' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:287:10:287:11 | cf | Argument 1 provided to type-generic macro 'lround' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:288:13:288:14 | cf | Argument 1 provided to type-generic macro 'nearbyint' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:289:13:289:14 | cf | Argument 1 provided to type-generic macro 'nextafter' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:290:16:290:17 | cf | Argument 2 provided to type-generic macro 'nextafter' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:291:14:291:15 | cf | Argument 1 provided to type-generic macro 'nexttoward' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:292:17:292:18 | cf | Argument 2 provided to type-generic macro 'nexttoward' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:294:13:294:14 | cf | Argument 1 provided to type-generic macro 'remainder' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:295:16:295:17 | cf | Argument 2 provided to type-generic macro 'remainder' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:296:10:296:11 | cf | Argument 1 provided to type-generic macro 'remquo' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:297:13:297:14 | cf | Argument 2 provided to type-generic macro 'remquo' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:298:8:298:9 | cf | Argument 1 provided to type-generic macro 'rint' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:299:9:299:10 | cf | Argument 1 provided to type-generic macro 'round' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:300:10:300:11 | cf | Argument 1 provided to type-generic macro 'scalbn' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:301:13:301:14 | cf | Argument 2 provided to type-generic macro 'scalbn' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:302:11:302:12 | cf | Argument 1 provided to type-generic macro 'scalbln' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:303:14:303:15 | cf | Argument 2 provided to type-generic macro 'scalbln' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:309:10:309:11 | cf | Argument 1 provided to type-generic macro 'tgamma' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:310:9:310:10 | cf | Argument 1 provided to type-generic macro 'trunc' has essentially complex floating type, which is not essentially signed, unsigned, or real floating type. | +| test.c:325:13:325:13 | i | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:328:18:328:18 | c | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | diff --git a/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected.clang b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected.clang new file mode 100644 index 0000000000..313438ea6c --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected.clang @@ -0,0 +1,76 @@ +| test.c:29:7:29:7 | c | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:40:7:40:7 | e | Argument 1 provided to type-generic macro 'cos' has essentially enum type, which is not essentially signed, unsigned, or floating type. | +| test.c:41:7:41:7 | b | Argument 1 provided to type-generic macro 'cos' has essentially boolean type, which is not essentially signed, unsigned, or floating type. | +| test.c:156:8:156:8 | c | Argument 1 provided to type-generic macro 'acos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:157:9:157:9 | c | Argument 1 provided to type-generic macro 'acosh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:158:8:158:8 | c | Argument 1 provided to type-generic macro 'asin' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:159:9:159:9 | c | Argument 1 provided to type-generic macro 'asinh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:160:9:160:9 | c | Argument 1 provided to type-generic macro 'atan2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:161:12:161:12 | c | Argument 2 provided to type-generic macro 'atan2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:162:9:162:9 | c | Argument 1 provided to type-generic macro 'atanh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:163:8:163:8 | c | Argument 1 provided to type-generic macro 'carg' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:164:8:164:8 | c | Argument 1 provided to type-generic macro 'ceil' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:165:9:165:9 | c | Argument 1 provided to type-generic macro 'cimag' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:166:8:166:8 | c | Argument 1 provided to type-generic macro 'conj' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:167:15:167:15 | c | Argument 2 provided to type-generic macro 'copysign' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:168:12:168:12 | c | Argument 1 provided to type-generic macro 'copysign' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:169:8:169:8 | c | Argument 1 provided to type-generic macro 'cosh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:170:9:170:9 | c | Argument 1 provided to type-generic macro 'cproj' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:171:9:171:9 | c | Argument 1 provided to type-generic macro 'creal' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:172:7:172:7 | c | Argument 1 provided to type-generic macro 'erf' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:173:8:173:8 | c | Argument 1 provided to type-generic macro 'erfc' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:174:7:174:7 | c | Argument 1 provided to type-generic macro 'exp' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:175:8:175:8 | c | Argument 1 provided to type-generic macro 'exp2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:176:9:176:9 | c | Argument 1 provided to type-generic macro 'expm1' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:177:8:177:8 | c | Argument 1 provided to type-generic macro 'fabs' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:178:8:178:8 | c | Argument 1 provided to type-generic macro 'fdim' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:179:11:179:11 | c | Argument 2 provided to type-generic macro 'fdim' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:180:9:180:9 | c | Argument 1 provided to type-generic macro 'floor' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:181:7:181:7 | c | Argument 1 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:182:10:182:10 | c | Argument 2 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:183:13:183:13 | c | Argument 3 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:184:8:184:8 | c | Argument 1 provided to type-generic macro 'fmax' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:185:11:185:11 | c | Argument 2 provided to type-generic macro 'fmax' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:186:8:186:8 | c | Argument 1 provided to type-generic macro 'fmin' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:187:11:187:11 | c | Argument 2 provided to type-generic macro 'fmin' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:188:8:188:8 | c | Argument 1 provided to type-generic macro 'fmod' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:189:11:189:11 | c | Argument 2 provided to type-generic macro 'fmod' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:191:9:191:9 | c | Argument 1 provided to type-generic macro 'hypot' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:192:12:192:12 | c | Argument 2 provided to type-generic macro 'hypot' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:193:9:193:9 | c | Argument 1 provided to type-generic macro 'ilogb' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:194:9:194:9 | c | Argument 1 provided to type-generic macro 'ldexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:195:12:195:12 | c | Argument 2 provided to type-generic macro 'ldexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:196:10:196:10 | c | Argument 1 provided to type-generic macro 'lgamma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:197:10:197:10 | c | Argument 1 provided to type-generic macro 'llrint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:198:11:198:11 | c | Argument 1 provided to type-generic macro 'llround' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:199:7:199:7 | c | Argument 1 provided to type-generic macro 'log' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:200:9:200:9 | c | Argument 1 provided to type-generic macro 'log10' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:201:9:201:9 | c | Argument 1 provided to type-generic macro 'log1p' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:202:8:202:8 | c | Argument 1 provided to type-generic macro 'log2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:203:8:203:8 | c | Argument 1 provided to type-generic macro 'logb' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:204:9:204:9 | c | Argument 1 provided to type-generic macro 'lrint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:205:10:205:10 | c | Argument 1 provided to type-generic macro 'lround' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:206:13:206:13 | c | Argument 1 provided to type-generic macro 'nearbyint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:207:13:207:13 | c | Argument 1 provided to type-generic macro 'nextafter' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:208:16:208:16 | c | Argument 2 provided to type-generic macro 'nextafter' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:209:14:209:14 | c | Argument 1 provided to type-generic macro 'nexttoward' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:210:17:210:17 | c | Argument 2 provided to type-generic macro 'nexttoward' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:211:7:211:7 | c | Argument 1 provided to type-generic macro 'pow' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:212:10:212:10 | c | Argument 2 provided to type-generic macro 'pow' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:213:13:213:13 | c | Argument 1 provided to type-generic macro 'remainder' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:214:16:214:16 | c | Argument 2 provided to type-generic macro 'remainder' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:217:8:217:8 | c | Argument 1 provided to type-generic macro 'rint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:218:9:218:9 | c | Argument 1 provided to type-generic macro 'round' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:219:10:219:10 | c | Argument 1 provided to type-generic macro 'scalbn' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:220:13:220:13 | c | Argument 2 provided to type-generic macro 'scalbn' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:221:11:221:11 | c | Argument 1 provided to type-generic macro 'scalbln' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:222:14:222:14 | c | Argument 2 provided to type-generic macro 'scalbln' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:223:7:223:7 | c | Argument 1 provided to type-generic macro 'sin' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:224:8:224:8 | c | Argument 1 provided to type-generic macro 'sinh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:225:8:225:8 | c | Argument 1 provided to type-generic macro 'sqrt' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:226:7:226:7 | c | Argument 1 provided to type-generic macro 'tan' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:227:8:227:8 | c | Argument 1 provided to type-generic macro 'tanh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:228:10:228:10 | c | Argument 1 provided to type-generic macro 'tgamma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:229:9:229:9 | c | Argument 1 provided to type-generic macro 'trunc' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:325:14:325:14 | i | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:328:20:328:20 | c | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | diff --git a/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected.gcc b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected.gcc new file mode 100644 index 0000000000..79b070ae84 --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.expected.gcc @@ -0,0 +1,79 @@ +| test.c:29:7:29:7 | c | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:40:7:40:7 | e | Argument 1 provided to type-generic macro 'cos' has essentially enum type, which is not essentially signed, unsigned, or floating type. | +| test.c:41:7:41:7 | b | Argument 1 provided to type-generic macro 'cos' has essentially boolean type, which is not essentially signed, unsigned, or floating type. | +| test.c:156:8:156:8 | c | Argument 1 provided to type-generic macro 'acos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:157:9:157:9 | c | Argument 1 provided to type-generic macro 'acosh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:158:8:158:8 | c | Argument 1 provided to type-generic macro 'asin' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:159:9:159:9 | c | Argument 1 provided to type-generic macro 'asinh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:160:9:160:9 | c | Argument 1 provided to type-generic macro 'atan2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:161:12:161:12 | c | Argument 2 provided to type-generic macro 'atan2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:162:9:162:9 | c | Argument 1 provided to type-generic macro 'atanh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:163:8:163:8 | c | Argument 1 provided to type-generic macro 'carg' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:164:8:164:8 | c | Argument 1 provided to type-generic macro 'ceil' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:165:9:165:9 | c | Argument 1 provided to type-generic macro 'cimag' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:166:8:166:8 | c | Argument 1 provided to type-generic macro 'conj' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:167:15:167:15 | c | Argument 2 provided to type-generic macro 'copysign' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:168:12:168:12 | c | Argument 1 provided to type-generic macro 'copysign' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:169:8:169:8 | c | Argument 1 provided to type-generic macro 'cosh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:170:9:170:9 | c | Argument 1 provided to type-generic macro 'cproj' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:171:9:171:9 | c | Argument 1 provided to type-generic macro 'creal' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:172:7:172:7 | c | Argument 1 provided to type-generic macro 'erf' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:173:8:173:8 | c | Argument 1 provided to type-generic macro 'erfc' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:174:7:174:7 | c | Argument 1 provided to type-generic macro 'exp' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:175:8:175:8 | c | Argument 1 provided to type-generic macro 'exp2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:176:9:176:9 | c | Argument 1 provided to type-generic macro 'expm1' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:177:8:177:8 | c | Argument 1 provided to type-generic macro 'fabs' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:178:8:178:8 | c | Argument 1 provided to type-generic macro 'fdim' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:179:11:179:11 | c | Argument 2 provided to type-generic macro 'fdim' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:180:9:180:9 | c | Argument 1 provided to type-generic macro 'floor' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:181:7:181:7 | c | Argument 1 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:182:10:182:10 | c | Argument 2 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:183:13:183:13 | c | Argument 3 provided to type-generic macro 'fma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:184:8:184:8 | c | Argument 1 provided to type-generic macro 'fmax' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:185:11:185:11 | c | Argument 2 provided to type-generic macro 'fmax' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:186:8:186:8 | c | Argument 1 provided to type-generic macro 'fmin' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:187:11:187:11 | c | Argument 2 provided to type-generic macro 'fmin' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:188:8:188:8 | c | Argument 1 provided to type-generic macro 'fmod' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:189:11:189:11 | c | Argument 2 provided to type-generic macro 'fmod' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:190:9:190:9 | c | Argument 1 provided to type-generic macro 'frexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:191:9:191:9 | c | Argument 1 provided to type-generic macro 'hypot' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:192:12:192:12 | c | Argument 2 provided to type-generic macro 'hypot' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:193:9:193:9 | c | Argument 1 provided to type-generic macro 'ilogb' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:194:9:194:9 | c | Argument 1 provided to type-generic macro 'ldexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:195:12:195:12 | c | Argument 2 provided to type-generic macro 'ldexp' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:196:10:196:10 | c | Argument 1 provided to type-generic macro 'lgamma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:197:10:197:10 | c | Argument 1 provided to type-generic macro 'llrint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:198:11:198:11 | c | Argument 1 provided to type-generic macro 'llround' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:199:7:199:7 | c | Argument 1 provided to type-generic macro 'log' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:200:9:200:9 | c | Argument 1 provided to type-generic macro 'log10' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:201:9:201:9 | c | Argument 1 provided to type-generic macro 'log1p' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:202:8:202:8 | c | Argument 1 provided to type-generic macro 'log2' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:203:8:203:8 | c | Argument 1 provided to type-generic macro 'logb' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:204:9:204:9 | c | Argument 1 provided to type-generic macro 'lrint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:205:10:205:10 | c | Argument 1 provided to type-generic macro 'lround' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:206:13:206:13 | c | Argument 1 provided to type-generic macro 'nearbyint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:207:13:207:13 | c | Argument 1 provided to type-generic macro 'nextafter' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:208:16:208:16 | c | Argument 2 provided to type-generic macro 'nextafter' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:209:14:209:14 | c | Argument 1 provided to type-generic macro 'nexttoward' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:210:17:210:17 | c | Argument 2 provided to type-generic macro 'nexttoward' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:211:7:211:7 | c | Argument 1 provided to type-generic macro 'pow' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:212:10:212:10 | c | Argument 2 provided to type-generic macro 'pow' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:213:13:213:13 | c | Argument 1 provided to type-generic macro 'remainder' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:214:16:214:16 | c | Argument 2 provided to type-generic macro 'remainder' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:215:10:215:10 | c | Argument 1 provided to type-generic macro 'remquo' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:216:13:216:13 | c | Argument 2 provided to type-generic macro 'remquo' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:217:8:217:8 | c | Argument 1 provided to type-generic macro 'rint' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:218:9:218:9 | c | Argument 1 provided to type-generic macro 'round' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:219:10:219:10 | c | Argument 1 provided to type-generic macro 'scalbn' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:220:13:220:13 | c | Argument 2 provided to type-generic macro 'scalbn' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:221:11:221:11 | c | Argument 1 provided to type-generic macro 'scalbln' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:222:14:222:14 | c | Argument 2 provided to type-generic macro 'scalbln' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:223:7:223:7 | c | Argument 1 provided to type-generic macro 'sin' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:224:8:224:8 | c | Argument 1 provided to type-generic macro 'sinh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:225:8:225:8 | c | Argument 1 provided to type-generic macro 'sqrt' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:226:7:226:7 | c | Argument 1 provided to type-generic macro 'tan' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:227:8:227:8 | c | Argument 1 provided to type-generic macro 'tanh' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:228:10:228:10 | c | Argument 1 provided to type-generic macro 'tgamma' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:229:9:229:9 | c | Argument 1 provided to type-generic macro 'trunc' has essentially character type, which is not essentially signed, unsigned, or real floating type. | +| test.c:325:14:325:14 | i | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | +| test.c:328:20:328:20 | c | Argument 1 provided to type-generic macro 'cos' has essentially character type, which is not essentially signed, unsigned, or floating type. | diff --git a/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.qlref b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.qlref new file mode 100644 index 0000000000..cb7206db11 --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.qlref @@ -0,0 +1 @@ +rules/RULE-21-22/TgMathArgumentWithInvalidEssentialType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-22/test.c b/c/misra/test/rules/RULE-21-22/test.c new file mode 100644 index 0000000000..970df4fd56 --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/test.c @@ -0,0 +1,329 @@ +#include +#include +#include +#include + +void f1() { + int i = 0; + unsigned int ui = 0; + short s = 0; + unsigned short us = 0; + char c = 0; + unsigned char uc = 0; + signed char sc = 0; + long l = 0; + unsigned long ul = 0; + float f = 0.0f; + float _Complex cf = 0.0f + 0.0f * I; + double d = 0.0; + char *p = 0; + void *vp = 0; + uintptr_t uip = p; + enum { e1 } e = e1; + bool b = true; + + cos(i); // COMPLIANT + cos(ui); // COMPLIANT + cos(s); // COMPLIANT + cos(us); // COMPLIANT + cos(c); // NON-COMPLIANT + cos(uc); // COMPLIANT + cos(sc); // COMPLIANT + cos(l); // COMPLIANT + cos(ul); // COMPLIANT + cos(f); // COMPLIANT + cos(cf); // COMPLIANT + cos(d); // COMPLIANT + // cos(p); // Doesn't compile + // cos(vp); // Doesn't compile + cos(uip); // COMPLIANT + cos(e); // NON-COMPLIANT + cos(b); // NON-COMPLIANT + cos(1); // COMPLIANT + cos(1.1f); // COMPLIANT + cos('a'); // NON-COMPLIANT[false negative] + + /** + * Int, float, and complex allowed: + */ + acos(i); // COMPLIANT + acos(f); // COMPLIANT + acosh(i); // COMPLIANT + acosh(f); // COMPLIANT + asin(i); // COMPLIANT + asin(f); // COMPLIANT + asinh(i); // COMPLIANT + asinh(f); // COMPLIANT + atan(i); // COMPLIANT + atan(f); // COMPLIANT + atan2(i, i); // COMPLIANT + atan2(f, f); // COMPLIANT + atanh(i); // COMPLIANT + atanh(f); // COMPLIANT + carg(i); // COMPLIANT + carg(f); // COMPLIANT + cbrt(i); // COMPLIANT + cbrt(f); // COMPLIANT + ceil(i); // COMPLIANT + ceil(f); // COMPLIANT + cimag(i); // COMPLIANT + cimag(f); // COMPLIANT + conj(i); // COMPLIANT + conj(f); // COMPLIANT + copysign(i, i); // COMPLIANT + copysign(f, f); // COMPLIANT + cos(i); // COMPLIANT + cos(f); // COMPLIANT + cosh(i); // COMPLIANT + cosh(f); // COMPLIANT + cproj(i); // COMPLIANT + cproj(f); // COMPLIANT + creal(i); // COMPLIANT + creal(f); // COMPLIANT + erf(i); // COMPLIANT + erf(f); // COMPLIANT + erfc(i); // COMPLIANT + erfc(f); // COMPLIANT + exp(i); // COMPLIANT + exp(f); // COMPLIANT + exp2(i); // COMPLIANT + exp2(f); // COMPLIANT + expm1(i); // COMPLIANT + expm1(f); // COMPLIANT + fabs(i); // COMPLIANT + fabs(f); // COMPLIANT + fdim(i, i); // COMPLIANT + fdim(f, f); // COMPLIANT + floor(i); // COMPLIANT + floor(f); // COMPLIANT + fma(i, i, i); // COMPLIANT + fma(f, f, f); // COMPLIANT + fmax(i, i); // COMPLIANT + fmax(f, f); // COMPLIANT + fmin(i, i); // COMPLIANT + fmin(f, f); // COMPLIANT + fmod(i, i); // COMPLIANT + fmod(f, f); // COMPLIANT + frexp(i, &i); // COMPLIANT + frexp(f, &p); // COMPLIANT + hypot(i, i); // COMPLIANT + hypot(f, f); // COMPLIANT + ilogb(i); // COMPLIANT + ilogb(f); // COMPLIANT + llrint(i); // COMPLIANT + llrint(f); // COMPLIANT + ldexp(i, i); // COMPLIANT + ldexp(f, f); // COMPLIANT + lgamma(i); // COMPLIANT + lgamma(f); // COMPLIANT + llround(i); // COMPLIANT + llround(f); // COMPLIANT + log(i); // COMPLIANT + log(f); // COMPLIANT + pow(i, i); // COMPLIANT + pow(f, f); // COMPLIANT + remainder(i, i); // COMPLIANT + remainder(f, f); // COMPLIANT + remquo(i, i, &i); // COMPLIANT + remquo(f, f, &f); // COMPLIANT + rint(i); // COMPLIANT + rint(f); // COMPLIANT + round(i); // COMPLIANT + round(f); // COMPLIANT + scalbn(i, i); // COMPLIANT + scalbn(f, f); // COMPLIANT + scalbln(i, i); // COMPLIANT + scalbln(f, f); // COMPLIANT + sin(i); // COMPLIANT + sin(f); // COMPLIANT + sin(cf); // COMPLIANT + sinh(i); // COMPLIANT + sinh(f); // COMPLIANT + sqrt(i); // COMPLIANT + sqrt(f); // COMPLIANT + tan(i); // COMPLIANT + tan(f); // COMPLIANT + tanh(i); // COMPLIANT + tanh(f); // COMPLIANT + tgamma(i); // COMPLIANT + tgamma(f); // COMPLIANT + trunc(i); // COMPLIANT + trunc(f); // COMPLIANT + + /** + * Char not allowed: + */ + acos(c); // NON-COMPLIANT + acosh(c); // NON-COMPLIANT + asin(c); // NON-COMPLIANT + asinh(c); // NON-COMPLIANT + atan2(c, i); // NON-COMPLIANT + atan2(i, c); // NON-COMPLIANT + atanh(c); // NON-COMPLIANT + carg(c); // NON-COMPLIANT + ceil(c); // NON-COMPLIANT + cimag(c); // NON-COMPLIANT + conj(c); // NON-COMPLIANT + copysign(i, c); // NON-COMPLIANT + copysign(c, i); // NON-COMPLIANT + cosh(c); // NON-COMPLIANT + cproj(c); // NON-COMPLIANT + creal(c); // NON-COMPLIANT + erf(c); // NON-COMPLIANT + erfc(c); // NON-COMPLIANT + exp(c); // NON-COMPLIANT + exp2(c); // NON-COMPLIANT + expm1(c); // NON-COMPLIANT + fabs(c); // NON-COMPLIANT + fdim(c, i); // NON-COMPLIANT + fdim(i, c); // NON-COMPLIANT + floor(c); // NON-COMPLIANT + fma(c, i, i); // NON-COMPLIANT + fma(i, c, i); // NON-COMPLIANT + fma(i, i, c); // NON-COMPLIANT + fmax(c, i); // NON-COMPLIANT + fmax(i, c); // NON-COMPLIANT + fmin(c, i); // NON-COMPLIANT + fmin(i, c); // NON-COMPLIANT + fmod(c, i); // NON-COMPLIANT + fmod(i, c); // NON-COMPLIANT + frexp(c, i); // NON-COMPLIANT + hypot(c, i); // NON-COMPLIANT + hypot(i, c); // NON-COMPLIANT + ilogb(c); // NON-COMPLIANT + ldexp(c, i); // NON-COMPLIANT + ldexp(i, c); // NON-COMPLIANT + lgamma(c); // NON-COMPLIANT + llrint(c); // NON-COMPLIANT + llround(c); // NON-COMPLIANT + log(c); // NON-COMPLIANT + log10(c); // NON-COMPLIANT + log1p(c); // NON-COMPLIANT + log2(c); // NON-COMPLIANT + logb(c); // NON-COMPLIANT + lrint(c); // NON-COMPLIANT + lround(c); // NON-COMPLIANT + nearbyint(c); // NON-COMPLIANT + nextafter(c, i); // NON-COMPLIANT + nextafter(i, c); // NON-COMPLIANT + nexttoward(c, i); // NON-COMPLIANT + nexttoward(i, c); // NON-COMPLIANT + pow(c, i); // NON-COMPLIANT + pow(i, c); // NON-COMPLIANT + remainder(c, i); // NON-COMPLIANT + remainder(i, c); // NON-COMPLIANT + remquo(c, i, i); // NON-COMPLIANT + remquo(i, c, i); // NON-COMPLIANT + rint(c); // NON-COMPLIANT + round(c); // NON-COMPLIANT + scalbn(c, i); // NON-COMPLIANT + scalbn(i, c); // NON-COMPLIANT + scalbln(c, i); // NON-COMPLIANT + scalbln(i, c); // NON-COMPLIANT + sin(c); // NON-COMPLIANT + sinh(c); // NON-COMPLIANT + sqrt(c); // NON-COMPLIANT + tan(c); // NON-COMPLIANT + tanh(c); // NON-COMPLIANT + tgamma(c); // NON-COMPLIANT + trunc(c); // NON-COMPLIANT + + /** + * Complex types allowed in some calls, not others: + */ + acos(cf); // COMPLIANT + acosh(cf); // COMPLIANT + asin(cf); // COMPLIANT + asinh(cf); // COMPLIANT + atan(cf); // COMPLIANT + atan2(cf, i); // NON-COMPLIANT + atan2(i, cf); // NON-COMPLIANT + atanh(cf); // COMPLIANT + carg(cf); // COMPLIANT + cbrt(cf); // NON-COMPLIANT + ceil(cf); // NON-COMPLIANT + cimag(cf); // COMPLIANT + conj(cf); // COMPLIANT + copysign(i, cf); // NON-COMPLIANT + copysign(cf, i); // NON-COMPLIANT + copysign(i, cf); // NON-COMPLIANT + cos(cf); // COMPLIANT + cosh(cf); // COMPLIANT + cproj(cf); // COMPLIANT + creal(cf); // COMPLIANT + erf(cf); // NON-COMPLIANT + erfc(cf); // NON-COMPLIANT + exp(cf); // COMPLIANT + exp2(cf); // NON-COMPLIANT + expm1(cf); // NON-COMPLIANT + fabs(cf); // COMPLIANT + fdim(cf, i); // NON-COMPLIANT + fdim(i, cf); // NON-COMPLIANT + floor(cf); // NON-COMPLIANT + fma(cf, i, i); // NON-COMPLIANT + fma(i, cf, i); // NON-COMPLIANT + fma(i, i, cf); // NON-COMPLIANT + fmax(cf, i); // NON-COMPLIANT + fmax(i, cf); // NON-COMPLIANT + fmin(cf, i); // NON-COMPLIANT + fmin(i, cf); // NON-COMPLIANT + fmod(cf, i); // NON-COMPLIANT + fmod(i, cf); // NON-COMPLIANT + frexp(cf, i); // NON-COMPLIANT + hypot(cf, i); // NON-COMPLIANT + hypot(i, cf); // NON-COMPLIANT + ilogb(cf); // NON-COMPLIANT + ldexp(cf, i); // NON-COMPLIANT + ldexp(i, cf); // NON-COMPLIANT + lgamma(cf); // NON-COMPLIANT + llrint(cf); // NON-COMPLIANT + llround(cf); // NON-COMPLIANT + log(cf); // COMPLIANT + log10(cf); // NON-COMPLIANT + log1p(cf); // NON-COMPLIANT + log2(cf); // NON-COMPLIANT + logb(cf); // NON-COMPLIANT + lrint(cf); // NON-COMPLIANT + lround(cf); // NON-COMPLIANT + nearbyint(cf); // NON-COMPLIANT + nextafter(cf, i); // NON-COMPLIANT + nextafter(i, cf); // NON-COMPLIANT + nexttoward(cf, i); // NON-COMPLIANT + nexttoward(i, cf); // NON-COMPLIANT + pow(cf, cf); // COMPLIANT + remainder(cf, i); // NON-COMPLIANT + remainder(i, cf); // NON-COMPLIANT + remquo(cf, i, i); // NON-COMPLIANT + remquo(i, cf, i); // NON-COMPLIANT + rint(cf); // NON-COMPLIANT + round(cf); // NON-COMPLIANT + scalbn(cf, i); // NON-COMPLIANT + scalbn(i, cf); // NON-COMPLIANT + scalbln(cf, i); // NON-COMPLIANT + scalbln(i, cf); // NON-COMPLIANT + sin(cf); // COMPLIANT + sinh(cf); // COMPLIANT + sqrt(cf); // COMPLIANT + tan(cf); // COMPLIANT + tanh(cf); // COMPLIANT + tgamma(cf); // NON-COMPLIANT + trunc(cf); // NON-COMPLIANT + + /* Test output arguments thoroughly */ + frexp(i, &i); // COMPLIANT + frexp(i, vp); // COMPLIANT + frexp(i, 0); // COMPLIANT + frexp(i, 'c' - 'c'); // COMPLIANT + frexp(i, c); // COMPLIANT + remquo(i, i, &i); // COMPLIANT + remquo(i, i, vp); // COMPLIANT + remquo(i, i, 0); // COMPLIANT + remquo(i, i, 'c' - 'c'); // COMPLIANT + remquo(i, i, c); // COMPLIANT + + /* Test casts */ + cos((char)i); // NON-COMPLIANT + cos((int)c); // COMPLIANT + cos((int)(char)i); // COMPLIANT + cos((char)(int)c); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-22/test.c.clang b/c/misra/test/rules/RULE-21-22/test.c.clang new file mode 100644 index 0000000000..d28576e058 --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/test.c.clang @@ -0,0 +1,329 @@ +#include +#include +#include +#include + +void f1() { + int i = 0; + unsigned int ui = 0; + short s = 0; + unsigned short us = 0; + char c = 0; + unsigned char uc = 0; + signed char sc = 0; + long l = 0; + unsigned long ul = 0; + float f = 0.0f; + float _Complex cf = 0.0f + 0.0f * I; + double d = 0.0; + char *p = 0; + void *vp = 0; + uintptr_t uip = p; + enum { e1 } e = e1; + bool b = true; + + cos(i); // COMPLIANT + cos(ui); // COMPLIANT + cos(s); // COMPLIANT + cos(us); // COMPLIANT + cos(c); // NON-COMPLIANT + cos(uc); // COMPLIANT + cos(sc); // COMPLIANT + cos(l); // COMPLIANT + cos(ul); // COMPLIANT + cos(f); // COMPLIANT + cos(cf); // COMPLIANT + cos(d); // COMPLIANT + // cos(p); // Doesn't compile + // cos(vp); // Doesn't compile + cos(uip); // COMPLIANT + cos(e); // NON-COMPLIANT + cos(b); // NON-COMPLIANT + cos(1); // COMPLIANT + cos(1.1f); // COMPLIANT + cos('a'); // NON-COMPLIANT[false negative] + + /** + * Int, float, and complex allowed: + */ + acos(i); // COMPLIANT + acos(f); // COMPLIANT + acosh(i); // COMPLIANT + acosh(f); // COMPLIANT + asin(i); // COMPLIANT + asin(f); // COMPLIANT + asinh(i); // COMPLIANT + asinh(f); // COMPLIANT + atan(i); // COMPLIANT + atan(f); // COMPLIANT + atan2(i, i); // COMPLIANT + atan2(f, f); // COMPLIANT + atanh(i); // COMPLIANT + atanh(f); // COMPLIANT + carg(i); // COMPLIANT + carg(f); // COMPLIANT + cbrt(i); // COMPLIANT + cbrt(f); // COMPLIANT + ceil(i); // COMPLIANT + ceil(f); // COMPLIANT + cimag(i); // COMPLIANT + cimag(f); // COMPLIANT + conj(i); // COMPLIANT + conj(f); // COMPLIANT + copysign(i, i); // COMPLIANT + copysign(f, f); // COMPLIANT + cos(i); // COMPLIANT + cos(f); // COMPLIANT + cosh(i); // COMPLIANT + cosh(f); // COMPLIANT + cproj(i); // COMPLIANT + cproj(f); // COMPLIANT + creal(i); // COMPLIANT + creal(f); // COMPLIANT + erf(i); // COMPLIANT + erf(f); // COMPLIANT + erfc(i); // COMPLIANT + erfc(f); // COMPLIANT + exp(i); // COMPLIANT + exp(f); // COMPLIANT + exp2(i); // COMPLIANT + exp2(f); // COMPLIANT + expm1(i); // COMPLIANT + expm1(f); // COMPLIANT + fabs(i); // COMPLIANT + fabs(f); // COMPLIANT + fdim(i, i); // COMPLIANT + fdim(f, f); // COMPLIANT + floor(i); // COMPLIANT + floor(f); // COMPLIANT + fma(i, i, i); // COMPLIANT + fma(f, f, f); // COMPLIANT + fmax(i, i); // COMPLIANT + fmax(f, f); // COMPLIANT + fmin(i, i); // COMPLIANT + fmin(f, f); // COMPLIANT + fmod(i, i); // COMPLIANT + fmod(f, f); // COMPLIANT + frexp(i, &i); // COMPLIANT + frexp(f, &p); // COMPLIANT + hypot(i, i); // COMPLIANT + hypot(f, f); // COMPLIANT + ilogb(i); // COMPLIANT + ilogb(f); // COMPLIANT + llrint(i); // COMPLIANT + llrint(f); // COMPLIANT + ldexp(i, i); // COMPLIANT + ldexp(f, f); // COMPLIANT + lgamma(i); // COMPLIANT + lgamma(f); // COMPLIANT + llround(i); // COMPLIANT + llround(f); // COMPLIANT + log(i); // COMPLIANT + log(f); // COMPLIANT + pow(i, i); // COMPLIANT + pow(f, f); // COMPLIANT + remainder(i, i); // COMPLIANT + remainder(f, f); // COMPLIANT + remquo(i, i, &i); // COMPLIANT + remquo(f, f, &f); // COMPLIANT + rint(i); // COMPLIANT + rint(f); // COMPLIANT + round(i); // COMPLIANT + round(f); // COMPLIANT + scalbn(i, i); // COMPLIANT + scalbn(f, f); // COMPLIANT + scalbln(i, i); // COMPLIANT + scalbln(f, f); // COMPLIANT + sin(i); // COMPLIANT + sin(f); // COMPLIANT + sin(cf); // COMPLIANT + sinh(i); // COMPLIANT + sinh(f); // COMPLIANT + sqrt(i); // COMPLIANT + sqrt(f); // COMPLIANT + tan(i); // COMPLIANT + tan(f); // COMPLIANT + tanh(i); // COMPLIANT + tanh(f); // COMPLIANT + tgamma(i); // COMPLIANT + tgamma(f); // COMPLIANT + trunc(i); // COMPLIANT + trunc(f); // COMPLIANT + + /** + * Char not allowed: + */ + acos(c); // NON-COMPLIANT + acosh(c); // NON-COMPLIANT + asin(c); // NON-COMPLIANT + asinh(c); // NON-COMPLIANT + atan2(c, i); // NON-COMPLIANT + atan2(i, c); // NON-COMPLIANT + atanh(c); // NON-COMPLIANT + carg(c); // NON-COMPLIANT + ceil(c); // NON-COMPLIANT + cimag(c); // NON-COMPLIANT + conj(c); // NON-COMPLIANT + copysign(i, c); // NON-COMPLIANT + copysign(c, i); // NON-COMPLIANT + cosh(c); // NON-COMPLIANT + cproj(c); // NON-COMPLIANT + creal(c); // NON-COMPLIANT + erf(c); // NON-COMPLIANT + erfc(c); // NON-COMPLIANT + exp(c); // NON-COMPLIANT + exp2(c); // NON-COMPLIANT + expm1(c); // NON-COMPLIANT + fabs(c); // NON-COMPLIANT + fdim(c, i); // NON-COMPLIANT + fdim(i, c); // NON-COMPLIANT + floor(c); // NON-COMPLIANT + fma(c, i, i); // NON-COMPLIANT + fma(i, c, i); // NON-COMPLIANT + fma(i, i, c); // NON-COMPLIANT + fmax(c, i); // NON-COMPLIANT + fmax(i, c); // NON-COMPLIANT + fmin(c, i); // NON-COMPLIANT + fmin(i, c); // NON-COMPLIANT + fmod(c, i); // NON-COMPLIANT + fmod(i, c); // NON-COMPLIANT + //frexp(c, i); // NON-COMPLIANT + hypot(c, i); // NON-COMPLIANT + hypot(i, c); // NON-COMPLIANT + ilogb(c); // NON-COMPLIANT + ldexp(c, i); // NON-COMPLIANT + ldexp(i, c); // NON-COMPLIANT + lgamma(c); // NON-COMPLIANT + llrint(c); // NON-COMPLIANT + llround(c); // NON-COMPLIANT + log(c); // NON-COMPLIANT + log10(c); // NON-COMPLIANT + log1p(c); // NON-COMPLIANT + log2(c); // NON-COMPLIANT + logb(c); // NON-COMPLIANT + lrint(c); // NON-COMPLIANT + lround(c); // NON-COMPLIANT + nearbyint(c); // NON-COMPLIANT + nextafter(c, i); // NON-COMPLIANT + nextafter(i, c); // NON-COMPLIANT + nexttoward(c, i); // NON-COMPLIANT + nexttoward(i, c); // NON-COMPLIANT + pow(c, i); // NON-COMPLIANT + pow(i, c); // NON-COMPLIANT + remainder(c, i); // NON-COMPLIANT + remainder(i, c); // NON-COMPLIANT + //remquo(c, i, i); // NON-COMPLIANT + //remquo(i, c, i); // NON-COMPLIANT + rint(c); // NON-COMPLIANT + round(c); // NON-COMPLIANT + scalbn(c, i); // NON-COMPLIANT + scalbn(i, c); // NON-COMPLIANT + scalbln(c, i); // NON-COMPLIANT + scalbln(i, c); // NON-COMPLIANT + sin(c); // NON-COMPLIANT + sinh(c); // NON-COMPLIANT + sqrt(c); // NON-COMPLIANT + tan(c); // NON-COMPLIANT + tanh(c); // NON-COMPLIANT + tgamma(c); // NON-COMPLIANT + trunc(c); // NON-COMPLIANT + + /** + * Complex types allowed in some calls, not others: + */ + acos(cf); // COMPLIANT + acosh(cf); // COMPLIANT + asin(cf); // COMPLIANT + asinh(cf); // COMPLIANT + atan(cf); // COMPLIANT + //atan2(cf, i); // NON-COMPLIANT + //atan2(i, cf); // NON-COMPLIANT + atanh(cf); // COMPLIANT + carg(cf); // COMPLIANT + //cbrt(cf); // NON-COMPLIANT + //ceil(cf); // NON-COMPLIANT + cimag(cf); // COMPLIANT + conj(cf); // COMPLIANT + //copysign(i, cf); // NON-COMPLIANT + //copysign(cf, i); // NON-COMPLIANT + //copysign(i, cf); // NON-COMPLIANT + cos(cf); // COMPLIANT + cosh(cf); // COMPLIANT + cproj(cf); // COMPLIANT + creal(cf); // COMPLIANT + //erf(cf); // NON-COMPLIANT + //erfc(cf); // NON-COMPLIANT + exp(cf); // COMPLIANT + //exp2(cf); // NON-COMPLIANT + //expm1(cf); // NON-COMPLIANT + fabs(cf); // COMPLIANT + //fdim(cf, i); // NON-COMPLIANT + //fdim(i, cf); // NON-COMPLIANT + //floor(cf); // NON-COMPLIANT + //fma(cf, i, i); // NON-COMPLIANT + //fma(i, cf, i); // NON-COMPLIANT + //fma(i, i, cf); // NON-COMPLIANT + //fmax(cf, i); // NON-COMPLIANT + //fmax(i, cf); // NON-COMPLIANT + //fmin(cf, i); // NON-COMPLIANT + //fmin(i, cf); // NON-COMPLIANT + //fmod(cf, i); // NON-COMPLIANT + //fmod(i, cf); // NON-COMPLIANT + //frexp(cf, i); // NON-COMPLIANT + //hypot(cf, i); // NON-COMPLIANT + //hypot(i, cf); // NON-COMPLIANT + //ilogb(cf); // NON-COMPLIANT + //ldexp(cf, i); // NON-COMPLIANT + //ldexp(i, cf); // NON-COMPLIANT + //lgamma(cf); // NON-COMPLIANT + //llrint(cf); // NON-COMPLIANT + //llround(cf); // NON-COMPLIANT + log(cf); // COMPLIANT + //log10(cf); // NON-COMPLIANT + //log1p(cf); // NON-COMPLIANT + //log2(cf); // NON-COMPLIANT + //logb(cf); // NON-COMPLIANT + //lrint(cf); // NON-COMPLIANT + //lround(cf); // NON-COMPLIANT + //nearbyint(cf); // NON-COMPLIANT + //nextafter(cf, i); // NON-COMPLIANT + //nextafter(i, cf); // NON-COMPLIANT + //nexttoward(cf, i); // NON-COMPLIANT + //nexttoward(i, cf); // NON-COMPLIANT + pow(cf, cf); // COMPLIANT + //remainder(cf, i); // NON-COMPLIANT + //remainder(i, cf); // NON-COMPLIANT + //remquo(cf, i, i); // NON-COMPLIANT + //remquo(i, cf, i); // NON-COMPLIANT + //rint(cf); // NON-COMPLIANT + //round(cf); // NON-COMPLIANT + //scalbn(cf, i); // NON-COMPLIANT + //scalbn(i, cf); // NON-COMPLIANT + //scalbln(cf, i); // NON-COMPLIANT + //scalbln(i, cf); // NON-COMPLIANT + sin(cf); // COMPLIANT + sinh(cf); // COMPLIANT + sqrt(cf); // COMPLIANT + tan(cf); // COMPLIANT + tanh(cf); // COMPLIANT + //tgamma(cf); // NON-COMPLIANT + //trunc(cf); // NON-COMPLIANT + + /* Test output arguments thoroughly */ + frexp(i, &i); // COMPLIANT + frexp(i, vp); // COMPLIANT + frexp(i, 0); // COMPLIANT + frexp(i, 'c' - 'c'); // COMPLIANT + //frexp(i, c); // COMPLIANT + remquo(i, i, &i); // COMPLIANT + remquo(i, i, vp); // COMPLIANT + remquo(i, i, 0); // COMPLIANT + remquo(i, i, 'c' - 'c'); // COMPLIANT + //remquo(i, i, c); // COMPLIANT + + /* Test casts */ + cos((char) i); // NON-COMPLIANT + cos((int) c); // COMPLIANT + cos((int) (char) i); // COMPLIANT + cos((char) (int) c); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-22/test.c.gcc b/c/misra/test/rules/RULE-21-22/test.c.gcc new file mode 100644 index 0000000000..4661a0b4f7 --- /dev/null +++ b/c/misra/test/rules/RULE-21-22/test.c.gcc @@ -0,0 +1,329 @@ +#include +#include +#include +#include + +void f1() { + int i = 0; + unsigned int ui = 0; + short s = 0; + unsigned short us = 0; + char c = 0; + unsigned char uc = 0; + signed char sc = 0; + long l = 0; + unsigned long ul = 0; + float f = 0.0f; + float _Complex cf = 0.0f + 0.0f * I; + double d = 0.0; + char *p = 0; + void *vp = 0; + uintptr_t uip = p; + enum { e1 } e = e1; + bool b = true; + + cos(i); // COMPLIANT + cos(ui); // COMPLIANT + cos(s); // COMPLIANT + cos(us); // COMPLIANT + cos(c); // NON-COMPLIANT + cos(uc); // COMPLIANT + cos(sc); // COMPLIANT + cos(l); // COMPLIANT + cos(ul); // COMPLIANT + cos(f); // COMPLIANT + cos(cf); // COMPLIANT + cos(d); // COMPLIANT + // cos(p); // Doesn't compile + // cos(vp); // Doesn't compile + cos(uip); // COMPLIANT + cos(e); // NON-COMPLIANT + cos(b); // NON-COMPLIANT + cos(1); // COMPLIANT + cos(1.1f); // COMPLIANT + cos('a'); // NON-COMPLIANT[false negative] + + /** + * Int, float, and complex allowed: + */ + acos(i); // COMPLIANT + acos(f); // COMPLIANT + acosh(i); // COMPLIANT + acosh(f); // COMPLIANT + asin(i); // COMPLIANT + asin(f); // COMPLIANT + asinh(i); // COMPLIANT + asinh(f); // COMPLIANT + atan(i); // COMPLIANT + atan(f); // COMPLIANT + atan2(i, i); // COMPLIANT + atan2(f, f); // COMPLIANT + atanh(i); // COMPLIANT + atanh(f); // COMPLIANT + carg(i); // COMPLIANT + carg(f); // COMPLIANT + cbrt(i); // COMPLIANT + cbrt(f); // COMPLIANT + ceil(i); // COMPLIANT + ceil(f); // COMPLIANT + cimag(i); // COMPLIANT + cimag(f); // COMPLIANT + conj(i); // COMPLIANT + conj(f); // COMPLIANT + copysign(i, i); // COMPLIANT + copysign(f, f); // COMPLIANT + cos(i); // COMPLIANT + cos(f); // COMPLIANT + cosh(i); // COMPLIANT + cosh(f); // COMPLIANT + cproj(i); // COMPLIANT + cproj(f); // COMPLIANT + creal(i); // COMPLIANT + creal(f); // COMPLIANT + erf(i); // COMPLIANT + erf(f); // COMPLIANT + erfc(i); // COMPLIANT + erfc(f); // COMPLIANT + exp(i); // COMPLIANT + exp(f); // COMPLIANT + exp2(i); // COMPLIANT + exp2(f); // COMPLIANT + expm1(i); // COMPLIANT + expm1(f); // COMPLIANT + fabs(i); // COMPLIANT + fabs(f); // COMPLIANT + fdim(i, i); // COMPLIANT + fdim(f, f); // COMPLIANT + floor(i); // COMPLIANT + floor(f); // COMPLIANT + fma(i, i, i); // COMPLIANT + fma(f, f, f); // COMPLIANT + fmax(i, i); // COMPLIANT + fmax(f, f); // COMPLIANT + fmin(i, i); // COMPLIANT + fmin(f, f); // COMPLIANT + fmod(i, i); // COMPLIANT + fmod(f, f); // COMPLIANT + frexp(i, &i); // COMPLIANT + frexp(f, &p); // COMPLIANT + hypot(i, i); // COMPLIANT + hypot(f, f); // COMPLIANT + ilogb(i); // COMPLIANT + ilogb(f); // COMPLIANT + llrint(i); // COMPLIANT + llrint(f); // COMPLIANT + ldexp(i, i); // COMPLIANT + ldexp(f, f); // COMPLIANT + lgamma(i); // COMPLIANT + lgamma(f); // COMPLIANT + llround(i); // COMPLIANT + llround(f); // COMPLIANT + log(i); // COMPLIANT + log(f); // COMPLIANT + pow(i, i); // COMPLIANT + pow(f, f); // COMPLIANT + remainder(i, i); // COMPLIANT + remainder(f, f); // COMPLIANT + remquo(i, i, &i); // COMPLIANT + remquo(f, f, &f); // COMPLIANT + rint(i); // COMPLIANT + rint(f); // COMPLIANT + round(i); // COMPLIANT + round(f); // COMPLIANT + scalbn(i, i); // COMPLIANT + scalbn(f, f); // COMPLIANT + scalbln(i, i); // COMPLIANT + scalbln(f, f); // COMPLIANT + sin(i); // COMPLIANT + sin(f); // COMPLIANT + sin(cf); // COMPLIANT + sinh(i); // COMPLIANT + sinh(f); // COMPLIANT + sqrt(i); // COMPLIANT + sqrt(f); // COMPLIANT + tan(i); // COMPLIANT + tan(f); // COMPLIANT + tanh(i); // COMPLIANT + tanh(f); // COMPLIANT + tgamma(i); // COMPLIANT + tgamma(f); // COMPLIANT + trunc(i); // COMPLIANT + trunc(f); // COMPLIANT + + /** + * Char not allowed: + */ + acos(c); // NON-COMPLIANT + acosh(c); // NON-COMPLIANT + asin(c); // NON-COMPLIANT + asinh(c); // NON-COMPLIANT + atan2(c, i); // NON-COMPLIANT + atan2(i, c); // NON-COMPLIANT + atanh(c); // NON-COMPLIANT + carg(c); // NON-COMPLIANT + ceil(c); // NON-COMPLIANT + cimag(c); // NON-COMPLIANT + conj(c); // NON-COMPLIANT + copysign(i, c); // NON-COMPLIANT + copysign(c, i); // NON-COMPLIANT + cosh(c); // NON-COMPLIANT + cproj(c); // NON-COMPLIANT + creal(c); // NON-COMPLIANT + erf(c); // NON-COMPLIANT + erfc(c); // NON-COMPLIANT + exp(c); // NON-COMPLIANT + exp2(c); // NON-COMPLIANT + expm1(c); // NON-COMPLIANT + fabs(c); // NON-COMPLIANT + fdim(c, i); // NON-COMPLIANT + fdim(i, c); // NON-COMPLIANT + floor(c); // NON-COMPLIANT + fma(c, i, i); // NON-COMPLIANT + fma(i, c, i); // NON-COMPLIANT + fma(i, i, c); // NON-COMPLIANT + fmax(c, i); // NON-COMPLIANT + fmax(i, c); // NON-COMPLIANT + fmin(c, i); // NON-COMPLIANT + fmin(i, c); // NON-COMPLIANT + fmod(c, i); // NON-COMPLIANT + fmod(i, c); // NON-COMPLIANT + frexp(c, i); // NON-COMPLIANT + hypot(c, i); // NON-COMPLIANT + hypot(i, c); // NON-COMPLIANT + ilogb(c); // NON-COMPLIANT + ldexp(c, i); // NON-COMPLIANT + ldexp(i, c); // NON-COMPLIANT + lgamma(c); // NON-COMPLIANT + llrint(c); // NON-COMPLIANT + llround(c); // NON-COMPLIANT + log(c); // NON-COMPLIANT + log10(c); // NON-COMPLIANT + log1p(c); // NON-COMPLIANT + log2(c); // NON-COMPLIANT + logb(c); // NON-COMPLIANT + lrint(c); // NON-COMPLIANT + lround(c); // NON-COMPLIANT + nearbyint(c); // NON-COMPLIANT + nextafter(c, i); // NON-COMPLIANT + nextafter(i, c); // NON-COMPLIANT + nexttoward(c, i); // NON-COMPLIANT + nexttoward(i, c); // NON-COMPLIANT + pow(c, i); // NON-COMPLIANT + pow(i, c); // NON-COMPLIANT + remainder(c, i); // NON-COMPLIANT + remainder(i, c); // NON-COMPLIANT + remquo(c, i, i); // NON-COMPLIANT + remquo(i, c, i); // NON-COMPLIANT + rint(c); // NON-COMPLIANT + round(c); // NON-COMPLIANT + scalbn(c, i); // NON-COMPLIANT + scalbn(i, c); // NON-COMPLIANT + scalbln(c, i); // NON-COMPLIANT + scalbln(i, c); // NON-COMPLIANT + sin(c); // NON-COMPLIANT + sinh(c); // NON-COMPLIANT + sqrt(c); // NON-COMPLIANT + tan(c); // NON-COMPLIANT + tanh(c); // NON-COMPLIANT + tgamma(c); // NON-COMPLIANT + trunc(c); // NON-COMPLIANT + + /** + * Complex types allowed in some calls, not others: + */ + acos(cf); // COMPLIANT + acosh(cf); // COMPLIANT + asin(cf); // COMPLIANT + asinh(cf); // COMPLIANT + atan(cf); // COMPLIANT + //atan2(cf, i); // NON-COMPLIANT + //atan2(i, cf); // NON-COMPLIANT + atanh(cf); // COMPLIANT + carg(cf); // COMPLIANT + //cbrt(cf); // NON-COMPLIANT + //ceil(cf); // NON-COMPLIANT + cimag(cf); // COMPLIANT + conj(cf); // COMPLIANT + //copysign(i, cf); // NON-COMPLIANT + //copysign(cf, i); // NON-COMPLIANT + //copysign(i, cf); // NON-COMPLIANT + cos(cf); // COMPLIANT + cosh(cf); // COMPLIANT + cproj(cf); // COMPLIANT + creal(cf); // COMPLIANT + //erf(cf); // NON-COMPLIANT + //erfc(cf); // NON-COMPLIANT + exp(cf); // COMPLIANT + //exp2(cf); // NON-COMPLIANT + //expm1(cf); // NON-COMPLIANT + fabs(cf); // COMPLIANT + //fdim(cf, i); // NON-COMPLIANT + //fdim(i, cf); // NON-COMPLIANT + //floor(cf); // NON-COMPLIANT + //fma(cf, i, i); // NON-COMPLIANT + //fma(i, cf, i); // NON-COMPLIANT + //fma(i, i, cf); // NON-COMPLIANT + //fmax(cf, i); // NON-COMPLIANT + //fmax(i, cf); // NON-COMPLIANT + //fmin(cf, i); // NON-COMPLIANT + //fmin(i, cf); // NON-COMPLIANT + //fmod(cf, i); // NON-COMPLIANT + //fmod(i, cf); // NON-COMPLIANT + //frexp(cf, i); // NON-COMPLIANT + //hypot(cf, i); // NON-COMPLIANT + //hypot(i, cf); // NON-COMPLIANT + //ilogb(cf); // NON-COMPLIANT + //ldexp(cf, i); // NON-COMPLIANT + //ldexp(i, cf); // NON-COMPLIANT + //lgamma(cf); // NON-COMPLIANT + //llrint(cf); // NON-COMPLIANT + //llround(cf); // NON-COMPLIANT + log(cf); // COMPLIANT + //log10(cf); // NON-COMPLIANT + //log1p(cf); // NON-COMPLIANT + //log2(cf); // NON-COMPLIANT + //logb(cf); // NON-COMPLIANT + //lrint(cf); // NON-COMPLIANT + //lround(cf); // NON-COMPLIANT + //nearbyint(cf); // NON-COMPLIANT + //nextafter(cf, i); // NON-COMPLIANT + //nextafter(i, cf); // NON-COMPLIANT + //nexttoward(cf, i); // NON-COMPLIANT + //nexttoward(i, cf); // NON-COMPLIANT + pow(cf, cf); // COMPLIANT + //remainder(cf, i); // NON-COMPLIANT + //remainder(i, cf); // NON-COMPLIANT + //remquo(cf, i, i); // NON-COMPLIANT + //remquo(i, cf, i); // NON-COMPLIANT + //rint(cf); // NON-COMPLIANT + //round(cf); // NON-COMPLIANT + //scalbn(cf, i); // NON-COMPLIANT + //scalbn(i, cf); // NON-COMPLIANT + //scalbln(cf, i); // NON-COMPLIANT + //scalbln(i, cf); // NON-COMPLIANT + sin(cf); // COMPLIANT + sinh(cf); // COMPLIANT + sqrt(cf); // COMPLIANT + tan(cf); // COMPLIANT + tanh(cf); // COMPLIANT + //tgamma(cf); // NON-COMPLIANT + //trunc(cf); // NON-COMPLIANT + + /* Test output arguments thoroughly */ + frexp(i, &i); // COMPLIANT + frexp(i, vp); // COMPLIANT + frexp(i, 0); // COMPLIANT + frexp(i, 'c' - 'c'); // COMPLIANT + frexp(i, c); // COMPLIANT + remquo(i, i, &i); // COMPLIANT + remquo(i, i, vp); // COMPLIANT + remquo(i, i, 0); // COMPLIANT + remquo(i, i, 'c' - 'c'); // COMPLIANT + remquo(i, i, c); // COMPLIANT + + /* Test casts */ + cos((char) i); // NON-COMPLIANT + cos((int) c); // COMPLIANT + cos((int) (char) i); // COMPLIANT + cos((char) (int) c); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected new file mode 100644 index 0000000000..6136aa4314 --- /dev/null +++ b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected @@ -0,0 +1,139 @@ +| test.c:95:3:95:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:96:3:96:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:97:3:97:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:98:3:98:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:99:3:99:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:100:3:100:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:101:3:101:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:102:3:102:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:103:3:103:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:104:3:104:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:105:3:105:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:106:3:106:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:107:3:107:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:108:3:108:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:109:3:109:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:110:3:110:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:111:3:111:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:112:3:112:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:113:3:113:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:114:3:114:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:121:3:121:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:122:3:122:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:123:3:123:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:124:3:124:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:125:3:125:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:126:3:126:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:127:3:127:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:128:3:128:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:131:3:131:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:132:3:132:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:133:3:133:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:134:3:134:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:135:3:135:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:136:3:136:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:137:3:137:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:138:3:138:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:139:3:139:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:140:3:140:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:141:3:141:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:142:3:142:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:143:3:143:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:144:3:144:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:145:3:145:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:146:3:146:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:147:3:147:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:148:3:148:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:149:3:149:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:150:3:150:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:151:3:151:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:152:3:152:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:153:3:153:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:154:3:154:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:155:3:155:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:156:3:156:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:157:3:157:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:158:3:158:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:159:3:159:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:160:3:160:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:161:3:161:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:162:3:162:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:165:3:165:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:166:3:166:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:167:3:167:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:168:3:168:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:169:3:169:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:170:3:170:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:171:3:171:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:172:3:172:17 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:175:3:175:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (float, double). | +| test.c:176:3:176:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (float, long double). | +| test.c:177:3:177:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (double, float). | +| test.c:178:3:178:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (double, long double). | +| test.c:179:3:179:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long double, float). | +| test.c:180:3:180:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long double, double). | +| test.c:183:3:183:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:184:3:184:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:185:3:185:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:186:3:186:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:187:3:187:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:188:3:188:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:189:3:189:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:190:3:190:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:191:3:191:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:192:3:192:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:193:3:193:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:194:3:194:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:195:3:195:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:196:3:196:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:197:3:197:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:198:3:198:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:199:3:199:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:200:3:200:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:201:3:201:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:202:3:202:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:203:3:203:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:204:3:204:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:205:3:205:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:206:3:206:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:207:3:207:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:208:3:208:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:209:3:209:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:210:3:210:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:211:3:211:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:212:3:212:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:213:3:213:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, float). | +| test.c:214:3:214:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, double). | +| test.c:215:3:215:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long double). | +| test.c:216:3:216:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, float). | +| test.c:217:3:217:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, double). | +| test.c:218:3:218:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long double). | +| test.c:219:3:219:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, float). | +| test.c:220:3:220:13 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, double). | +| test.c:221:3:221:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, long double). | +| test.c:222:3:222:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, float). | +| test.c:223:3:223:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, double). | +| test.c:224:3:224:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, long double). | +| test.c:225:3:225:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, float). | +| test.c:226:3:226:14 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, double). | +| test.c:227:3:227:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long double). | +| test.c:228:3:228:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, float). | +| test.c:229:3:229:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, double). | +| test.c:230:3:230:16 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long double). | +| test.c:235:3:235:15 | atan2(x,y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:242:3:242:16 | copysign(x,y) | Call to type-generic macro 'copysign' has arguments with differing standard types (int, float). | +| test.c:245:3:245:12 | fdim(x,y) | Call to type-generic macro 'fdim' has arguments with differing standard types (int, float). | +| test.c:248:3:248:14 | fma(x,y,z) | Call to type-generic macro 'fma' has arguments with differing standard types (float, int, int). | +| test.c:249:3:249:14 | fma(x,y,z) | Call to type-generic macro 'fma' has arguments with differing standard types (int, float, int). | +| test.c:250:3:250:14 | fma(x,y,z) | Call to type-generic macro 'fma' has arguments with differing standard types (int, int, float). | +| test.c:253:3:253:12 | fmax(x,y) | Call to type-generic macro 'fmax' has arguments with differing standard types (int, float). | +| test.c:256:3:256:12 | fmin(x,y) | Call to type-generic macro 'fmin' has arguments with differing standard types (int, float). | +| test.c:259:3:259:12 | fmod(x,y) | Call to type-generic macro 'fmod' has arguments with differing standard types (int, float). | +| test.c:262:3:262:13 | hypot(x,y) | Call to type-generic macro 'hypot' has arguments with differing standard types (int, float). | +| test.c:265:3:265:13 | ldexp(x,y) | Call to type-generic macro 'ldexp' has arguments with differing standard types (int, float). | +| test.c:268:3:268:17 | nextafter(x,y) | Call to type-generic macro 'nextafter' has arguments with differing standard types (int, float). | +| test.c:271:3:271:18 | nexttoward(x,y) | Call to type-generic macro 'nexttoward' has arguments with differing standard types (int, float). | +| test.c:274:3:274:17 | remainder(x,y) | Call to type-generic macro 'remainder' has arguments with differing standard types (int, float). | +| test.c:277:3:277:17 | remquo(x,y,z) | Call to type-generic macro 'remquo' has arguments with differing standard types (int, float). | +| test.c:280:3:280:15 | scalbln(x,y) | Call to type-generic macro 'scalbln' has arguments with differing standard types (int, float). | +| test.c:283:3:283:14 | scalbn(x,y) | Call to type-generic macro 'scalbn' has arguments with differing standard types (int, float). | diff --git a/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected.clang b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected.clang new file mode 100644 index 0000000000..e6ad5c62e4 --- /dev/null +++ b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected.clang @@ -0,0 +1,139 @@ +| test.c:95:3:95:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:96:3:96:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:97:3:97:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:98:3:98:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:99:3:99:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:100:3:100:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:101:3:101:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:102:3:102:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:103:3:103:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:104:3:104:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:105:3:105:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:106:3:106:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:107:3:107:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:108:3:108:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:109:3:109:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:110:3:110:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:111:3:111:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:112:3:112:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:113:3:113:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:114:3:114:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:121:3:121:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:122:3:122:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:123:3:123:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:124:3:124:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:125:3:125:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:126:3:126:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:127:3:127:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:128:3:128:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:131:3:131:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:132:3:132:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:133:3:133:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:134:3:134:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:135:3:135:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:136:3:136:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:137:3:137:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:138:3:138:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:139:3:139:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:140:3:140:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:141:3:141:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:142:3:142:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:143:3:143:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:144:3:144:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:145:3:145:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:146:3:146:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:147:3:147:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:148:3:148:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:149:3:149:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:150:3:150:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:151:3:151:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:152:3:152:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:153:3:153:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:154:3:154:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:155:3:155:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:156:3:156:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:157:3:157:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:158:3:158:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:159:3:159:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:160:3:160:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:161:3:161:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:162:3:162:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:165:3:165:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:166:3:166:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:167:3:167:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:168:3:168:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:169:3:169:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:170:3:170:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:171:3:171:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:172:3:172:17 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:175:3:175:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (float, double). | +| test.c:176:3:176:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (float, long double). | +| test.c:177:3:177:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (double, float). | +| test.c:178:3:178:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (double, long double). | +| test.c:179:3:179:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long double, float). | +| test.c:180:3:180:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long double, double). | +| test.c:183:3:183:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:184:3:184:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:185:3:185:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:186:3:186:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:187:3:187:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:188:3:188:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:189:3:189:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:190:3:190:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:191:3:191:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:192:3:192:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:193:3:193:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:194:3:194:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:195:3:195:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:196:3:196:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:197:3:197:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:198:3:198:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:199:3:199:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:200:3:200:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:201:3:201:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:202:3:202:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:203:3:203:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:204:3:204:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:205:3:205:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:206:3:206:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:207:3:207:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:208:3:208:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:209:3:209:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:210:3:210:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:211:3:211:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:212:3:212:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:213:3:213:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, float). | +| test.c:214:3:214:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, double). | +| test.c:215:3:215:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long double). | +| test.c:216:3:216:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, float). | +| test.c:217:3:217:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, double). | +| test.c:218:3:218:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long double). | +| test.c:219:3:219:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, float). | +| test.c:220:3:220:13 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, double). | +| test.c:221:3:221:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, long double). | +| test.c:222:3:222:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, float). | +| test.c:223:3:223:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, double). | +| test.c:224:3:224:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, long double). | +| test.c:225:3:225:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, float). | +| test.c:226:3:226:14 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, double). | +| test.c:227:3:227:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long double). | +| test.c:228:3:228:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, float). | +| test.c:229:3:229:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, double). | +| test.c:230:3:230:16 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long double). | +| test.c:235:3:235:15 | atan2(__x,__y) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:242:3:242:16 | copysign(__x,__y) | Call to type-generic macro 'copysign' has arguments with differing standard types (int, float). | +| test.c:245:3:245:12 | fdim(__x,__y) | Call to type-generic macro 'fdim' has arguments with differing standard types (int, float). | +| test.c:248:3:248:14 | fma(__x,__y,__z) | Call to type-generic macro 'fma' has arguments with differing standard types (float, int, int). | +| test.c:249:3:249:14 | fma(__x,__y,__z) | Call to type-generic macro 'fma' has arguments with differing standard types (int, float, int). | +| test.c:250:3:250:14 | fma(__x,__y,__z) | Call to type-generic macro 'fma' has arguments with differing standard types (int, int, float). | +| test.c:253:3:253:12 | fmax(__x,__y) | Call to type-generic macro 'fmax' has arguments with differing standard types (int, float). | +| test.c:256:3:256:12 | fmin(__x,__y) | Call to type-generic macro 'fmin' has arguments with differing standard types (int, float). | +| test.c:259:3:259:12 | fmod(__x,__y) | Call to type-generic macro 'fmod' has arguments with differing standard types (int, float). | +| test.c:262:3:262:13 | hypot(__x,__y) | Call to type-generic macro 'hypot' has arguments with differing standard types (int, float). | +| test.c:265:3:265:13 | ldexp(__x,__y) | Call to type-generic macro 'ldexp' has arguments with differing standard types (int, float). | +| test.c:268:3:268:17 | nextafter(__x,__y) | Call to type-generic macro 'nextafter' has arguments with differing standard types (int, float). | +| test.c:271:3:271:18 | nexttoward(__x,__y) | Call to type-generic macro 'nexttoward' has arguments with differing standard types (int, float). | +| test.c:274:3:274:17 | remainder(__x,__y) | Call to type-generic macro 'remainder' has arguments with differing standard types (int, float). | +| test.c:277:3:277:17 | remquo(__x,__y,__z) | Call to type-generic macro 'remquo' has arguments with differing standard types (int, float). | +| test.c:280:3:280:15 | scalbln(__x,__y) | Call to type-generic macro 'scalbln' has arguments with differing standard types (int, float). | +| test.c:283:3:283:14 | scalbn(__x,__y) | Call to type-generic macro 'scalbn' has arguments with differing standard types (int, float). | diff --git a/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected.gcc b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected.gcc new file mode 100644 index 0000000000..f8c610f8c2 --- /dev/null +++ b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.expected.gcc @@ -0,0 +1,139 @@ +| test.c:95:3:95:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:96:3:96:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:97:3:97:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:98:3:98:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:99:3:99:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:100:3:100:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:101:3:101:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:102:3:102:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:103:3:103:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:104:3:104:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:105:3:105:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:106:3:106:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:107:3:107:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:108:3:108:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:109:3:109:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:110:3:110:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:111:3:111:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:112:3:112:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:113:3:113:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:114:3:114:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned int). | +| test.c:121:3:121:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:122:3:122:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:123:3:123:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:124:3:124:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:125:3:125:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:126:3:126:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:127:3:127:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:128:3:128:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:131:3:131:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:132:3:132:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:133:3:133:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:134:3:134:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:135:3:135:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:136:3:136:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:137:3:137:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:138:3:138:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:139:3:139:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:140:3:140:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:141:3:141:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:142:3:142:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:143:3:143:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:144:3:144:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:145:3:145:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:146:3:146:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:147:3:147:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:148:3:148:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long). | +| test.c:149:3:149:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:150:3:150:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long). | +| test.c:151:3:151:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:152:3:152:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:153:3:153:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, int). | +| test.c:154:3:154:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned int). | +| test.c:155:3:155:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:156:3:156:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, unsigned long). | +| test.c:157:3:157:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:158:3:158:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, unsigned long). | +| test.c:159:3:159:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:160:3:160:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:161:3:161:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, int). | +| test.c:162:3:162:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, unsigned int). | +| test.c:165:3:165:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:166:3:166:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:167:3:167:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:168:3:168:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, unsigned long). | +| test.c:169:3:169:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:170:3:170:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:171:3:171:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:172:3:172:17 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long). | +| test.c:175:3:175:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (float, double). | +| test.c:176:3:176:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (float, long double). | +| test.c:177:3:177:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (double, float). | +| test.c:178:3:178:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (double, long double). | +| test.c:179:3:179:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long double, float). | +| test.c:180:3:180:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long double, double). | +| test.c:183:3:183:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:184:3:184:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:185:3:185:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:186:3:186:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:187:3:187:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:188:3:188:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:189:3:189:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:190:3:190:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:191:3:191:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:192:3:192:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:193:3:193:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:194:3:194:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:195:3:195:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:196:3:196:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:197:3:197:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:198:3:198:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:199:3:199:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:200:3:200:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:201:3:201:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:202:3:202:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:203:3:203:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:204:3:204:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:205:3:205:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:206:3:206:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:207:3:207:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:208:3:208:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:209:3:209:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:210:3:210:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:211:3:211:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, double). | +| test.c:212:3:212:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, long double). | +| test.c:213:3:213:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, float). | +| test.c:214:3:214:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, double). | +| test.c:215:3:215:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long double). | +| test.c:216:3:216:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, float). | +| test.c:217:3:217:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, double). | +| test.c:218:3:218:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned int, long double). | +| test.c:219:3:219:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, float). | +| test.c:220:3:220:13 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, double). | +| test.c:221:3:221:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, long double). | +| test.c:222:3:222:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, float). | +| test.c:223:3:223:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, double). | +| test.c:224:3:224:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (long, long double). | +| test.c:225:3:225:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, float). | +| test.c:226:3:226:14 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, double). | +| test.c:227:3:227:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long double). | +| test.c:228:3:228:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, float). | +| test.c:229:3:229:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, double). | +| test.c:230:3:230:16 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (unsigned long, long double). | +| test.c:235:3:235:15 | atan2(Val1,Val2) | Call to type-generic macro 'atan2' has arguments with differing standard types (int, float). | +| test.c:242:3:242:16 | copysign(Val1,Val2) | Call to type-generic macro 'copysign' has arguments with differing standard types (int, float). | +| test.c:245:3:245:12 | fdim(Val1,Val2) | Call to type-generic macro 'fdim' has arguments with differing standard types (int, float). | +| test.c:248:3:248:14 | fma(Val1,Val2,Val3) | Call to type-generic macro 'fma' has arguments with differing standard types (float, int, int). | +| test.c:249:3:249:14 | fma(Val1,Val2,Val3) | Call to type-generic macro 'fma' has arguments with differing standard types (int, float, int). | +| test.c:250:3:250:14 | fma(Val1,Val2,Val3) | Call to type-generic macro 'fma' has arguments with differing standard types (int, int, float). | +| test.c:253:3:253:12 | fmax(Val1,Val2) | Call to type-generic macro 'fmax' has arguments with differing standard types (int, float). | +| test.c:256:3:256:12 | fmin(Val1,Val2) | Call to type-generic macro 'fmin' has arguments with differing standard types (int, float). | +| test.c:259:3:259:12 | fmod(Val1,Val2) | Call to type-generic macro 'fmod' has arguments with differing standard types (int, float). | +| test.c:262:3:262:13 | hypot(Val1,Val2) | Call to type-generic macro 'hypot' has arguments with differing standard types (int, float). | +| test.c:265:3:265:13 | ldexp(Val1,Val2) | Call to type-generic macro 'ldexp' has arguments with differing standard types (int, float). | +| test.c:268:3:268:17 | nextafter(Val1,Val2) | Call to type-generic macro 'nextafter' has arguments with differing standard types (int, float). | +| test.c:271:3:271:18 | nexttoward(Val1,Val2) | Call to type-generic macro 'nexttoward' has arguments with differing standard types (int, float). | +| test.c:274:3:274:17 | remainder(Val1,Val2) | Call to type-generic macro 'remainder' has arguments with differing standard types (int, float). | +| test.c:277:3:277:17 | remquo(Val1,Val2,Val3) | Call to type-generic macro 'remquo' has arguments with differing standard types (int, float). | +| test.c:280:3:280:15 | scalbln(Val1,Val2) | Call to type-generic macro 'scalbln' has arguments with differing standard types (int, float). | +| test.c:283:3:283:14 | scalbn(Val1,Val2) | Call to type-generic macro 'scalbn' has arguments with differing standard types (int, float). | diff --git a/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.qlref b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.qlref new file mode 100644 index 0000000000..550893822f --- /dev/null +++ b/c/misra/test/rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.qlref @@ -0,0 +1 @@ +rules/RULE-21-23/TgMathArgumentsWithDifferingStandardType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-23/test.c b/c/misra/test/rules/RULE-21-23/test.c new file mode 100644 index 0000000000..08df1184a7 --- /dev/null +++ b/c/misra/test/rules/RULE-21-23/test.c @@ -0,0 +1,288 @@ +#include +#include + +void f1() { + signed char c = 0; + unsigned char uc = 0; + short s = 0; + unsigned short us = 0; + int i = 0; + unsigned int ui = 0; + long l = 0; + unsigned long ul = 0; + float f = 0.0f; + double d = 0.0; + long double ld = 0.0; + uint8_t u8 = 0; + int8_t i8 = 0; + uint16_t u16 = 0; + int16_t i16 = 0; + uint32_t u32 = 0; + int32_t i32 = 0; + uint64_t u64 = 0; + int64_t i64 = 0; + + /** + * Test exact types + */ + atan2(c, c); // COMPLIANT + atan2(uc, uc); // COMPLIANT + atan2(s, s); // COMPLIANT + atan2(us, us); // COMPLIANT + atan2(i, i); // COMPLIANT + atan2(ui, ui); // COMPLIANT + atan2(ui, ui); // COMPLIANT + atan2(l, l); // COMPLIANT + atan2(ul, ul); // COMPLIANT + atan2(f, f); // COMPLIANT + atan2(d, d); // COMPLIANT + atan2(ld, ld); // COMPLIANT + atan2(u8, u8); // COMPLIANT + atan2(i8, i8); // COMPLIANT + atan2(u16, u16); // COMPLIANT + atan2(i16, i16); // COMPLIANT + atan2(u32, u32); // COMPLIANT + atan2(i32, i32); // COMPLIANT + atan2(u64, u64); // COMPLIANT + atan2(i64, i64); // COMPLIANT + + /** Test equivalent types */ + atan2(c, i8); // COMPLIANT + atan2(i8, c); // COMPLIANT + atan2(uc, u8); // COMPLIANT + atan2(u8, uc); // COMPLIANT + atan2(s, i16); // COMPLIANT + atan2(i16, s); // COMPLIANT + atan2(us, u16); // COMPLIANT + atan2(u16, us); // COMPLIANT + atan2(i, i32); // COMPLIANT + atan2(i32, i); // COMPLIANT + atan2(ui, u32); // COMPLIANT + atan2(u32, ui); // COMPLIANT + atan2(l, i64); // COMPLIANT + atan2(i64, l); // COMPLIANT + atan2(ul, u64); // COMPLIANT + atan2(u64, ul); // COMPLIANT + + /** Types are the same after integer promotion */ + atan2(c, i8); // COMPLIANT + atan2(c, u8); // COMPLIANT + atan2(c, i16); // COMPLIANT + atan2(c, u16); // COMPLIANT + atan2(c, i32); // COMPLIANT + atan2(uc, i8); // COMPLIANT + atan2(uc, u8); // COMPLIANT + atan2(uc, i16); // COMPLIANT + atan2(uc, u16); // COMPLIANT + atan2(uc, i32); // COMPLIANT + atan2(s, i8); // COMPLIANT + atan2(s, u8); // COMPLIANT + atan2(s, i16); // COMPLIANT + atan2(s, u16); // COMPLIANT + atan2(s, i32); // COMPLIANT + atan2(us, i8); // COMPLIANT + atan2(us, u8); // COMPLIANT + atan2(us, i16); // COMPLIANT + atan2(us, u16); // COMPLIANT + atan2(us, i32); // COMPLIANT + atan2(i, i8); // COMPLIANT + atan2(i, u8); // COMPLIANT + atan2(i, i16); // COMPLIANT + atan2(i, u16); // COMPLIANT + atan2(i, i32); // COMPLIANT + + /** Integer promotion makes a signed int, not an unsigned int */ + atan2(c, ui); // NON-COMPLIANT + atan2(c, u32); // NON-COMPLIANT + atan2(i8, ui); // NON-COMPLIANT + atan2(i8, u32); // NON-COMPLIANT + atan2(uc, ui); // NON-COMPLIANT + atan2(uc, u32); // NON-COMPLIANT + atan2(u8, ui); // NON-COMPLIANT + atan2(u8, u32); // NON-COMPLIANT + atan2(s, ui); // NON-COMPLIANT + atan2(s, u32); // NON-COMPLIANT + atan2(i16, ui); // NON-COMPLIANT + atan2(i16, u32); // NON-COMPLIANT + atan2(us, ui); // NON-COMPLIANT + atan2(us, u32); // NON-COMPLIANT + atan2(u16, ui); // NON-COMPLIANT + atan2(u16, u32); // NON-COMPLIANT + atan2(i, ui); // NON-COMPLIANT + atan2(i, u32); // NON-COMPLIANT + atan2(i32, ui); // NON-COMPLIANT + atan2(i32, u32); // NON-COMPLIANT + atan2(ui, ui); // COMPLIANT + atan2(ui, u32); // COMPLIANT + atan2(u32, ui); // COMPLIANT + atan2(u32, u32); // COMPLIANT + + /** Integer promotion makes int, not long */ + atan2(c, l); // NON-COMPLIANT + atan2(i8, l); // NON-COMPLIANT + atan2(uc, l); // NON-COMPLIANT + atan2(u8, l); // NON-COMPLIANT + atan2(s, l); // NON-COMPLIANT + atan2(i16, l); // NON-COMPLIANT + atan2(us, l); // NON-COMPLIANT + atan2(u16, l); // NON-COMPLIANT + + /** Integer vs long */ + atan2(i, l); // NON-COMPLIANT + atan2(i32, l); // NON-COMPLIANT + atan2(ui, l); // NON-COMPLIANT + atan2(u32, l); // NON-COMPLIANT + atan2(l, i); // NON-COMPLIANT + atan2(l, ui); // NON-COMPLIANT + atan2(l, i32); // NON-COMPLIANT + atan2(l, u32); // NON-COMPLIANT + atan2(i, ul); // NON-COMPLIANT + atan2(i32, ul); // NON-COMPLIANT + atan2(ui, ul); // NON-COMPLIANT + atan2(u32, ul); // NON-COMPLIANT + atan2(ul, i); // NON-COMPLIANT + atan2(ul, ui); // NON-COMPLIANT + atan2(ul, i32); // NON-COMPLIANT + atan2(ul, u32); // NON-COMPLIANT + atan2(i, i64); // NON-COMPLIANT + atan2(i32, i64); // NON-COMPLIANT + atan2(ui, i64); // NON-COMPLIANT + atan2(u32, i64); // NON-COMPLIANT + atan2(i64, i); // NON-COMPLIANT + atan2(i64, ui); // NON-COMPLIANT + atan2(i64, i32); // NON-COMPLIANT + atan2(i64, u32); // NON-COMPLIANT + atan2(i, u64); // NON-COMPLIANT + atan2(i32, u64); // NON-COMPLIANT + atan2(ui, u64); // NON-COMPLIANT + atan2(u32, u64); // NON-COMPLIANT + atan2(u64, i); // NON-COMPLIANT + atan2(u64, ui); // NON-COMPLIANT + atan2(u64, i32); // NON-COMPLIANT + atan2(u64, u32); // NON-COMPLIANT + + /** Signed vs unsigned long, since those don't promote */ + atan2(l, ul); // NON-COMPLIANT + atan2(l, u64); // NON-COMPLIANT + atan2(i64, ul); // NON-COMPLIANT + atan2(i64, u64); // NON-COMPLIANT + atan2(ul, l); // NON-COMPLIANT + atan2(ul, i64); // NON-COMPLIANT + atan2(u64, l); // NON-COMPLIANT + atan2(u64, i64); // NON-COMPLIANT + + /** Mismatched float sizes */ + atan2(f, d); // NON-COMPLIANT + atan2(f, ld); // NON-COMPLIANT + atan2(d, f); // NON-COMPLIANT + atan2(d, ld); // NON-COMPLIANT + atan2(ld, f); // NON-COMPLIANT + atan2(ld, d); // NON-COMPLIANT + + /** Float vs int */ + atan2(c, f); // NON-COMPLIANT + atan2(c, d); // NON-COMPLIANT + atan2(c, ld); // NON-COMPLIANT + atan2(i8, f); // NON-COMPLIANT + atan2(i8, d); // NON-COMPLIANT + atan2(i8, ld); // NON-COMPLIANT + atan2(uc, f); // NON-COMPLIANT + atan2(uc, d); // NON-COMPLIANT + atan2(uc, ld); // NON-COMPLIANT + atan2(u8, f); // NON-COMPLIANT + atan2(u8, d); // NON-COMPLIANT + atan2(u8, ld); // NON-COMPLIANT + atan2(s, f); // NON-COMPLIANT + atan2(s, d); // NON-COMPLIANT + atan2(s, ld); // NON-COMPLIANT + atan2(i16, f); // NON-COMPLIANT + atan2(i16, d); // NON-COMPLIANT + atan2(i16, ld); // NON-COMPLIANT + atan2(us, f); // NON-COMPLIANT + atan2(us, d); // NON-COMPLIANT + atan2(us, ld); // NON-COMPLIANT + atan2(u16, f); // NON-COMPLIANT + atan2(u16, d); // NON-COMPLIANT + atan2(u16, ld); // NON-COMPLIANT + atan2(i, f); // NON-COMPLIANT + atan2(i, d); // NON-COMPLIANT + atan2(i, ld); // NON-COMPLIANT + atan2(i32, f); // NON-COMPLIANT + atan2(i32, d); // NON-COMPLIANT + atan2(i32, ld); // NON-COMPLIANT + atan2(ui, f); // NON-COMPLIANT + atan2(ui, d); // NON-COMPLIANT + atan2(ui, ld); // NON-COMPLIANT + atan2(u32, f); // NON-COMPLIANT + atan2(u32, d); // NON-COMPLIANT + atan2(u32, ld); // NON-COMPLIANT + atan2(l, f); // NON-COMPLIANT + atan2(l, d); // NON-COMPLIANT + atan2(l, ld); // NON-COMPLIANT + atan2(i64, f); // NON-COMPLIANT + atan2(i64, d); // NON-COMPLIANT + atan2(i64, ld); // NON-COMPLIANT + atan2(ul, f); // NON-COMPLIANT + atan2(ul, d); // NON-COMPLIANT + atan2(ul, ld); // NON-COMPLIANT + atan2(u64, f); // NON-COMPLIANT + atan2(u64, d); // NON-COMPLIANT + atan2(u64, ld); // NON-COMPLIANT + + /** Casts and conversions */ + atan2((float)i, f); // COMPLIANT + atan2(i, (int)f); // COMPLIANT + atan2((i), f); // NON-COMPLIANT + atan2(((float)i), f); // COMPLIANT + atan2((float)((int)l), f); // COMPLIANT + + /** Other functions */ + copysign(f, f); // COMPLIANT + copysign(i, i); // COMPLIANT + copysign(i, f); // NON-COMPLIANT + fdim(f, f); // COMPLIANT + fdim(i, i); // COMPLIANT + fdim(i, f); // NON-COMPLIANT + fma(f, f, f); // COMPLIANT + fma(i, i, i); // COMPLIANT + fma(f, i, i); // NON-COMPLIANT + fma(i, f, i); // NON-COMPLIANT + fma(i, i, f); // NON-COMPLIANT + fmax(f, f); // COMPLIANT + fmax(i, i); // COMPLIANT + fmax(i, f); // NON-COMPLIANT + fmin(f, f); // COMPLIANT + fmin(i, i); // COMPLIANT + fmin(i, f); // NON-COMPLIANT + fmod(f, f); // COMPLIANT + fmod(i, i); // COMPLIANT + fmod(i, f); // NON-COMPLIANT + hypot(f, f); // COMPLIANT + hypot(i, i); // COMPLIANT + hypot(i, f); // NON-COMPLIANT + ldexp(f, f); // COMPLIANT + ldexp(i, i); // COMPLIANT + ldexp(i, f); // NON-COMPLIANT + nextafter(f, f); // COMPLIANT + nextafter(i, i); // COMPLIANT + nextafter(i, f); // NON-COMPLIANT + nexttoward(f, f); // COMPLIANT + nexttoward(i, i); // COMPLIANT + nexttoward(i, f); // NON-COMPLIANT + remainder(f, f); // COMPLIANT + remainder(i, i); // COMPLIANT + remainder(i, f); // NON-COMPLIANT + remquo(f, f, 0); // COMPLIANT + remquo(i, i, 0); // COMPLIANT + remquo(i, f, 0); // NON-COMPLIANT + scalbln(f, f); // COMPLIANT + scalbln(i, i); // COMPLIANT + scalbln(i, f); // NON-COMPLIANT + scalbn(f, f); // COMPLIANT + scalbn(i, i); // COMPLIANT + scalbn(i, f); // NON-COMPLIANT + + // `frexp` has two parameters, but the second is an output parameter, and + // should not be covered by this rule. + frexp(f, 0); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-24/CallToBannedRandomFunction.expected b/c/misra/test/rules/RULE-21-24/CallToBannedRandomFunction.expected new file mode 100644 index 0000000000..b3953d166b --- /dev/null +++ b/c/misra/test/rules/RULE-21-24/CallToBannedRandomFunction.expected @@ -0,0 +1,2 @@ +| test.c:5:3:5:7 | call to srand | Call to banned random number generation function 'srand'. | +| test.c:6:11:6:14 | call to rand | Call to banned random number generation function 'rand'. | diff --git a/c/misra/test/rules/RULE-21-24/CallToBannedRandomFunction.qlref b/c/misra/test/rules/RULE-21-24/CallToBannedRandomFunction.qlref new file mode 100644 index 0000000000..b229c0e84f --- /dev/null +++ b/c/misra/test/rules/RULE-21-24/CallToBannedRandomFunction.qlref @@ -0,0 +1 @@ +rules/RULE-21-24/CallToBannedRandomFunction.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-24/test.c b/c/misra/test/rules/RULE-21-24/test.c new file mode 100644 index 0000000000..56cfae3cb1 --- /dev/null +++ b/c/misra/test/rules/RULE-21-24/test.c @@ -0,0 +1,11 @@ +#include "stdlib.h" + +void f() { + // rand() is banned -- and thus, so is srand(). + srand(0); // NON-COMPLIANT + int x = rand(); // NON-COMPLIANT + + // Other functions from stdlib are not banned by this rule. + x = abs(-4); // COMPLIANT + getenv("ENV_VAR"); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.expected b/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.expected new file mode 100644 index 0000000000..0b17405a0e --- /dev/null +++ b/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.expected @@ -0,0 +1,106 @@ +edges +| test.c:4:5:4:6 | *g2 | test.c:54:33:54:34 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:55:29:55:30 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:56:42:56:43 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:57:35:57:36 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:58:36:58:37 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:59:56:59:57 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:60:60:60:61 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:61:54:61:55 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:62:58:62:59 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:63:37:63:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:64:37:64:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:65:36:65:37 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:66:37:66:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:67:37:67:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:68:23:68:24 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:69:23:69:24 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:72:23:72:24 | g2 | provenance | | +| test.c:4:10:4:29 | memory_order_relaxed | test.c:4:5:4:6 | *g2 | provenance | | +| test.c:4:10:4:29 | memory_order_relaxed | test.c:4:10:4:29 | memory_order_relaxed | provenance | | +| test.c:5:5:5:6 | *g3 | test.c:73:23:73:24 | g3 | provenance | | +| test.c:5:10:5:29 | memory_order_acquire | test.c:5:5:5:6 | *g3 | provenance | | +| test.c:5:10:5:29 | memory_order_acquire | test.c:5:10:5:29 | memory_order_acquire | provenance | | +| test.c:6:5:6:6 | *g4 | test.c:74:23:74:24 | g4 | provenance | | +| test.c:6:10:6:29 | memory_order_consume | test.c:6:5:6:6 | *g4 | provenance | | +| test.c:6:10:6:29 | memory_order_consume | test.c:6:10:6:29 | memory_order_consume | provenance | | +| test.c:7:5:7:6 | *g5 | test.c:75:23:75:24 | g5 | provenance | | +| test.c:7:10:7:29 | memory_order_acq_rel | test.c:7:5:7:6 | *g5 | provenance | | +| test.c:7:10:7:29 | memory_order_acq_rel | test.c:7:10:7:29 | memory_order_acq_rel | provenance | | +| test.c:8:5:8:6 | *g6 | test.c:76:23:76:24 | g6 | provenance | | +| test.c:8:10:8:29 | memory_order_release | test.c:8:5:8:6 | *g6 | provenance | | +| test.c:8:10:8:29 | memory_order_release | test.c:8:10:8:29 | memory_order_release | provenance | | +nodes +| test.c:4:5:4:6 | *g2 | semmle.label | *g2 | +| test.c:4:10:4:29 | memory_order_relaxed | semmle.label | memory_order_relaxed | +| test.c:4:10:4:29 | memory_order_relaxed | semmle.label | memory_order_relaxed | +| test.c:5:5:5:6 | *g3 | semmle.label | *g3 | +| test.c:5:10:5:29 | memory_order_acquire | semmle.label | memory_order_acquire | +| test.c:5:10:5:29 | memory_order_acquire | semmle.label | memory_order_acquire | +| test.c:6:5:6:6 | *g4 | semmle.label | *g4 | +| test.c:6:10:6:29 | memory_order_consume | semmle.label | memory_order_consume | +| test.c:6:10:6:29 | memory_order_consume | semmle.label | memory_order_consume | +| test.c:7:5:7:6 | *g5 | semmle.label | *g5 | +| test.c:7:10:7:29 | memory_order_acq_rel | semmle.label | memory_order_acq_rel | +| test.c:7:10:7:29 | memory_order_acq_rel | semmle.label | memory_order_acq_rel | +| test.c:8:5:8:6 | *g6 | semmle.label | *g6 | +| test.c:8:10:8:29 | memory_order_release | semmle.label | memory_order_release | +| test.c:8:10:8:29 | memory_order_release | semmle.label | memory_order_release | +| test.c:17:29:17:48 | memory_order_relaxed | semmle.label | memory_order_relaxed | +| test.c:18:29:18:48 | memory_order_acquire | semmle.label | memory_order_acquire | +| test.c:19:29:19:48 | memory_order_consume | semmle.label | memory_order_consume | +| test.c:20:29:20:48 | memory_order_acq_rel | semmle.label | memory_order_acq_rel | +| test.c:21:29:21:48 | memory_order_release | semmle.label | memory_order_release | +| test.c:54:33:54:34 | g2 | semmle.label | g2 | +| test.c:55:29:55:30 | g2 | semmle.label | g2 | +| test.c:56:42:56:43 | g2 | semmle.label | g2 | +| test.c:57:35:57:36 | g2 | semmle.label | g2 | +| test.c:58:36:58:37 | g2 | semmle.label | g2 | +| test.c:59:56:59:57 | g2 | semmle.label | g2 | +| test.c:60:60:60:61 | g2 | semmle.label | g2 | +| test.c:61:54:61:55 | g2 | semmle.label | g2 | +| test.c:62:58:62:59 | g2 | semmle.label | g2 | +| test.c:63:37:63:38 | g2 | semmle.label | g2 | +| test.c:64:37:64:38 | g2 | semmle.label | g2 | +| test.c:65:36:65:37 | g2 | semmle.label | g2 | +| test.c:66:37:66:38 | g2 | semmle.label | g2 | +| test.c:67:37:67:38 | g2 | semmle.label | g2 | +| test.c:68:23:68:24 | g2 | semmle.label | g2 | +| test.c:69:23:69:24 | g2 | semmle.label | g2 | +| test.c:72:23:72:24 | g2 | semmle.label | g2 | +| test.c:73:23:73:24 | g3 | semmle.label | g3 | +| test.c:74:23:74:24 | g4 | semmle.label | g4 | +| test.c:75:23:75:24 | g5 | semmle.label | g5 | +| test.c:76:23:76:24 | g6 | semmle.label | g6 | +| test.c:80:23:80:23 | 1 | semmle.label | 1 | +| test.c:81:23:81:25 | 100 | semmle.label | 100 | +subpaths +#select +| test.c:17:29:17:48 | memory_order_relaxed | test.c:17:29:17:48 | memory_order_relaxed | test.c:17:29:17:48 | memory_order_relaxed | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:18:29:18:48 | memory_order_acquire | test.c:18:29:18:48 | memory_order_acquire | test.c:18:29:18:48 | memory_order_acquire | Invalid memory order '$@' in call to function '$@'. | memory_order_acquire | memory_order_acquire | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:19:29:19:48 | memory_order_consume | test.c:19:29:19:48 | memory_order_consume | test.c:19:29:19:48 | memory_order_consume | Invalid memory order '$@' in call to function '$@'. | memory_order_consume | memory_order_consume | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:20:29:20:48 | memory_order_acq_rel | test.c:20:29:20:48 | memory_order_acq_rel | test.c:20:29:20:48 | memory_order_acq_rel | Invalid memory order '$@' in call to function '$@'. | memory_order_acq_rel | memory_order_acq_rel | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:21:29:21:48 | memory_order_release | test.c:21:29:21:48 | memory_order_release | test.c:21:29:21:48 | memory_order_release | Invalid memory order '$@' in call to function '$@'. | memory_order_release | memory_order_release | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:54:33:54:34 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:54:33:54:34 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_store_explicit | +| test.c:55:29:55:30 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:55:29:55:30 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:56:42:56:43 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:56:42:56:43 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_flag_test_and_set_explicit | +| test.c:57:35:57:36 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:57:35:57:36 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_flag_clear_explicit | +| test.c:58:36:58:37 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:58:36:58:37 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_exchange_explicit | +| test.c:59:56:59:57 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:59:56:59:57 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_strong_explicit | +| test.c:60:60:60:61 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:60:60:60:61 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_strong_explicit | +| test.c:61:54:61:55 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:61:54:61:55 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_weak_explicit | +| test.c:62:58:62:59 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:62:58:62:59 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_weak_explicit | +| test.c:63:37:63:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:63:37:63:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_add_explicit | +| test.c:64:37:64:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:64:37:64:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_sub_explicit | +| test.c:65:36:65:37 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:65:36:65:37 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_or_explicit | +| test.c:66:37:66:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:66:37:66:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_xor_explicit | +| test.c:67:37:67:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:67:37:67:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_and_explicit | +| test.c:68:23:68:24 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:68:23:68:24 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:69:23:69:24 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:69:23:69:24 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_signal_fence | +| test.c:72:23:72:24 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:72:23:72:24 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:73:23:73:24 | g3 | test.c:5:10:5:29 | memory_order_acquire | test.c:73:23:73:24 | g3 | Invalid memory order '$@' in call to function '$@'. | memory_order_acquire | memory_order_acquire | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:74:23:74:24 | g4 | test.c:6:10:6:29 | memory_order_consume | test.c:74:23:74:24 | g4 | Invalid memory order '$@' in call to function '$@'. | memory_order_consume | memory_order_consume | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:75:23:75:24 | g5 | test.c:7:10:7:29 | memory_order_acq_rel | test.c:75:23:75:24 | g5 | Invalid memory order '$@' in call to function '$@'. | memory_order_acq_rel | memory_order_acq_rel | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:76:23:76:24 | g6 | test.c:8:10:8:29 | memory_order_release | test.c:76:23:76:24 | g6 | Invalid memory order '$@' in call to function '$@'. | memory_order_release | memory_order_release | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:80:23:80:23 | 1 | test.c:80:23:80:23 | 1 | test.c:80:23:80:23 | 1 | Invalid memory order '$@' in call to function '$@'. | memory_order_consume | memory_order_consume | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:81:23:81:25 | 100 | test.c:81:23:81:25 | 100 | test.c:81:23:81:25 | 100 | Invalid memory order '$@' in call to function '$@'. | 100 | 100 | Invocation of a standard function implemented as a macro | atomic_thread_fence | diff --git a/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.expected.gcc b/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.expected.gcc new file mode 100644 index 0000000000..db07e0aa33 --- /dev/null +++ b/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.expected.gcc @@ -0,0 +1,106 @@ +edges +| test.c:4:5:4:6 | *g2 | test.c:54:33:54:34 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:55:29:55:30 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:56:42:56:43 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:57:35:57:36 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:58:36:58:37 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:59:56:59:57 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:60:60:60:61 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:61:54:61:55 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:62:58:62:59 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:63:37:63:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:64:37:64:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:65:36:65:37 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:66:37:66:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:67:37:67:38 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:68:23:68:24 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:69:23:69:24 | g2 | provenance | | +| test.c:4:5:4:6 | *g2 | test.c:72:23:72:24 | g2 | provenance | | +| test.c:4:10:4:29 | memory_order_relaxed | test.c:4:5:4:6 | *g2 | provenance | | +| test.c:4:10:4:29 | memory_order_relaxed | test.c:4:10:4:29 | memory_order_relaxed | provenance | | +| test.c:5:5:5:6 | *g3 | test.c:73:23:73:24 | g3 | provenance | | +| test.c:5:10:5:29 | memory_order_acquire | test.c:5:5:5:6 | *g3 | provenance | | +| test.c:5:10:5:29 | memory_order_acquire | test.c:5:10:5:29 | memory_order_acquire | provenance | | +| test.c:6:5:6:6 | *g4 | test.c:74:23:74:24 | g4 | provenance | | +| test.c:6:10:6:29 | memory_order_consume | test.c:6:5:6:6 | *g4 | provenance | | +| test.c:6:10:6:29 | memory_order_consume | test.c:6:10:6:29 | memory_order_consume | provenance | | +| test.c:7:5:7:6 | *g5 | test.c:75:23:75:24 | g5 | provenance | | +| test.c:7:10:7:29 | memory_order_acq_rel | test.c:7:5:7:6 | *g5 | provenance | | +| test.c:7:10:7:29 | memory_order_acq_rel | test.c:7:10:7:29 | memory_order_acq_rel | provenance | | +| test.c:8:5:8:6 | *g6 | test.c:76:23:76:24 | g6 | provenance | | +| test.c:8:10:8:29 | memory_order_release | test.c:8:5:8:6 | *g6 | provenance | | +| test.c:8:10:8:29 | memory_order_release | test.c:8:10:8:29 | memory_order_release | provenance | | +nodes +| test.c:4:5:4:6 | *g2 | semmle.label | *g2 | +| test.c:4:10:4:29 | memory_order_relaxed | semmle.label | memory_order_relaxed | +| test.c:4:10:4:29 | memory_order_relaxed | semmle.label | memory_order_relaxed | +| test.c:5:5:5:6 | *g3 | semmle.label | *g3 | +| test.c:5:10:5:29 | memory_order_acquire | semmle.label | memory_order_acquire | +| test.c:5:10:5:29 | memory_order_acquire | semmle.label | memory_order_acquire | +| test.c:6:5:6:6 | *g4 | semmle.label | *g4 | +| test.c:6:10:6:29 | memory_order_consume | semmle.label | memory_order_consume | +| test.c:6:10:6:29 | memory_order_consume | semmle.label | memory_order_consume | +| test.c:7:5:7:6 | *g5 | semmle.label | *g5 | +| test.c:7:10:7:29 | memory_order_acq_rel | semmle.label | memory_order_acq_rel | +| test.c:7:10:7:29 | memory_order_acq_rel | semmle.label | memory_order_acq_rel | +| test.c:8:5:8:6 | *g6 | semmle.label | *g6 | +| test.c:8:10:8:29 | memory_order_release | semmle.label | memory_order_release | +| test.c:8:10:8:29 | memory_order_release | semmle.label | memory_order_release | +| test.c:17:3:17:49 | memory_order_relaxed | semmle.label | memory_order_relaxed | +| test.c:18:3:18:49 | memory_order_acquire | semmle.label | memory_order_acquire | +| test.c:19:3:19:49 | memory_order_consume | semmle.label | memory_order_consume | +| test.c:20:3:20:49 | memory_order_acq_rel | semmle.label | memory_order_acq_rel | +| test.c:21:3:21:49 | memory_order_release | semmle.label | memory_order_release | +| test.c:54:33:54:34 | g2 | semmle.label | g2 | +| test.c:55:29:55:30 | g2 | semmle.label | g2 | +| test.c:56:42:56:43 | g2 | semmle.label | g2 | +| test.c:57:35:57:36 | g2 | semmle.label | g2 | +| test.c:58:36:58:37 | g2 | semmle.label | g2 | +| test.c:59:56:59:57 | g2 | semmle.label | g2 | +| test.c:60:60:60:61 | g2 | semmle.label | g2 | +| test.c:61:54:61:55 | g2 | semmle.label | g2 | +| test.c:62:58:62:59 | g2 | semmle.label | g2 | +| test.c:63:37:63:38 | g2 | semmle.label | g2 | +| test.c:64:37:64:38 | g2 | semmle.label | g2 | +| test.c:65:36:65:37 | g2 | semmle.label | g2 | +| test.c:66:37:66:38 | g2 | semmle.label | g2 | +| test.c:67:37:67:38 | g2 | semmle.label | g2 | +| test.c:68:23:68:24 | g2 | semmle.label | g2 | +| test.c:69:23:69:24 | g2 | semmle.label | g2 | +| test.c:72:23:72:24 | g2 | semmle.label | g2 | +| test.c:73:23:73:24 | g3 | semmle.label | g3 | +| test.c:74:23:74:24 | g4 | semmle.label | g4 | +| test.c:75:23:75:24 | g5 | semmle.label | g5 | +| test.c:76:23:76:24 | g6 | semmle.label | g6 | +| test.c:80:23:80:23 | 1 | semmle.label | 1 | +| test.c:81:23:81:25 | 100 | semmle.label | 100 | +subpaths +#select +| test.c:17:3:17:49 | memory_order_relaxed | test.c:17:3:17:49 | memory_order_relaxed | test.c:17:3:17:49 | memory_order_relaxed | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:18:3:18:49 | memory_order_acquire | test.c:18:3:18:49 | memory_order_acquire | test.c:18:3:18:49 | memory_order_acquire | Invalid memory order '$@' in call to function '$@'. | memory_order_acquire | memory_order_acquire | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:19:3:19:49 | memory_order_consume | test.c:19:3:19:49 | memory_order_consume | test.c:19:3:19:49 | memory_order_consume | Invalid memory order '$@' in call to function '$@'. | memory_order_consume | memory_order_consume | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:20:3:20:49 | memory_order_acq_rel | test.c:20:3:20:49 | memory_order_acq_rel | test.c:20:3:20:49 | memory_order_acq_rel | Invalid memory order '$@' in call to function '$@'. | memory_order_acq_rel | memory_order_acq_rel | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:21:3:21:49 | memory_order_release | test.c:21:3:21:49 | memory_order_release | test.c:21:3:21:49 | memory_order_release | Invalid memory order '$@' in call to function '$@'. | memory_order_release | memory_order_release | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:54:33:54:34 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:54:33:54:34 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_store_explicit | +| test.c:55:29:55:30 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:55:29:55:30 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_load_explicit | +| test.c:56:42:56:43 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:56:42:56:43 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_flag_test_and_set_explicit | +| test.c:57:35:57:36 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:57:35:57:36 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_flag_clear_explicit | +| test.c:58:36:58:37 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:58:36:58:37 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_exchange_explicit | +| test.c:59:56:59:57 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:59:56:59:57 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_strong_explicit | +| test.c:60:60:60:61 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:60:60:60:61 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_strong_explicit | +| test.c:61:54:61:55 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:61:54:61:55 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_weak_explicit | +| test.c:62:58:62:59 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:62:58:62:59 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_compare_exchange_weak_explicit | +| test.c:63:37:63:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:63:37:63:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_add_explicit | +| test.c:64:37:64:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:64:37:64:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_sub_explicit | +| test.c:65:36:65:37 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:65:36:65:37 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_or_explicit | +| test.c:66:37:66:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:66:37:66:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_xor_explicit | +| test.c:67:37:67:38 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:67:37:67:38 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_fetch_and_explicit | +| test.c:68:23:68:24 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:68:23:68:24 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:69:23:69:24 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:69:23:69:24 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_signal_fence | +| test.c:72:23:72:24 | g2 | test.c:4:10:4:29 | memory_order_relaxed | test.c:72:23:72:24 | g2 | Invalid memory order '$@' in call to function '$@'. | memory_order_relaxed | memory_order_relaxed | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:73:23:73:24 | g3 | test.c:5:10:5:29 | memory_order_acquire | test.c:73:23:73:24 | g3 | Invalid memory order '$@' in call to function '$@'. | memory_order_acquire | memory_order_acquire | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:74:23:74:24 | g4 | test.c:6:10:6:29 | memory_order_consume | test.c:74:23:74:24 | g4 | Invalid memory order '$@' in call to function '$@'. | memory_order_consume | memory_order_consume | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:75:23:75:24 | g5 | test.c:7:10:7:29 | memory_order_acq_rel | test.c:75:23:75:24 | g5 | Invalid memory order '$@' in call to function '$@'. | memory_order_acq_rel | memory_order_acq_rel | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:76:23:76:24 | g6 | test.c:8:10:8:29 | memory_order_release | test.c:76:23:76:24 | g6 | Invalid memory order '$@' in call to function '$@'. | memory_order_release | memory_order_release | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:80:23:80:23 | 1 | test.c:80:23:80:23 | 1 | test.c:80:23:80:23 | 1 | Invalid memory order '$@' in call to function '$@'. | memory_order_consume | memory_order_consume | Invocation of a standard function implemented as a macro | atomic_thread_fence | +| test.c:81:23:81:25 | 100 | test.c:81:23:81:25 | 100 | test.c:81:23:81:25 | 100 | Invalid memory order '$@' in call to function '$@'. | 100 | 100 | Invocation of a standard function implemented as a macro | atomic_thread_fence | diff --git a/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.qlref b/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.qlref new file mode 100644 index 0000000000..5c205adc24 --- /dev/null +++ b/c/misra/test/rules/RULE-21-25/InvalidMemoryOrderArgument.qlref @@ -0,0 +1 @@ +rules/RULE-21-25/InvalidMemoryOrderArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-25/test.c b/c/misra/test/rules/RULE-21-25/test.c new file mode 100644 index 0000000000..d096634d30 --- /dev/null +++ b/c/misra/test/rules/RULE-21-25/test.c @@ -0,0 +1,86 @@ +#include + +int g1 = memory_order_seq_cst; +int g2 = memory_order_relaxed; +int g3 = memory_order_acquire; +int g4 = memory_order_consume; +int g5 = memory_order_acq_rel; +int g6 = memory_order_release; +int *ptr; + +void f(int p) { + _Atomic int l1; + atomic_flag l2; + + // Directly specified values: + atomic_load_explicit(&l1, memory_order_seq_cst); // COMPLIANT + atomic_load_explicit(&l1, memory_order_relaxed); // NON-COMPLIANT + atomic_load_explicit(&l1, memory_order_acquire); // NON-COMPLIANT + atomic_load_explicit(&l1, memory_order_consume); // NON-COMPLIANT + atomic_load_explicit(&l1, memory_order_acq_rel); // NON-COMPLIANT + atomic_load_explicit(&l1, memory_order_release); // NON-COMPLIANT + + // Implicit values: + atomic_store(&l1, 0); // COMPLIANT + atomic_load(&l1); // COMPLIANT + atomic_flag_test_and_set(&l2); // COMPLIANT + atomic_flag_clear(&l2); // COMPLIANT + atomic_exchange(&l1, 0); // COMPLIANT + atomic_compare_exchange_strong(&l1, ptr, 1); // COMPLIANT + atomic_compare_exchange_weak(&l1, ptr, 1); // COMPLIANT + atomic_fetch_add(&l1, 0); // COMPLIANT + atomic_fetch_sub(&l1, 0); // COMPLIANT + atomic_fetch_or(&l1, 0); // COMPLIANT + atomic_fetch_xor(&l1, 0); // COMPLIANT + atomic_fetch_and(&l1, 0); // COMPLIANT + + // Compliant flowed values (one test per sink): + atomic_store_explicit(&l1, 0, g1); // COMPLIANT + atomic_load_explicit(&l1, g1); // COMPLIANT + atomic_flag_test_and_set_explicit(&l2, g1); // COMPLIANT + atomic_flag_clear_explicit(&l2, g1); // COMPLIANT + atomic_exchange_explicit(&l1, 0, g1); // COMPLIANT + atomic_compare_exchange_strong_explicit(&l1, ptr, 1, g1, g1); // COMPLIANT + atomic_compare_exchange_weak_explicit(&l1, ptr, 1, g1, g1); // COMPLIANT + atomic_fetch_add_explicit(&l1, 0, g1); // COMPLIANT + atomic_fetch_sub_explicit(&l1, 0, g1); // COMPLIANT + atomic_fetch_or_explicit(&l1, 0, g1); // COMPLIANT + atomic_fetch_xor_explicit(&l1, 0, g1); // COMPLIANT + atomic_fetch_and_explicit(&l1, 0, g1); // COMPLIANT + atomic_thread_fence(g1); // COMPLIANT + atomic_signal_fence(g1); // COMPLIANT + + // Non-compliant flowed values (one test per sink): + atomic_store_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_load_explicit(&l1, g2); // NON-COMPLIANT + atomic_flag_test_and_set_explicit(&l2, g2); // NON-COMPLIANT + atomic_flag_clear_explicit(&l2, g2); // NON-COMPLIANT + atomic_exchange_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_compare_exchange_strong_explicit(&l1, ptr, 1, g2, g1); // NON-COMPLIANT + atomic_compare_exchange_strong_explicit(&l1, ptr, 1, g1, g2); // NON-COMPLIANT + atomic_compare_exchange_weak_explicit(&l1, ptr, 1, g2, g1); // NON-COMPLIANT + atomic_compare_exchange_weak_explicit(&l1, ptr, 1, g1, g2); // NON-COMPLIANT + atomic_fetch_add_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_fetch_sub_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_fetch_or_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_fetch_xor_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_fetch_and_explicit(&l1, 0, g2); // NON-COMPLIANT + atomic_thread_fence(g2); // NON-COMPLIANT + atomic_signal_fence(g2); // NON-COMPLIANT + + // Non-compliant flowed values (one test per source): + atomic_thread_fence(g2); // NON-COMPLIANT + atomic_thread_fence(g3); // NON-COMPLIANT + atomic_thread_fence(g4); // NON-COMPLIANT + atomic_thread_fence(g5); // NON-COMPLIANT + atomic_thread_fence(g6); // NON-COMPLIANT + + // Computed flow sources: + atomic_thread_fence(memory_order_seq_cst * 1); // COMPLIANT + atomic_thread_fence(1); // NON-COMPLIANT + atomic_thread_fence(100); // NON-COMPLIANT + atomic_thread_fence(g1 + 1); // NON_COMPLIANT[FALSE_NEGATIVE] + + // No unsafe flow, currently accepted: + atomic_thread_fence(p); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-26/TimedlockOnInappropriateMutexType.expected b/c/misra/test/rules/RULE-21-26/TimedlockOnInappropriateMutexType.expected new file mode 100644 index 0000000000..0a4c0a496a --- /dev/null +++ b/c/misra/test/rules/RULE-21-26/TimedlockOnInappropriateMutexType.expected @@ -0,0 +1,45 @@ +edges +| test.c:10:24:10:24 | *m | test.c:10:43:10:43 | *m | provenance | | +| test.c:13:12:13:14 | mtx_init output argument | test.c:14:17:14:19 | *& ... | provenance | | +| test.c:13:12:13:14 | mtx_init output argument | test.c:15:14:15:16 | *& ... | provenance | | +| test.c:15:14:15:16 | *& ... | test.c:10:24:10:24 | *m | provenance | | +| test.c:17:12:17:14 | mtx_init output argument | test.c:18:17:18:19 | *& ... | provenance | | +| test.c:17:12:17:14 | mtx_init output argument | test.c:19:14:19:16 | *& ... | provenance | | +| test.c:19:14:19:16 | *& ... | test.c:10:24:10:24 | *m | provenance | | +| test.c:30:12:30:14 | mtx_init output argument | test.c:31:17:31:19 | *& ... | provenance | | +| test.c:30:12:30:14 | mtx_init output argument | test.c:32:14:32:16 | *& ... | provenance | | +| test.c:32:14:32:16 | *& ... | test.c:10:24:10:24 | *m | provenance | | +| test.c:42:12:42:16 | mtx_init output argument | test.c:42:13:42:14 | *l3 [post update] [m] | provenance | | +| test.c:42:13:42:14 | *l3 [post update] [m] | test.c:43:18:43:19 | *l3 [m] | provenance | | +| test.c:42:13:42:14 | *l3 [post update] [m] | test.c:44:15:44:16 | *l3 [m] | provenance | | +| test.c:43:18:43:19 | *l3 [m] | test.c:43:17:43:21 | *& ... | provenance | | +| test.c:44:14:44:18 | *& ... | test.c:10:24:10:24 | *m | provenance | | +| test.c:44:15:44:16 | *l3 [m] | test.c:44:14:44:18 | *& ... | provenance | | +nodes +| test.c:10:24:10:24 | *m | semmle.label | *m | +| test.c:10:43:10:43 | *m | semmle.label | *m | +| test.c:13:12:13:14 | mtx_init output argument | semmle.label | mtx_init output argument | +| test.c:14:17:14:19 | *& ... | semmle.label | *& ... | +| test.c:15:14:15:16 | *& ... | semmle.label | *& ... | +| test.c:17:12:17:14 | mtx_init output argument | semmle.label | mtx_init output argument | +| test.c:18:17:18:19 | *& ... | semmle.label | *& ... | +| test.c:19:14:19:16 | *& ... | semmle.label | *& ... | +| test.c:30:12:30:14 | mtx_init output argument | semmle.label | mtx_init output argument | +| test.c:31:17:31:19 | *& ... | semmle.label | *& ... | +| test.c:32:14:32:16 | *& ... | semmle.label | *& ... | +| test.c:42:12:42:16 | mtx_init output argument | semmle.label | mtx_init output argument | +| test.c:42:13:42:14 | *l3 [post update] [m] | semmle.label | *l3 [post update] [m] | +| test.c:43:17:43:21 | *& ... | semmle.label | *& ... | +| test.c:43:18:43:19 | *l3 [m] | semmle.label | *l3 [m] | +| test.c:44:14:44:18 | *& ... | semmle.label | *& ... | +| test.c:44:15:44:16 | *l3 [m] | semmle.label | *l3 [m] | +subpaths +#select +| test.c:10:43:10:43 | *m | test.c:13:12:13:14 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:13:12:13:14 | mtx_init output argument | initialized | +| test.c:10:43:10:43 | *m | test.c:17:12:17:14 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:17:12:17:14 | mtx_init output argument | initialized | +| test.c:10:43:10:43 | *m | test.c:30:12:30:14 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:30:12:30:14 | mtx_init output argument | initialized | +| test.c:10:43:10:43 | *m | test.c:42:12:42:16 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:42:12:42:16 | mtx_init output argument | initialized | +| test.c:14:17:14:19 | *& ... | test.c:13:12:13:14 | mtx_init output argument | test.c:14:17:14:19 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:13:12:13:14 | mtx_init output argument | initialized | +| test.c:18:17:18:19 | *& ... | test.c:17:12:17:14 | mtx_init output argument | test.c:18:17:18:19 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:17:12:17:14 | mtx_init output argument | initialized | +| test.c:31:17:31:19 | *& ... | test.c:30:12:30:14 | mtx_init output argument | test.c:31:17:31:19 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:30:12:30:14 | mtx_init output argument | initialized | +| test.c:43:17:43:21 | *& ... | test.c:42:12:42:16 | mtx_init output argument | test.c:43:17:43:21 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:42:12:42:16 | mtx_init output argument | initialized | diff --git a/c/misra/test/rules/RULE-21-26/TimedlockOnInappropriateMutexType.qlref b/c/misra/test/rules/RULE-21-26/TimedlockOnInappropriateMutexType.qlref new file mode 100644 index 0000000000..9ffe7e7494 --- /dev/null +++ b/c/misra/test/rules/RULE-21-26/TimedlockOnInappropriateMutexType.qlref @@ -0,0 +1 @@ +rules/RULE-21-26/TimedlockOnInappropriateMutexType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-26/test.c b/c/misra/test/rules/RULE-21-26/test.c new file mode 100644 index 0000000000..b08f7e54a1 --- /dev/null +++ b/c/misra/test/rules/RULE-21-26/test.c @@ -0,0 +1,45 @@ +#include "threads.h" + +mtx_t g1; +mtx_t g2; +mtx_t g3; +mtx_t g4; + +struct timespec ts = {0, 0}; + +void doTimeLock(mtx_t *m) { mtx_timedlock(m, &ts); } + +int main(int argc, char *argv[]) { + mtx_init(&g1, mtx_plain); + mtx_timedlock(&g1, &ts); // NON-COMPLIANT + doTimeLock(&g1); // NON-COMPLIANT + + mtx_init(&g2, mtx_plain | mtx_recursive); + mtx_timedlock(&g2, &ts); // NON-COMPLIANT + doTimeLock(&g2); // NON-COMPLIANT + + mtx_init(&g3, mtx_timed); + mtx_timedlock(&g3, &ts); // COMPLIANT + doTimeLock(&g3); // COMPLIANT + + mtx_init(&g4, mtx_timed | mtx_recursive); + mtx_timedlock(&g4, &ts); // COMPLIANT + doTimeLock(&g4); // COMPLIANT + + mtx_t l1; + mtx_init(&l1, mtx_plain); + mtx_timedlock(&l1, &ts); // NON-COMPLIANT + doTimeLock(&l1); // NON-COMPLIANT + + mtx_t l2; + mtx_init(&l2, mtx_timed); + mtx_timedlock(&l2, &ts); // COMPLIANT + doTimeLock(&l2); // COMPLIANT + + struct s { + mtx_t m; + } l3; + mtx_init(&l3.m, mtx_plain); + mtx_timedlock(&l3.m, &ts); // NON-COMPLIANT + doTimeLock(&l3.m); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.expected index 0215c2e5b8..e9ea6daecc 100644 --- a/c/misra/test/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-3/MemoryAllocDeallocFunctionsOfStdlibhUsed.expected @@ -1,5 +1,5 @@ -| test.c:8:15:8:20 | call to malloc | Use of banned dynamic memory allocation. | -| test.c:9:15:9:20 | call to calloc | Use of banned dynamic memory allocation. | -| test.c:10:8:10:14 | call to realloc | Use of banned dynamic memory allocation. | -| test.c:11:3:11:6 | call to free | Use of banned dynamic memory deallocation. | -| test.c:12:3:12:6 | call to free | Use of banned dynamic memory deallocation. | +| test.c:13:15:13:20 | call to malloc | Use of banned dynamic memory allocation. | +| test.c:14:15:14:20 | call to calloc | Use of banned dynamic memory allocation. | +| test.c:15:8:15:14 | call to realloc | Use of banned dynamic memory allocation. | +| test.c:16:3:16:6 | call to free | Use of banned dynamic memory deallocation. | +| test.c:17:3:17:6 | call to free | Use of banned dynamic memory deallocation. | diff --git a/c/misra/test/rules/RULE-21-3/test.c b/c/misra/test/rules/RULE-21-3/test.c index d9aee3a322..fd4543faaf 100644 --- a/c/misra/test/rules/RULE-21-3/test.c +++ b/c/misra/test/rules/RULE-21-3/test.c @@ -1,3 +1,8 @@ +// Note: A subset of these cases are also tested in c/misra/test/rules/RULE-1-5 +// via a MemoryAllocDeallocFunctionsOfStdlibhUsed.qlref and .expected file in +// that directory. Changes to these tests may require updating the test code or +// expectations in that directory as well. + #include #include void f2(); diff --git a/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected index aa28e9264f..bddea12878 100644 --- a/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected +++ b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected @@ -1,2 +1,2 @@ -| test.c:7:7:7:12 | setjmp | Use of $@. | setjmp.h:33:1:33:21 | #define setjmp setjmp | setjmp | -| test.c:8:3:8:9 | call to longjmp | Use of $@. | setjmp.h:31:16:31:22 | longjmp | longjmp | +| test.c:7:7:7:12 | setjmp | Use of setjmp. | +| test.c:8:3:8:9 | call to longjmp | Use of longjmp. | diff --git a/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.clang b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.clang new file mode 100644 index 0000000000..9061834c4d --- /dev/null +++ b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.clang @@ -0,0 +1,2 @@ +| test.c:7:7:7:17 | setjmp(env) | Use of setjmp. | +| test.c:8:3:8:9 | call to longjmp | Use of longjmp. | diff --git a/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.gcc b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.gcc new file mode 100644 index 0000000000..9061834c4d --- /dev/null +++ b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.gcc @@ -0,0 +1,2 @@ +| test.c:7:7:7:17 | setjmp(env) | Use of setjmp. | +| test.c:8:3:8:9 | call to longjmp | Use of longjmp. | diff --git a/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.qcc b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.qcc new file mode 100644 index 0000000000..c72d7d4e20 --- /dev/null +++ b/c/misra/test/rules/RULE-21-4/StandardHeaderFileUsedSetjmph.expected.qcc @@ -0,0 +1,2 @@ +| test.c:7:7:7:17 | setjmp(__env) | Use of setjmp. | +| test.c:8:3:8:17 | longjmp(__env,__val) | Use of longjmp. | diff --git a/c/misra/test/rules/RULE-21-4/setjmp.h b/c/misra/test/rules/RULE-21-4/setjmp.h deleted file mode 100644 index deb63fdd6e..0000000000 --- a/c/misra/test/rules/RULE-21-4/setjmp.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _SETJMP_H -#define _SETJMP_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "features.h" - -#include "bits/setjmp.h" - -typedef struct __jmp_buf_tag { - __jmp_buf __jb; - unsigned long __fl; - unsigned long __ss[128 / sizeof(long)]; -} jmp_buf[1]; - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ - defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -typedef jmp_buf sigjmp_buf; -int sigsetjmp(sigjmp_buf, int); -_Noreturn void siglongjmp(sigjmp_buf, int); -#endif - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -int _setjmp(jmp_buf); -_Noreturn void _longjmp(jmp_buf, int); -#endif - -int setjmp(jmp_buf); -_Noreturn void longjmp(jmp_buf, int); - -#define setjmp setjmp - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-4/test.c b/c/misra/test/rules/RULE-21-4/test.c index c7de20b818..8ac53736bf 100644 --- a/c/misra/test/rules/RULE-21-4/test.c +++ b/c/misra/test/rules/RULE-21-4/test.c @@ -1,4 +1,4 @@ -#include "setjmp.h" +#include void f1() { jmp_buf env; // COMPLIANT - Assumption of features outlined in rule is diff --git a/c/misra/test/rules/RULE-21-5/StandardHeaderFileUsedSignalh.expected b/c/misra/test/rules/RULE-21-5/StandardHeaderFileUsedSignalh.expected index 3aba88b3aa..0e0fb8b904 100644 --- a/c/misra/test/rules/RULE-21-5/StandardHeaderFileUsedSignalh.expected +++ b/c/misra/test/rules/RULE-21-5/StandardHeaderFileUsedSignalh.expected @@ -1,2 +1,2 @@ -| test.c:4:7:4:12 | call to signal | Call to banned function $@. | signal.h:282:8:282:13 | signal | signal | -| test.c:6:7:6:11 | call to raise | Call to banned function $@. | signal.h:283:5:283:9 | raise | raise | +| test.c:4:7:4:12 | call to signal | Call to banned function signal. | +| test.c:6:7:6:11 | call to raise | Call to banned function raise. | diff --git a/c/misra/test/rules/RULE-21-5/signal.h b/c/misra/test/rules/RULE-21-5/signal.h deleted file mode 100644 index 8ac0aab063..0000000000 --- a/c/misra/test/rules/RULE-21-5/signal.h +++ /dev/null @@ -1,296 +0,0 @@ -#ifndef _SIGNAL_H -#define _SIGNAL_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ - defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) - -#ifdef _GNU_SOURCE -#define __ucontext ucontext -#endif - -#define __NEED_size_t -#define __NEED_pid_t -#define __NEED_uid_t -#define __NEED_struct_timespec -#define __NEED_pthread_t -#define __NEED_pthread_attr_t -#define __NEED_time_t -#define __NEED_clock_t -#define __NEED_sigset_t - -#include - -#define SIG_BLOCK 0 -#define SIG_UNBLOCK 1 -#define SIG_SETMASK 2 - -#define SI_ASYNCNL (-60) -#define SI_TKILL (-6) -#define SI_SIGIO (-5) -#define SI_ASYNCIO (-4) -#define SI_MESGQ (-3) -#define SI_TIMER (-2) -#define SI_QUEUE (-1) -#define SI_USER 0 -#define SI_KERNEL 128 - -typedef struct sigaltstack stack_t; - -#endif - -#include - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ - defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) - -#define SIG_HOLD ((void (*)(int))2) - -#define FPE_INTDIV 1 -#define FPE_INTOVF 2 -#define FPE_FLTDIV 3 -#define FPE_FLTOVF 4 -#define FPE_FLTUND 5 -#define FPE_FLTRES 6 -#define FPE_FLTINV 7 -#define FPE_FLTSUB 8 - -#define ILL_ILLOPC 1 -#define ILL_ILLOPN 2 -#define ILL_ILLADR 3 -#define ILL_ILLTRP 4 -#define ILL_PRVOPC 5 -#define ILL_PRVREG 6 -#define ILL_COPROC 7 -#define ILL_BADSTK 8 - -#define SEGV_MAPERR 1 -#define SEGV_ACCERR 2 -#define SEGV_BNDERR 3 -#define SEGV_PKUERR 4 - -#define BUS_ADRALN 1 -#define BUS_ADRERR 2 -#define BUS_OBJERR 3 -#define BUS_MCEERR_AR 4 -#define BUS_MCEERR_AO 5 - -#define CLD_EXITED 1 -#define CLD_KILLED 2 -#define CLD_DUMPED 3 -#define CLD_TRAPPED 4 -#define CLD_STOPPED 5 -#define CLD_CONTINUED 6 - -union sigval { - int sival_int; - void *sival_ptr; -}; - -typedef struct { -#ifdef __SI_SWAP_ERRNO_CODE - int si_signo, si_code, si_errno; -#else - int si_signo, si_errno, si_code; -#endif - union { - char __pad[128 - 2 * sizeof(int) - sizeof(long)]; - struct { - union { - struct { - pid_t si_pid; - uid_t si_uid; - } __piduid; - struct { - int si_timerid; - int si_overrun; - } __timer; - } __first; - union { - union sigval si_value; - struct { - int si_status; - clock_t si_utime, si_stime; - } __sigchld; - } __second; - } __si_common; - struct { - void *si_addr; - short si_addr_lsb; - union { - struct { - void *si_lower; - void *si_upper; - } __addr_bnd; - unsigned si_pkey; - } __first; - } __sigfault; - struct { - long si_band; - int si_fd; - } __sigpoll; - struct { - void *si_call_addr; - int si_syscall; - unsigned si_arch; - } __sigsys; - } __si_fields; -} siginfo_t; -#define si_pid __si_fields.__si_common.__first.__piduid.si_pid -#define si_uid __si_fields.__si_common.__first.__piduid.si_uid -#define si_status __si_fields.__si_common.__second.__sigchld.si_status -#define si_utime __si_fields.__si_common.__second.__sigchld.si_utime -#define si_stime __si_fields.__si_common.__second.__sigchld.si_stime -#define si_value __si_fields.__si_common.__second.si_value -#define si_addr __si_fields.__sigfault.si_addr -#define si_addr_lsb __si_fields.__sigfault.si_addr_lsb -#define si_lower __si_fields.__sigfault.__first.__addr_bnd.si_lower -#define si_upper __si_fields.__sigfault.__first.__addr_bnd.si_upper -#define si_pkey __si_fields.__sigfault.__first.si_pkey -#define si_band __si_fields.__sigpoll.si_band -#define si_fd __si_fields.__sigpoll.si_fd -#define si_timerid __si_fields.__si_common.__first.__timer.si_timerid -#define si_overrun __si_fields.__si_common.__first.__timer.si_overrun -#define si_ptr si_value.sival_ptr -#define si_int si_value.sival_int -#define si_call_addr __si_fields.__sigsys.si_call_addr -#define si_syscall __si_fields.__sigsys.si_syscall -#define si_arch __si_fields.__sigsys.si_arch - -struct sigaction { - union { - void (*sa_handler)(int); - void (*sa_sigaction)(int, siginfo_t *, void *); - } __sa_handler; - sigset_t sa_mask; - int sa_flags; - void (*sa_restorer)(void); -}; -#define sa_handler __sa_handler.sa_handler -#define sa_sigaction __sa_handler.sa_sigaction - -struct sigevent { - union sigval sigev_value; - int sigev_signo; - int sigev_notify; - union { - char __pad[64 - 2 * sizeof(int) - sizeof(union sigval)]; - pid_t sigev_notify_thread_id; - struct { - void (*sigev_notify_function)(union sigval); - pthread_attr_t *sigev_notify_attributes; - } __sev_thread; - } __sev_fields; -}; - -#define sigev_notify_thread_id __sev_fields.sigev_notify_thread_id -#define sigev_notify_function __sev_fields.__sev_thread.sigev_notify_function -#define sigev_notify_attributes \ - __sev_fields.__sev_thread.sigev_notify_attributes - -#define SIGEV_SIGNAL 0 -#define SIGEV_NONE 1 -#define SIGEV_THREAD 2 -#define SIGEV_THREAD_ID 4 - -int __libc_current_sigrtmin(void); -int __libc_current_sigrtmax(void); - -#define SIGRTMIN (__libc_current_sigrtmin()) -#define SIGRTMAX (__libc_current_sigrtmax()) - -int kill(pid_t, int); - -int sigemptyset(sigset_t *); -int sigfillset(sigset_t *); -int sigaddset(sigset_t *, int); -int sigdelset(sigset_t *, int); -int sigismember(const sigset_t *, int); - -int sigprocmask(int, const sigset_t *__restrict, sigset_t *__restrict); -int sigsuspend(const sigset_t *); -int sigaction(int, const struct sigaction *__restrict, - struct sigaction *__restrict); -int sigpending(sigset_t *); -int sigwait(const sigset_t *__restrict, int *__restrict); -int sigwaitinfo(const sigset_t *__restrict, siginfo_t *__restrict); -int sigtimedwait(const sigset_t *__restrict, siginfo_t *__restrict, - const struct timespec *__restrict); -int sigqueue(pid_t, int, union sigval); - -int pthread_sigmask(int, const sigset_t *__restrict, sigset_t *__restrict); -int pthread_kill(pthread_t, int); - -void psiginfo(const siginfo_t *, const char *); -void psignal(int, const char *); - -#endif - -#if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE) -int killpg(pid_t, int); -int sigaltstack(const stack_t *__restrict, stack_t *__restrict); -int sighold(int); -int sigignore(int); -int siginterrupt(int, int); -int sigpause(int); -int sigrelse(int); -void (*sigset(int, void (*)(int)))(int); -#define TRAP_BRKPT 1 -#define TRAP_TRACE 2 -#define TRAP_BRANCH 3 -#define TRAP_HWBKPT 4 -#define TRAP_UNK 5 -#define POLL_IN 1 -#define POLL_OUT 2 -#define POLL_MSG 3 -#define POLL_ERR 4 -#define POLL_PRI 5 -#define POLL_HUP 6 -#define SS_ONSTACK 1 -#define SS_DISABLE 2 -#define SS_AUTODISARM (1U << 31) -#define SS_FLAG_BITS SS_AUTODISARM -#endif - -#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) -#define NSIG _NSIG -typedef void (*sig_t)(int); -#endif - -#ifdef _GNU_SOURCE -typedef void (*sighandler_t)(int); -void (*bsd_signal(int, void (*)(int)))(int); -int sigisemptyset(const sigset_t *); -int sigorset(sigset_t *, const sigset_t *, const sigset_t *); -int sigandset(sigset_t *, const sigset_t *, const sigset_t *); - -#define SA_NOMASK SA_NODEFER -#define SA_ONESHOT SA_RESETHAND -#endif - -#define SIG_ERR ((void (*)(int)) - 1) -#define SIG_DFL ((void (*)(int))0) -#define SIG_IGN ((void (*)(int))1) - -typedef int sig_atomic_t; - -void (*signal(int, void (*)(int)))(int); -int raise(int); - -#if _REDIR_TIME64 -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ - defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -__REDIR(sigtimedwait, __sigtimedwait_time64); -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-5/test.c b/c/misra/test/rules/RULE-21-5/test.c index 0516c9d6ad..7d325b73c7 100644 --- a/c/misra/test/rules/RULE-21-5/test.c +++ b/c/misra/test/rules/RULE-21-5/test.c @@ -1,4 +1,4 @@ -#include "signal.h" +#include static void catch_function(int p1) {} void f1(void) { if (signal(SIGINT, catch_function) == SIG_ERR) { // NON_COMPLIANT diff --git a/c/misra/test/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.expected b/c/misra/test/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.expected index a818731be0..672480db33 100644 --- a/c/misra/test/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.expected +++ b/c/misra/test/rules/RULE-21-6/StandardLibraryInputoutputFunctionsUsed.expected @@ -1,7 +1,7 @@ -| test.c:8:10:8:14 | call to scanf | Call to banned function $@. | stdio.h:117:5:117:9 | scanf | scanf | -| test.c:9:5:9:10 | call to printf | Call to banned function $@. | stdio.h:107:5:107:10 | printf | printf | -| test.c:16:16:16:21 | call to fgetwc | Call to banned function $@. | wchar.h:125:8:125:13 | fgetwc | fgetwc | -| test.c:17:5:17:12 | call to putwchar | Call to banned function $@. | wchar.h:131:8:131:15 | putwchar | putwchar | -| test.c:22:7:22:10 | call to puts | Call to banned function $@. | stdio.h:105:5:105:8 | puts | puts | -| test.c:24:7:24:10 | call to puts | Call to banned function $@. | stdio.h:105:5:105:8 | puts | puts | -| test.c:26:5:26:8 | call to puts | Call to banned function $@. | stdio.h:105:5:105:8 | puts | puts | +| test.c:13:10:13:14 | call to scanf | Call to banned function scanf. | +| test.c:14:5:14:10 | call to printf | Call to banned function printf. | +| test.c:21:16:21:21 | call to fgetwc | Call to banned function fgetwc. | +| test.c:22:5:22:12 | call to putwchar | Call to banned function putwchar. | +| test.c:27:7:27:10 | call to puts | Call to banned function puts. | +| test.c:29:7:29:10 | call to puts | Call to banned function puts. | +| test.c:31:5:31:8 | call to puts | Call to banned function puts. | diff --git a/c/misra/test/rules/RULE-21-6/stdio.h b/c/misra/test/rules/RULE-21-6/stdio.h deleted file mode 100644 index 3604198c3e..0000000000 --- a/c/misra/test/rules/RULE-21-6/stdio.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef _STDIO_H -#define _STDIO_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#define __NEED_FILE -#define __NEED___isoc_va_list -#define __NEED_size_t - -#if __STDC_VERSION__ < 201112L -#define __NEED_struct__IO_FILE -#endif - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -#define __NEED_ssize_t -#define __NEED_off_t -#define __NEED_va_list -#endif - -#include - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - -#undef EOF -#define EOF (-1) - -#undef SEEK_SET -#undef SEEK_CUR -#undef SEEK_END -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 - -#define _IOFBF 0 -#define _IOLBF 1 -#define _IONBF 2 - -#define BUFSIZ 1024 -#define FILENAME_MAX 4096 -#define FOPEN_MAX 1000 -#define TMP_MAX 10000 -#define L_tmpnam 20 - -typedef union _G_fpos64_t { - char __opaque[16]; - long long __lldata; - double __align; -} fpos_t; - -extern FILE *const stdin; -extern FILE *const stdout; -extern FILE *const stderr; - -#define stdin (stdin) -#define stdout (stdout) -#define stderr (stderr) - -FILE *fopen(const char *__restrict, const char *__restrict); -FILE *freopen(const char *__restrict, const char *__restrict, FILE *__restrict); -int fclose(FILE *); - -int remove(const char *); -int rename(const char *, const char *); - -int feof(FILE *); -int ferror(FILE *); -int fflush(FILE *); -void clearerr(FILE *); - -int fseek(FILE *, long, int); -long ftell(FILE *); -void rewind(FILE *); - -int fgetpos(FILE *__restrict, fpos_t *__restrict); -int fsetpos(FILE *, const fpos_t *); - -size_t fread(void *__restrict, size_t, size_t, FILE *__restrict); -size_t fwrite(const void *__restrict, size_t, size_t, FILE *__restrict); - -int fgetc(FILE *); -int getc(FILE *); -int getchar(void); -int ungetc(int, FILE *); - -int fputc(int, FILE *); -int putc(int, FILE *); -int putchar(int); - -char *fgets(char *__restrict, int, FILE *__restrict); -#if __STDC_VERSION__ < 201112L -char *gets(char *); -#endif - -int fputs(const char *__restrict, FILE *__restrict); -int puts(const char *); - -int printf(const char *__restrict, ...); -int fprintf(FILE *__restrict, const char *__restrict, ...); -int sprintf(char *__restrict, const char *__restrict, ...); -int snprintf(char *__restrict, size_t, const char *__restrict, ...); - -int vprintf(const char *__restrict, __isoc_va_list); -int vfprintf(FILE *__restrict, const char *__restrict, __isoc_va_list); -int vsprintf(char *__restrict, const char *__restrict, __isoc_va_list); -int vsnprintf(char *__restrict, size_t, const char *__restrict, __isoc_va_list); - -int scanf(const char *__restrict, ...); -int fscanf(FILE *__restrict, const char *__restrict, ...); -int sscanf(const char *__restrict, const char *__restrict, ...); -int vscanf(const char *__restrict, __isoc_va_list); -int vfscanf(FILE *__restrict, const char *__restrict, __isoc_va_list); -int vsscanf(const char *__restrict, const char *__restrict, __isoc_va_list); - -void perror(const char *); - -int setvbuf(FILE *__restrict, char *__restrict, int, size_t); -void setbuf(FILE *__restrict, char *__restrict); - -char *tmpnam(char *); -FILE *tmpfile(void); - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -FILE *fmemopen(void *__restrict, size_t, const char *__restrict); -FILE *open_memstream(char **, size_t *); -FILE *fdopen(int, const char *); -FILE *popen(const char *, const char *); -int pclose(FILE *); -int fileno(FILE *); -int fseeko(FILE *, off_t, int); -off_t ftello(FILE *); -int dprintf(int, const char *__restrict, ...); -int vdprintf(int, const char *__restrict, __isoc_va_list); -void flockfile(FILE *); -int ftrylockfile(FILE *); -void funlockfile(FILE *); -int getc_unlocked(FILE *); -int getchar_unlocked(void); -int putc_unlocked(int, FILE *); -int putchar_unlocked(int); -ssize_t getdelim(char **__restrict, size_t *__restrict, int, FILE *__restrict); -ssize_t getline(char **__restrict, size_t *__restrict, FILE *__restrict); -int renameat(int, const char *, int, const char *); -char *ctermid(char *); -#define L_ctermid 20 -#endif - - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -#define P_tmpdir "/tmp" -char *tempnam(const char *, const char *); -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define L_cuserid 20 -char *cuserid(char *); -void setlinebuf(FILE *); -void setbuffer(FILE *, char *, size_t); -int fgetc_unlocked(FILE *); -int fputc_unlocked(int, FILE *); -int fflush_unlocked(FILE *); -size_t fread_unlocked(void *, size_t, size_t, FILE *); -size_t fwrite_unlocked(const void *, size_t, size_t, FILE *); -void clearerr_unlocked(FILE *); -int feof_unlocked(FILE *); -int ferror_unlocked(FILE *); -int fileno_unlocked(FILE *); -int getw(FILE *); -int putw(int, FILE *); -char *fgetln(FILE *, size_t *); -int asprintf(char **, const char *, ...); -int vasprintf(char **, const char *, __isoc_va_list); -#endif - -#ifdef _GNU_SOURCE -char *fgets_unlocked(char *, int, FILE *); -int fputs_unlocked(const char *, FILE *); - -typedef ssize_t (cookie_read_function_t)(void *, char *, size_t); -typedef ssize_t (cookie_write_function_t)(void *, const char *, size_t); -typedef int (cookie_seek_function_t)(void *, off_t *, int); -typedef int (cookie_close_function_t)(void *); - -typedef struct _IO_cookie_io_functions_t { - cookie_read_function_t *read; - cookie_write_function_t *write; - cookie_seek_function_t *seek; - cookie_close_function_t *close; -} cookie_io_functions_t; - -FILE *fopencookie(void *, const char *, cookie_io_functions_t); -#endif - -#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) -#define tmpfile64 tmpfile -#define fopen64 fopen -#define freopen64 freopen -#define fseeko64 fseeko -#define ftello64 ftello -#define fgetpos64 fgetpos -#define fsetpos64 fsetpos -#define fpos64_t fpos_t -#define off64_t off_t -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-6/test.c b/c/misra/test/rules/RULE-21-6/test.c index 7094891454..b66bb9b6b7 100644 --- a/c/misra/test/rules/RULE-21-6/test.c +++ b/c/misra/test/rules/RULE-21-6/test.c @@ -1,8 +1,13 @@ -#include "stdio.h" -#include "wchar.h" +// Note: A subset of these cases are also tested in c/misra/test/rules/RULE-1-5 +// via a StandardLibraryInputoutputFunctionsUsed.qlref and .expected file in +// that directory. Changes to these tests may require updating the test code or +// expectations in that directory as well. + #include #include +#include #include +#include void f1() { int n; while (scanf("%d", &n) == 1) // NON_COMPLIANT diff --git a/c/misra/test/rules/RULE-21-6/wchar.h b/c/misra/test/rules/RULE-21-6/wchar.h deleted file mode 100644 index 88eb55b18c..0000000000 --- a/c/misra/test/rules/RULE-21-6/wchar.h +++ /dev/null @@ -1,205 +0,0 @@ -#ifndef _WCHAR_H -#define _WCHAR_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#define __NEED_FILE -#define __NEED___isoc_va_list -#define __NEED_size_t -#define __NEED_wchar_t -#define __NEED_wint_t -#define __NEED_mbstate_t - -#if __STDC_VERSION__ < 201112L -#define __NEED_struct__IO_FILE -#endif - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define __NEED_locale_t -#define __NEED_va_list -#endif - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define __NEED_wctype_t -#endif - -#include - -#if L'\0'-1 > 0 -#define WCHAR_MAX (0xffffffffu+L'\0') -#define WCHAR_MIN (0+L'\0') -#else -#define WCHAR_MAX (0x7fffffff+L'\0') -#define WCHAR_MIN (-1-0x7fffffff+L'\0') -#endif - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - -#undef WEOF -#define WEOF 0xffffffffU - -wchar_t *wcscpy (wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcsncpy (wchar_t *__restrict, const wchar_t *__restrict, size_t); - -wchar_t *wcscat (wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcsncat (wchar_t *__restrict, const wchar_t *__restrict, size_t); - -int wcscmp (const wchar_t *, const wchar_t *); -int wcsncmp (const wchar_t *, const wchar_t *, size_t); - -int wcscoll(const wchar_t *, const wchar_t *); -size_t wcsxfrm (wchar_t *__restrict, const wchar_t *__restrict, size_t); - -wchar_t *wcschr (const wchar_t *, wchar_t); -wchar_t *wcsrchr (const wchar_t *, wchar_t); - -size_t wcscspn (const wchar_t *, const wchar_t *); -size_t wcsspn (const wchar_t *, const wchar_t *); -wchar_t *wcspbrk (const wchar_t *, const wchar_t *); - -wchar_t *wcstok (wchar_t *__restrict, const wchar_t *__restrict, wchar_t **__restrict); - -size_t wcslen (const wchar_t *); - -wchar_t *wcsstr (const wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcswcs (const wchar_t *, const wchar_t *); - -wchar_t *wmemchr (const wchar_t *, wchar_t, size_t); -int wmemcmp (const wchar_t *, const wchar_t *, size_t); -wchar_t *wmemcpy (wchar_t *__restrict, const wchar_t *__restrict, size_t); -wchar_t *wmemmove (wchar_t *, const wchar_t *, size_t); -wchar_t *wmemset (wchar_t *, wchar_t, size_t); - -wint_t btowc (int); -int wctob (wint_t); - -int mbsinit (const mbstate_t *); -size_t mbrtowc (wchar_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict); -size_t wcrtomb (char *__restrict, wchar_t, mbstate_t *__restrict); - -size_t mbrlen (const char *__restrict, size_t, mbstate_t *__restrict); - -size_t mbsrtowcs (wchar_t *__restrict, const char **__restrict, size_t, mbstate_t *__restrict); -size_t wcsrtombs (char *__restrict, const wchar_t **__restrict, size_t, mbstate_t *__restrict); - -float wcstof (const wchar_t *__restrict, wchar_t **__restrict); -double wcstod (const wchar_t *__restrict, wchar_t **__restrict); -long double wcstold (const wchar_t *__restrict, wchar_t **__restrict); - -long wcstol (const wchar_t *__restrict, wchar_t **__restrict, int); -unsigned long wcstoul (const wchar_t *__restrict, wchar_t **__restrict, int); - -long long wcstoll (const wchar_t *__restrict, wchar_t **__restrict, int); -unsigned long long wcstoull (const wchar_t *__restrict, wchar_t **__restrict, int); - - - -int fwide (FILE *, int); - - -int wprintf (const wchar_t *__restrict, ...); -int fwprintf (FILE *__restrict, const wchar_t *__restrict, ...); -int swprintf (wchar_t *__restrict, size_t, const wchar_t *__restrict, ...); - -int vwprintf (const wchar_t *__restrict, __isoc_va_list); -int vfwprintf (FILE *__restrict, const wchar_t *__restrict, __isoc_va_list); -int vswprintf (wchar_t *__restrict, size_t, const wchar_t *__restrict, __isoc_va_list); - -int wscanf (const wchar_t *__restrict, ...); -int fwscanf (FILE *__restrict, const wchar_t *__restrict, ...); -int swscanf (const wchar_t *__restrict, const wchar_t *__restrict, ...); - -int vwscanf (const wchar_t *__restrict, __isoc_va_list); -int vfwscanf (FILE *__restrict, const wchar_t *__restrict, __isoc_va_list); -int vswscanf (const wchar_t *__restrict, const wchar_t *__restrict, __isoc_va_list); - -wint_t fgetwc (FILE *); -wint_t getwc (FILE *); -wint_t getwchar (void); - -wint_t fputwc (wchar_t, FILE *); -wint_t putwc (wchar_t, FILE *); -wint_t putwchar (wchar_t); - -wchar_t *fgetws (wchar_t *__restrict, int, FILE *__restrict); -int fputws (const wchar_t *__restrict, FILE *__restrict); - -wint_t ungetwc (wint_t, FILE *); - -struct tm; -size_t wcsftime (wchar_t *__restrict, size_t, const wchar_t *__restrict, const struct tm *__restrict); - -#undef iswdigit - -#if defined(_GNU_SOURCE) -wint_t fgetwc_unlocked (FILE *); -wint_t getwc_unlocked (FILE *); -wint_t getwchar_unlocked (void); -wint_t fputwc_unlocked (wchar_t, FILE *); -wint_t putwc_unlocked (wchar_t, FILE *); -wint_t putwchar_unlocked (wchar_t); -wchar_t *fgetws_unlocked (wchar_t *__restrict, int, FILE *__restrict); -int fputws_unlocked (const wchar_t *__restrict, FILE *__restrict); -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -size_t wcsftime_l (wchar_t *__restrict, size_t, const wchar_t *__restrict, const struct tm *__restrict, locale_t); -#endif - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -FILE *open_wmemstream(wchar_t **, size_t *); -size_t mbsnrtowcs(wchar_t *__restrict, const char **__restrict, size_t, size_t, mbstate_t *__restrict); -size_t wcsnrtombs(char *__restrict, const wchar_t **__restrict, size_t, size_t, mbstate_t *__restrict); -wchar_t *wcsdup(const wchar_t *); -size_t wcsnlen (const wchar_t *, size_t); -wchar_t *wcpcpy (wchar_t *__restrict, const wchar_t *__restrict); -wchar_t *wcpncpy (wchar_t *__restrict, const wchar_t *__restrict, size_t); -int wcscasecmp(const wchar_t *, const wchar_t *); -int wcscasecmp_l(const wchar_t *, const wchar_t *, locale_t); -int wcsncasecmp(const wchar_t *, const wchar_t *, size_t); -int wcsncasecmp_l(const wchar_t *, const wchar_t *, size_t, locale_t); -int wcscoll_l(const wchar_t *, const wchar_t *, locale_t); -size_t wcsxfrm_l(wchar_t *__restrict, const wchar_t *__restrict, size_t, locale_t); -#endif - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -int wcwidth (wchar_t); -int wcswidth (const wchar_t *, size_t); -int iswalnum(wint_t); -int iswalpha(wint_t); -int iswblank(wint_t); -int iswcntrl(wint_t); -int iswdigit(wint_t); -int iswgraph(wint_t); -int iswlower(wint_t); -int iswprint(wint_t); -int iswpunct(wint_t); -int iswspace(wint_t); -int iswupper(wint_t); -int iswxdigit(wint_t); -int iswctype(wint_t, wctype_t); -wint_t towlower(wint_t); -wint_t towupper(wint_t); -wctype_t wctype(const char *); - -#ifndef __cplusplus -#undef iswdigit -#define iswdigit(a) (0 ? iswdigit(a) : ((unsigned)(a)-'0') < 10) -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.expected deleted file mode 100644 index a4ab0d6902..0000000000 --- a/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.expected +++ /dev/null @@ -1,4 +0,0 @@ -| test.c:6:14:6:17 | call to atof | Call to banned function $@. | stdlib.h:24:8:24:11 | atof | atof | -| test.c:7:12:7:15 | call to atoi | Call to banned function $@. | stdlib.h:21:5:21:8 | atoi | atoi | -| test.c:8:13:8:16 | call to atol | Call to banned function $@. | stdlib.h:22:6:22:9 | atol | atol | -| test.c:9:18:9:22 | call to atoll | Call to banned function $@. | stdlib.h:23:11:23:15 | atoll | atoll | diff --git a/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.qlref b/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.qlref deleted file mode 100644 index 52e70db92b..0000000000 --- a/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.testref b/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.testref new file mode 100644 index 0000000000..fccafa2049 --- /dev/null +++ b/c/misra/test/rules/RULE-21-7/AtofAtoiAtolAndAtollOfStdlibhUsed.testref @@ -0,0 +1 @@ +c/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-7/stdlib.h b/c/misra/test/rules/RULE-21-7/stdlib.h deleted file mode 100644 index b54a051fe9..0000000000 --- a/c/misra/test/rules/RULE-21-7/stdlib.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef _STDLIB_H -#define _STDLIB_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - -#define __NEED_size_t -#define __NEED_wchar_t - -#include - -int atoi (const char *); -long atol (const char *); -long long atoll (const char *); -double atof (const char *); - -float strtof (const char *__restrict, char **__restrict); -double strtod (const char *__restrict, char **__restrict); -long double strtold (const char *__restrict, char **__restrict); - -long strtol (const char *__restrict, char **__restrict, int); -unsigned long strtoul (const char *__restrict, char **__restrict, int); -long long strtoll (const char *__restrict, char **__restrict, int); -unsigned long long strtoull (const char *__restrict, char **__restrict, int); - -int rand (void); -void srand (unsigned); - -void *malloc (size_t); -void *calloc (size_t, size_t); -void *realloc (void *, size_t); -void free (void *); -void *aligned_alloc(size_t, size_t); - -_Noreturn void abort (void); -int atexit (void (*) (void)); -_Noreturn void exit (int); -_Noreturn void _Exit (int); -int at_quick_exit (void (*) (void)); -_Noreturn void quick_exit (int); - -char *getenv (const char *); - -int system (const char *); - -void *bsearch (const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); -void qsort (void *, size_t, size_t, int (*)(const void *, const void *)); - -int abs (int); -long labs (long); -long long llabs (long long); - -typedef struct { int quot, rem; } div_t; -typedef struct { long quot, rem; } ldiv_t; -typedef struct { long long quot, rem; } lldiv_t; - -div_t div (int, int); -ldiv_t ldiv (long, long); -lldiv_t lldiv (long long, long long); - -int mblen (const char *, size_t); -int mbtowc (wchar_t *__restrict, const char *__restrict, size_t); -int wctomb (char *, wchar_t); -size_t mbstowcs (wchar_t *__restrict, const char *__restrict, size_t); -size_t wcstombs (char *__restrict, const wchar_t *__restrict, size_t); - -#define EXIT_FAILURE 1 -#define EXIT_SUCCESS 0 - -size_t __ctype_get_mb_cur_max(void); -#define MB_CUR_MAX (__ctype_get_mb_cur_max()) - -#define RAND_MAX (0x7fffffff) - - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) - -#define WNOHANG 1 -#define WUNTRACED 2 - -#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) -#define WTERMSIG(s) ((s) & 0x7f) -#define WSTOPSIG(s) WEXITSTATUS(s) -#define WIFEXITED(s) (!WTERMSIG(s)) -#define WIFSTOPPED(s) ((short)((((s)&0xffff)*0x10001)>>8) > 0x7f00) -#define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu) - -int posix_memalign (void **, size_t, size_t); -int setenv (const char *, const char *, int); -int unsetenv (const char *); -int mkstemp (char *); -int mkostemp (char *, int); -char *mkdtemp (char *); -int getsubopt (char **, char *const *, char **); -int rand_r (unsigned *); - -#endif - - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -char *realpath (const char *__restrict, char *__restrict); -long int random (void); -void srandom (unsigned int); -char *initstate (unsigned int, char *, size_t); -char *setstate (char *); -int putenv (char *); -int posix_openpt (int); -int grantpt (int); -int unlockpt (int); -char *ptsname (int); -char *l64a (long); -long a64l (const char *); -void setkey (const char *); -double drand48 (void); -double erand48 (unsigned short [3]); -long int lrand48 (void); -long int nrand48 (unsigned short [3]); -long mrand48 (void); -long jrand48 (unsigned short [3]); -void srand48 (long); -unsigned short *seed48 (unsigned short [3]); -void lcong48 (unsigned short [7]); -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#include -char *mktemp (char *); -int mkstemps (char *, int); -int mkostemps (char *, int, int); -void *valloc (size_t); -void *memalign(size_t, size_t); -int getloadavg(double *, int); -int clearenv(void); -#define WCOREDUMP(s) ((s) & 0x80) -#define WIFCONTINUED(s) ((s) == 0xffff) -void *reallocarray (void *, size_t, size_t); -#endif - -#ifdef _GNU_SOURCE -int ptsname_r(int, char *, size_t); -char *ecvt(double, int, int *, int *); -char *fcvt(double, int, int *, int *); -char *gcvt(double, int, char *); -char *secure_getenv(const char *); -struct __locale_struct; -float strtof_l(const char *__restrict, char **__restrict, struct __locale_struct *); -double strtod_l(const char *__restrict, char **__restrict, struct __locale_struct *); -long double strtold_l(const char *__restrict, char **__restrict, struct __locale_struct *); -#endif - -#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) -#define mkstemp64 mkstemp -#define mkostemp64 mkostemp -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define mkstemps64 mkstemps -#define mkostemps64 mkostemps -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-7/test.c b/c/misra/test/rules/RULE-21-7/test.c deleted file mode 100644 index 6c3c2d18eb..0000000000 --- a/c/misra/test/rules/RULE-21-7/test.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "stdlib.h" -#include -void f2(); -void f1() { - char l1[5] = "abcde"; - float l2 = atof(l1); // NON_COMLIANT - int l3 = atoi(l1); // NON_COMPLIANT - long l4 = atol(l1); // NON_COMPLIANT - long long l5 = atoll(l1); // NON_COMPLIANT - f2(); // COMPLIANT -} diff --git a/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected index b3e816050a..7dad54e9cf 100644 --- a/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected @@ -1,6 +1,6 @@ -| test.c:10:3:10:6 | call to exit | Call to banned function $@. | stdlib.h:46:16:46:19 | exit | exit | -| test.c:11:3:11:8 | call to system | Call to banned function $@. | stdlib.h:53:5:53:10 | system | system | -| test.c:12:3:12:7 | call to abort | Call to banned function $@. | stdlib.h:44:16:44:20 | abort | abort | -| test.c:14:3:14:9 | call to exit | Call to banned function $@. | stdlib.h:46:16:46:19 | exit | exit | -| test.c:15:3:15:7 | call to abort | Call to banned function $@. | stdlib.h:44:16:44:20 | abort | abort | -| test.c:16:3:16:14 | call to system | Call to banned function $@. | stdlib.h:53:5:53:10 | system | system | +| test.c:10:3:10:6 | call to exit | Call to banned function exit. | +| test.c:11:3:11:8 | call to system | Call to banned function system. | +| test.c:12:3:12:7 | call to abort | Call to banned function abort. | +| test.c:14:3:14:9 | call to exit | Call to banned function exit. | +| test.c:15:3:15:7 | call to abort | Call to banned function abort. | +| test.c:16:3:16:14 | call to system | Call to banned function system. | diff --git a/c/misra/test/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.expected index 93a3769c1e..b4232cb60e 100644 --- a/c/misra/test/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-8/TerminationMacrosOfStdlibhUsed.expected @@ -1,3 +1,3 @@ -| test.c:14:3:14:9 | EXIT(x) | Use of banned macro $@. | test.c:3:1:3:23 | #define EXIT(x) exit(x) | EXIT | -| test.c:15:3:15:7 | ABORT | Use of banned macro $@. | test.c:4:1:4:21 | #define ABORT abort() | ABORT | -| test.c:16:3:16:14 | SYSTEM(x) | Use of banned macro $@. | test.c:5:1:5:27 | #define SYSTEM(x) system(x) | SYSTEM | +| test.c:14:3:14:9 | EXIT(x) | Use of banned macro EXIT. | +| test.c:15:3:15:7 | ABORT | Use of banned macro ABORT. | +| test.c:16:3:16:14 | SYSTEM(x) | Use of banned macro SYSTEM. | diff --git a/c/misra/test/rules/RULE-21-8/stdlib.h b/c/misra/test/rules/RULE-21-8/stdlib.h deleted file mode 100644 index b54a051fe9..0000000000 --- a/c/misra/test/rules/RULE-21-8/stdlib.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef _STDLIB_H -#define _STDLIB_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - -#define __NEED_size_t -#define __NEED_wchar_t - -#include - -int atoi (const char *); -long atol (const char *); -long long atoll (const char *); -double atof (const char *); - -float strtof (const char *__restrict, char **__restrict); -double strtod (const char *__restrict, char **__restrict); -long double strtold (const char *__restrict, char **__restrict); - -long strtol (const char *__restrict, char **__restrict, int); -unsigned long strtoul (const char *__restrict, char **__restrict, int); -long long strtoll (const char *__restrict, char **__restrict, int); -unsigned long long strtoull (const char *__restrict, char **__restrict, int); - -int rand (void); -void srand (unsigned); - -void *malloc (size_t); -void *calloc (size_t, size_t); -void *realloc (void *, size_t); -void free (void *); -void *aligned_alloc(size_t, size_t); - -_Noreturn void abort (void); -int atexit (void (*) (void)); -_Noreturn void exit (int); -_Noreturn void _Exit (int); -int at_quick_exit (void (*) (void)); -_Noreturn void quick_exit (int); - -char *getenv (const char *); - -int system (const char *); - -void *bsearch (const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); -void qsort (void *, size_t, size_t, int (*)(const void *, const void *)); - -int abs (int); -long labs (long); -long long llabs (long long); - -typedef struct { int quot, rem; } div_t; -typedef struct { long quot, rem; } ldiv_t; -typedef struct { long long quot, rem; } lldiv_t; - -div_t div (int, int); -ldiv_t ldiv (long, long); -lldiv_t lldiv (long long, long long); - -int mblen (const char *, size_t); -int mbtowc (wchar_t *__restrict, const char *__restrict, size_t); -int wctomb (char *, wchar_t); -size_t mbstowcs (wchar_t *__restrict, const char *__restrict, size_t); -size_t wcstombs (char *__restrict, const wchar_t *__restrict, size_t); - -#define EXIT_FAILURE 1 -#define EXIT_SUCCESS 0 - -size_t __ctype_get_mb_cur_max(void); -#define MB_CUR_MAX (__ctype_get_mb_cur_max()) - -#define RAND_MAX (0x7fffffff) - - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) - -#define WNOHANG 1 -#define WUNTRACED 2 - -#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) -#define WTERMSIG(s) ((s) & 0x7f) -#define WSTOPSIG(s) WEXITSTATUS(s) -#define WIFEXITED(s) (!WTERMSIG(s)) -#define WIFSTOPPED(s) ((short)((((s)&0xffff)*0x10001)>>8) > 0x7f00) -#define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu) - -int posix_memalign (void **, size_t, size_t); -int setenv (const char *, const char *, int); -int unsetenv (const char *); -int mkstemp (char *); -int mkostemp (char *, int); -char *mkdtemp (char *); -int getsubopt (char **, char *const *, char **); -int rand_r (unsigned *); - -#endif - - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -char *realpath (const char *__restrict, char *__restrict); -long int random (void); -void srandom (unsigned int); -char *initstate (unsigned int, char *, size_t); -char *setstate (char *); -int putenv (char *); -int posix_openpt (int); -int grantpt (int); -int unlockpt (int); -char *ptsname (int); -char *l64a (long); -long a64l (const char *); -void setkey (const char *); -double drand48 (void); -double erand48 (unsigned short [3]); -long int lrand48 (void); -long int nrand48 (unsigned short [3]); -long mrand48 (void); -long jrand48 (unsigned short [3]); -void srand48 (long); -unsigned short *seed48 (unsigned short [3]); -void lcong48 (unsigned short [7]); -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#include -char *mktemp (char *); -int mkstemps (char *, int); -int mkostemps (char *, int, int); -void *valloc (size_t); -void *memalign(size_t, size_t); -int getloadavg(double *, int); -int clearenv(void); -#define WCOREDUMP(s) ((s) & 0x80) -#define WIFCONTINUED(s) ((s) == 0xffff) -void *reallocarray (void *, size_t, size_t); -#endif - -#ifdef _GNU_SOURCE -int ptsname_r(int, char *, size_t); -char *ecvt(double, int, int *, int *); -char *fcvt(double, int, int *, int *); -char *gcvt(double, int, char *); -char *secure_getenv(const char *); -struct __locale_struct; -float strtof_l(const char *__restrict, char **__restrict, struct __locale_struct *); -double strtod_l(const char *__restrict, char **__restrict, struct __locale_struct *); -long double strtold_l(const char *__restrict, char **__restrict, struct __locale_struct *); -#endif - -#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) -#define mkstemp64 mkstemp -#define mkostemp64 mkostemp -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define mkstemps64 mkstemps -#define mkostemps64 mkostemps -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-8/test.c b/c/misra/test/rules/RULE-21-8/test.c index 0c4f99ed27..e5348d7d72 100644 --- a/c/misra/test/rules/RULE-21-8/test.c +++ b/c/misra/test/rules/RULE-21-8/test.c @@ -1,4 +1,4 @@ -#include "stdlib.h" +#include #define EXIT(x) exit(x) #define ABORT abort() diff --git a/c/misra/test/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.expected index 2bf65c461f..e74ac68a2a 100644 --- a/c/misra/test/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-9/BsearchAndQsortOfStdlibhUsed.expected @@ -1,2 +1,2 @@ -| test.c:22:3:22:7 | call to qsort | Call to banned function $@. | stdlib.h:56:6:56:10 | qsort | qsort | -| test.c:26:9:26:15 | call to bsearch | Call to banned function $@. | stdlib.h:55:7:55:13 | bsearch | bsearch | +| test.c:22:3:22:7 | call to qsort | Call to banned function qsort. | +| test.c:26:9:26:15 | call to bsearch | Call to banned function bsearch. | diff --git a/c/misra/test/rules/RULE-21-9/stdlib.h b/c/misra/test/rules/RULE-21-9/stdlib.h deleted file mode 100644 index b54a051fe9..0000000000 --- a/c/misra/test/rules/RULE-21-9/stdlib.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef _STDLIB_H -#define _STDLIB_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void*)0) -#endif - -#define __NEED_size_t -#define __NEED_wchar_t - -#include - -int atoi (const char *); -long atol (const char *); -long long atoll (const char *); -double atof (const char *); - -float strtof (const char *__restrict, char **__restrict); -double strtod (const char *__restrict, char **__restrict); -long double strtold (const char *__restrict, char **__restrict); - -long strtol (const char *__restrict, char **__restrict, int); -unsigned long strtoul (const char *__restrict, char **__restrict, int); -long long strtoll (const char *__restrict, char **__restrict, int); -unsigned long long strtoull (const char *__restrict, char **__restrict, int); - -int rand (void); -void srand (unsigned); - -void *malloc (size_t); -void *calloc (size_t, size_t); -void *realloc (void *, size_t); -void free (void *); -void *aligned_alloc(size_t, size_t); - -_Noreturn void abort (void); -int atexit (void (*) (void)); -_Noreturn void exit (int); -_Noreturn void _Exit (int); -int at_quick_exit (void (*) (void)); -_Noreturn void quick_exit (int); - -char *getenv (const char *); - -int system (const char *); - -void *bsearch (const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); -void qsort (void *, size_t, size_t, int (*)(const void *, const void *)); - -int abs (int); -long labs (long); -long long llabs (long long); - -typedef struct { int quot, rem; } div_t; -typedef struct { long quot, rem; } ldiv_t; -typedef struct { long long quot, rem; } lldiv_t; - -div_t div (int, int); -ldiv_t ldiv (long, long); -lldiv_t lldiv (long long, long long); - -int mblen (const char *, size_t); -int mbtowc (wchar_t *__restrict, const char *__restrict, size_t); -int wctomb (char *, wchar_t); -size_t mbstowcs (wchar_t *__restrict, const char *__restrict, size_t); -size_t wcstombs (char *__restrict, const wchar_t *__restrict, size_t); - -#define EXIT_FAILURE 1 -#define EXIT_SUCCESS 0 - -size_t __ctype_get_mb_cur_max(void); -#define MB_CUR_MAX (__ctype_get_mb_cur_max()) - -#define RAND_MAX (0x7fffffff) - - -#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ - || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) - -#define WNOHANG 1 -#define WUNTRACED 2 - -#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) -#define WTERMSIG(s) ((s) & 0x7f) -#define WSTOPSIG(s) WEXITSTATUS(s) -#define WIFEXITED(s) (!WTERMSIG(s)) -#define WIFSTOPPED(s) ((short)((((s)&0xffff)*0x10001)>>8) > 0x7f00) -#define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu) - -int posix_memalign (void **, size_t, size_t); -int setenv (const char *, const char *, int); -int unsetenv (const char *); -int mkstemp (char *); -int mkostemp (char *, int); -char *mkdtemp (char *); -int getsubopt (char **, char *const *, char **); -int rand_r (unsigned *); - -#endif - - -#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ - || defined(_BSD_SOURCE) -char *realpath (const char *__restrict, char *__restrict); -long int random (void); -void srandom (unsigned int); -char *initstate (unsigned int, char *, size_t); -char *setstate (char *); -int putenv (char *); -int posix_openpt (int); -int grantpt (int); -int unlockpt (int); -char *ptsname (int); -char *l64a (long); -long a64l (const char *); -void setkey (const char *); -double drand48 (void); -double erand48 (unsigned short [3]); -long int lrand48 (void); -long int nrand48 (unsigned short [3]); -long mrand48 (void); -long jrand48 (unsigned short [3]); -void srand48 (long); -unsigned short *seed48 (unsigned short [3]); -void lcong48 (unsigned short [7]); -#endif - -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#include -char *mktemp (char *); -int mkstemps (char *, int); -int mkostemps (char *, int, int); -void *valloc (size_t); -void *memalign(size_t, size_t); -int getloadavg(double *, int); -int clearenv(void); -#define WCOREDUMP(s) ((s) & 0x80) -#define WIFCONTINUED(s) ((s) == 0xffff) -void *reallocarray (void *, size_t, size_t); -#endif - -#ifdef _GNU_SOURCE -int ptsname_r(int, char *, size_t); -char *ecvt(double, int, int *, int *); -char *fcvt(double, int, int *, int *); -char *gcvt(double, int, char *); -char *secure_getenv(const char *); -struct __locale_struct; -float strtof_l(const char *__restrict, char **__restrict, struct __locale_struct *); -double strtod_l(const char *__restrict, char **__restrict, struct __locale_struct *); -long double strtold_l(const char *__restrict, char **__restrict, struct __locale_struct *); -#endif - -#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) -#define mkstemp64 mkstemp -#define mkostemp64 mkostemp -#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define mkstemps64 mkstemps -#define mkostemps64 mkostemps -#endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/c/misra/test/rules/RULE-21-9/test.c b/c/misra/test/rules/RULE-21-9/test.c index 3bebecd355..dfb00f03e0 100644 --- a/c/misra/test/rules/RULE-21-9/test.c +++ b/c/misra/test/rules/RULE-21-9/test.c @@ -1,5 +1,5 @@ -#include "stdlib.h" #include +#include #include #define size_of_months (sizeof(months) / sizeof(months[0])) diff --git a/c/misra/test/rules/RULE-22-1/CloseFileHandleWhenNoLongerNeededMisra.testref b/c/misra/test/rules/RULE-22-1/CloseFileHandleWhenNoLongerNeededMisra.testref new file mode 100644 index 0000000000..960e2354ae --- /dev/null +++ b/c/misra/test/rules/RULE-22-1/CloseFileHandleWhenNoLongerNeededMisra.testref @@ -0,0 +1 @@ +c/common/test/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-1/FreeMemoryWhenNoLongerNeededMisra.testref b/c/misra/test/rules/RULE-22-1/FreeMemoryWhenNoLongerNeededMisra.testref new file mode 100644 index 0000000000..c3215c5533 --- /dev/null +++ b/c/misra/test/rules/RULE-22-1/FreeMemoryWhenNoLongerNeededMisra.testref @@ -0,0 +1 @@ +c/common/test/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-11/ThreadPreviouslyJoinedOrDetached.testref b/c/misra/test/rules/RULE-22-11/ThreadPreviouslyJoinedOrDetached.testref new file mode 100644 index 0000000000..61fa88fd08 --- /dev/null +++ b/c/misra/test/rules/RULE-22-11/ThreadPreviouslyJoinedOrDetached.testref @@ -0,0 +1 @@ +c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-11/ThreadWasPreviouslyJoinedOrDetached.testref b/c/misra/test/rules/RULE-22-11/ThreadWasPreviouslyJoinedOrDetached.testref new file mode 100644 index 0000000000..61fa88fd08 --- /dev/null +++ b/c/misra/test/rules/RULE-22-11/ThreadWasPreviouslyJoinedOrDetached.testref @@ -0,0 +1 @@ +c/common/test/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-12/NonstandardUseOfThreadingObject.expected b/c/misra/test/rules/RULE-22-12/NonstandardUseOfThreadingObject.expected new file mode 100644 index 0000000000..62a740f960 --- /dev/null +++ b/c/misra/test/rules/RULE-22-12/NonstandardUseOfThreadingObject.expected @@ -0,0 +1,27 @@ +| test.c:37:3:37:7 | mutex | Invalid usage of standard thread object type 'mtx_t'. | +| test.c:37:11:37:21 | * ... | Invalid usage of standard thread object type 'mtx_t'. | +| test.c:38:3:38:8 | thread | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:38:12:38:23 | * ... | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:39:3:39:13 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:39:17:39:27 | * ... | Invalid usage of standard thread object type 'tss_t'. | +| test.c:40:3:40:11 | condition | Invalid usage of standard thread object type 'cnd_t'. | +| test.c:40:15:40:25 | * ... | Invalid usage of standard thread object type 'cnd_t'. | +| test.c:43:10:43:15 | & ... | Invalid usage of standard thread object type 'mtx_t *'. | +| test.c:44:10:44:16 | & ... | Invalid usage of standard thread object type 'thrd_t *'. | +| test.c:45:10:45:21 | & ... | Invalid usage of standard thread object type 'tss_t *'. | +| test.c:46:10:46:19 | & ... | Invalid usage of standard thread object type 'cnd_t *'. | +| test.c:48:3:48:13 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:49:3:49:13 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:50:3:50:13 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:54:3:54:8 | thread | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:54:13:54:18 | thread | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:55:3:55:8 | thread | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:55:13:55:18 | thread | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:56:3:56:13 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:56:18:56:28 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:57:3:57:13 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:57:18:57:28 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:61:14:61:18 | mutex | Invalid usage of standard thread object type 'mtx_t'. | +| test.c:62:15:62:20 | thread | Invalid usage of standard thread object type 'thrd_t'. | +| test.c:63:20:63:30 | threadlocal | Invalid usage of standard thread object type 'tss_t'. | +| test.c:64:18:64:26 | condition | Invalid usage of standard thread object type 'cnd_t'. | diff --git a/c/misra/test/rules/RULE-22-12/NonstandardUseOfThreadingObject.qlref b/c/misra/test/rules/RULE-22-12/NonstandardUseOfThreadingObject.qlref new file mode 100644 index 0000000000..d1d345420d --- /dev/null +++ b/c/misra/test/rules/RULE-22-12/NonstandardUseOfThreadingObject.qlref @@ -0,0 +1 @@ +rules/RULE-22-12/NonstandardUseOfThreadingObject.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-12/test.c b/c/misra/test/rules/RULE-22-12/test.c new file mode 100644 index 0000000000..1c59c43d8c --- /dev/null +++ b/c/misra/test/rules/RULE-22-12/test.c @@ -0,0 +1,65 @@ +#include "string.h" +#include "threads.h" + +mtx_t mutex; +thrd_t thread; +tss_t threadlocal; +cnd_t condition; + +extern void use_mutex(mtx_t *m); +extern void use_thread(thrd_t *t); +extern void use_threadlocal(tss_t *t); +extern void use_condition(cnd_t *c); + +void valid_usages(void) { + mtx_init(&mutex, mtx_plain); // COMPLIANT + mtx_lock(&mutex); // COMPLIANT + mtx_unlock(&mutex); // COMPLIANT + thrd_create(&thread, NULL, NULL); // COMPLIANT + tss_create(&threadlocal, NULL); // COMPLIANT + tss_set(threadlocal, NULL); // COMPLIANT + cnd_init(&condition); // COMPLIANT + cnd_signal(&condition); // COMPLIANT + cnd_wait(&condition, &mutex); // COMPLIANT + + use_mutex(&mutex); // COMPLIANT + use_thread(&thread); // COMPLIANT + use_threadlocal(&threadlocal); // COMPLIANT + use_condition(&condition); // COMPLIANT +} + +extern void copy_mutex(mtx_t m); +extern void copy_thread(thrd_t t); +extern void copy_threadlocal(tss_t t); +extern void copy_condition(cnd_t t); + +void invalid_usages(void) { + mutex = *(mtx_t *)0; // NON-COMPLIANT + thread = *(thrd_t *)0; // NON-COMPLIANT + threadlocal = *(tss_t *)0; // NON-COMPLIANT + condition = *(cnd_t *)0; // NON-COMPLIANT + + int *buf; + memcpy(&mutex, buf, sizeof(mtx_t)); // NON-COMPLIANT + memcpy(&thread, buf, sizeof(thrd_t)); // NON-COMPLIANT + memcpy(&threadlocal, buf, sizeof(tss_t)); // NON-COMPLIANT + memcpy(&condition, buf, sizeof(cnd_t)); // NON-COMPLIANT + + threadlocal++; // NON-COMPLIANT + threadlocal += 1; // NON-COMPLIANT + threadlocal + 1; // NON-COMPLIANT + + // mutex == mutex; // NON-COMPLIANT + // mutex != mutex; // NON-COMPLIANT + thread == thread; // NON-COMPLIANT + thread != thread; // NON-COMPLIANT + threadlocal == threadlocal; // NON-COMPLIANT + threadlocal != threadlocal; // NON-COMPLIANT + // condition == condition; // NON-COMPLIANT + // condition != condition; // NON-COMPLIANT + + copy_mutex(mutex); // COMPLIANT + copy_thread(thread); // COMPLIANT + copy_threadlocal(threadlocal); // COMPLIANT + copy_condition(condition); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.expected b/c/misra/test/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.expected new file mode 100644 index 0000000000..0fe033366d --- /dev/null +++ b/c/misra/test/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.expected @@ -0,0 +1,13 @@ +| test.c:14:21:14:22 | g9 | Object of type 'mtx_t' has invalid storage duration type 'thread'. | +| test.c:15:22:15:24 | g10 | Object of type 'thrd_t' has invalid storage duration type 'thread'. | +| test.c:16:21:16:23 | g11 | Object of type 'tss_t' has invalid storage duration type 'thread'. | +| test.c:17:21:17:23 | g12 | Object of type 'cnd_t' has invalid storage duration type 'thread'. | +| test.c:34:9:34:10 | l1 | Object of type 'mtx_t' has invalid storage duration type 'automatic'. | +| test.c:35:10:35:11 | l2 | Object of type 'thrd_t' has invalid storage duration type 'automatic'. | +| test.c:36:9:36:10 | l3 | Object of type 'tss_t' has invalid storage duration type 'automatic'. | +| test.c:37:9:37:10 | l4 | Object of type 'cnd_t' has invalid storage duration type 'automatic'. | +| test.c:44:9:44:10 | l9 | Object of type 'mtx_t[10]' has invalid storage duration type 'automatic'. | +| test.c:46:13:46:15 | l11 | Object of type 'has_mtx_t' has invalid storage duration type 'automatic'. | +| test.c:48:13:48:15 | l13 | Object of type 'has_mtx_t[10]' has invalid storage duration type 'automatic'. | +| test.c:51:9:51:14 | call to malloc | Object of type 'mtx_t' has invalid storage duration type 'allocated'. | +| test.c:52:9:52:14 | call to malloc | Object of type 'mtx_t' has invalid storage duration type 'allocated'. | diff --git a/c/misra/test/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.qlref b/c/misra/test/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.qlref new file mode 100644 index 0000000000..9c054fc623 --- /dev/null +++ b/c/misra/test/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.qlref @@ -0,0 +1 @@ +rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-13/test.c b/c/misra/test/rules/RULE-22-13/test.c new file mode 100644 index 0000000000..193f4be471 --- /dev/null +++ b/c/misra/test/rules/RULE-22-13/test.c @@ -0,0 +1,53 @@ +#include "stdlib.h" +#include "threads.h" + +mtx_t g1; // COMPLIANT +thrd_t g2; // COMPLIANT +tss_t g3; // COMPLIANT +cnd_t g4; // COMPLIANT + +static mtx_t g5; // COMPLIANT +static thrd_t g6; // COMPLIANT +static tss_t g7; // COMPLIANT +static cnd_t g8; // COMPLIANT + +_Thread_local mtx_t g9; // NON-COMPLIANT +_Thread_local thrd_t g10; // NON-COMPLIANT +_Thread_local tss_t g11; // NON-COMPLIANT +_Thread_local cnd_t g12; // NON-COMPLIANT + +typedef struct { + mtx_t m; +} has_mtx_t; + +typedef struct { + mtx_t *m; +} has_ptr_mtx_t; + +mtx_t g13[10]; // COMPLIANT +mtx_t *g14; // COMPLIANT +has_mtx_t g15; // COMPLIANT +has_ptr_mtx_t g16; // COMPLIANT +has_mtx_t g17[10]; // COMPLIANT + +void f1(void) { + mtx_t l1; // NON-COMPLIANT + thrd_t l2; // NON-COMPLIANT + tss_t l3; // NON-COMPLIANT + cnd_t l4; // NON-COMPLIANT + + static mtx_t l5; // COMPLIANT + static thrd_t l6; // COMPLIANT + static tss_t l7; // COMPLIANT + static cnd_t l8; // COMPLIANT + + mtx_t l9[10]; // NON-COMPLIANT + mtx_t *l10; // COMPLIANT + has_mtx_t l11; // NON-COMPLIANT + has_ptr_mtx_t l12; // COMPLIANT + has_mtx_t l13[10]; // NON-COMPLIANT + + l10 = &g1; // COMPLIANT + l10 = malloc(sizeof(mtx_t)); // NON-COMPLIANT + l10 = malloc(sizeof(mtx_t) * 4); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-14/MutexInitWithInvalidMutexType.expected b/c/misra/test/rules/RULE-22-14/MutexInitWithInvalidMutexType.expected new file mode 100644 index 0000000000..1ea39b6b1d --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/MutexInitWithInvalidMutexType.expected @@ -0,0 +1,5 @@ +| test.c:140:3:140:10 | call to mtx_init | Mutex initialized with incorrect type expression. | +| test.c:141:3:141:10 | call to mtx_init | Mutex initialized with incorrect type expression. | +| test.c:142:3:142:10 | call to mtx_init | Mutex initialized with incorrect type expression. | +| test.c:144:3:144:10 | call to mtx_init | Mutex initialized with incorrect type expression. | +| test.c:145:3:145:10 | call to mtx_init | Mutex initialized with incorrect type expression. | diff --git a/c/misra/test/rules/RULE-22-14/MutexInitWithInvalidMutexType.qlref b/c/misra/test/rules/RULE-22-14/MutexInitWithInvalidMutexType.qlref new file mode 100644 index 0000000000..32b57cfd07 --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/MutexInitWithInvalidMutexType.qlref @@ -0,0 +1 @@ +rules/RULE-22-14/MutexInitWithInvalidMutexType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-14/MutexInitializedInsideThread.expected b/c/misra/test/rules/RULE-22-14/MutexInitializedInsideThread.expected new file mode 100644 index 0000000000..360c02f622 --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/MutexInitializedInsideThread.expected @@ -0,0 +1 @@ +| test.c:110:3:110:10 | call to mtx_init | Mutex initialization reachable from threaded function '$@'. | test.c:105:6:105:32 | from_root7_use_thread_local | from_root7_use_thread_local | diff --git a/c/misra/test/rules/RULE-22-14/MutexInitializedInsideThread.qlref b/c/misra/test/rules/RULE-22-14/MutexInitializedInsideThread.qlref new file mode 100644 index 0000000000..83ada06139 --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/MutexInitializedInsideThread.qlref @@ -0,0 +1 @@ +rules/RULE-22-14/MutexInitializedInsideThread.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-14/MutexNotInitializedBeforeUse.expected b/c/misra/test/rules/RULE-22-14/MutexNotInitializedBeforeUse.expected new file mode 100644 index 0000000000..b9560165ce --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/MutexNotInitializedBeforeUse.expected @@ -0,0 +1,14 @@ +| test.c:6:12:6:14 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:5:9:5:10 | l1 | l1 | test.c:4:6:4:19 | use_local_mtxs | use_local_mtxs | +| test.c:13:12:13:16 | & ... | Mutex in object '$@' possibly used before initialization, from entry point function '$@'. | test.c:12:5:12:6 | l2 | l2 | test.c:4:6:4:19 | use_local_mtxs | use_local_mtxs | +| test.c:18:12:18:17 | & ... | Mutex in object '$@' possibly used before initialization, from entry point function '$@'. | test.c:17:9:17:10 | l3 | l3 | test.c:4:6:4:19 | use_local_mtxs | use_local_mtxs | +| test.c:23:12:23:13 | l4 | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:22:15:22:20 | call to malloc | call to malloc | test.c:28:6:28:31 | root1_calls_use_local_mtxs | root1_calls_use_local_mtxs | +| test.c:41:12:41:14 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:34:7:34:8 | g1 | g1 | test.c:40:6:40:30 | root2_uses_global_mutexes | root2_uses_global_mutexes | +| test.c:42:12:42:17 | & ... | Mutex in object '$@' possibly used before initialization, from entry point function '$@'. | test.c:37:3:37:4 | g2 | g2 | test.c:40:6:40:30 | root2_uses_global_mutexes | root2_uses_global_mutexes | +| test.c:43:12:43:13 | g3 | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:56:8:56:13 | call to malloc | call to malloc | test.c:40:6:40:30 | root2_uses_global_mutexes | root2_uses_global_mutexes | +| test.c:64:12:64:14 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:34:7:34:8 | g1 | g1 | test.c:67:6:67:45 | root4_call_thread_without_initialization | root4_call_thread_without_initialization | +| test.c:88:12:88:14 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:87:9:87:10 | l1 | l1 | test.c:86:6:86:36 | island1_use_uninitialized_mutex | island1_use_uninitialized_mutex | +| test.c:100:12:100:14 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:98:21:98:22 | g5 | g5 | test.c:99:6:99:27 | root6_use_thread_local | root6_use_thread_local | +| test.c:107:12:107:14 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:98:21:98:22 | g5 | g5 | test.c:105:6:105:32 | from_root7_use_thread_local | from_root7_use_thread_local | +| test.c:124:12:124:13 | & ... | Condition '$@' possibly used before initialization, from entry point function '$@'. | test.c:122:9:122:9 | c | c | test.c:121:6:121:28 | root8_uninitialized_cnd | root8_uninitialized_cnd | +| test.c:124:16:124:17 | & ... | Mutex '$@' possibly used before initialization, from entry point function '$@'. | test.c:123:9:123:9 | m | m | test.c:121:6:121:28 | root8_uninitialized_cnd | root8_uninitialized_cnd | +| test.c:127:12:127:13 | & ... | Condition '$@' possibly used before initialization, from entry point function '$@'. | test.c:122:9:122:9 | c | c | test.c:121:6:121:28 | root8_uninitialized_cnd | root8_uninitialized_cnd | diff --git a/c/misra/test/rules/RULE-22-14/MutexNotInitializedBeforeUse.qlref b/c/misra/test/rules/RULE-22-14/MutexNotInitializedBeforeUse.qlref new file mode 100644 index 0000000000..2827a9c571 --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/MutexNotInitializedBeforeUse.qlref @@ -0,0 +1 @@ +rules/RULE-22-14/MutexNotInitializedBeforeUse.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-14/test.c b/c/misra/test/rules/RULE-22-14/test.c new file mode 100644 index 0000000000..c664a08dc3 --- /dev/null +++ b/c/misra/test/rules/RULE-22-14/test.c @@ -0,0 +1,158 @@ +#include "stdlib.h" +#include "threads.h" + +void use_local_mtxs(int x, int y) { + mtx_t l1; + mtx_lock(&l1); // NON-COMPLIANT + mtx_init(&l1, mtx_plain); + mtx_lock(&l1); // COMPLIANT + + struct { + mtx_t m; + } l2; + mtx_lock(&l2.m); // NON-COMPLIANT + mtx_init(&l2.m, mtx_plain); + mtx_lock(&l2.m); // COMPLIANT + + mtx_t l3[10]; + mtx_lock(&l3[y]); // NON-COMPLIANT + mtx_init(&l3[x], mtx_plain); + mtx_lock(&l3[y]); // COMPLIANT + + mtx_t *l4 = malloc(sizeof(mtx_t)); + mtx_lock(l4); // NON-COMPLIANT + mtx_init(l4, mtx_plain); + mtx_lock(l4); // COMPLIANT +} + +void root1_calls_use_local_mtxs() { + // Since a function exists which calls use_local_mtxs(), that function is not + // a root function. The query should still report unused locals in this case. + use_local_mtxs(1, 2); +} + +mtx_t g1; +struct { + mtx_t m1; +} g2; +mtx_t *g3; + +void root2_uses_global_mutexes() { + mtx_lock(&g1); // NON-COMPLIANT + mtx_lock(&g2.m1); // NON-COMPLIANT + mtx_lock(g3); // NON-COMPLIANT +} + +void from_root3_use_global_mutexes() { + mtx_lock(&g1); // COMPLIANT + mtx_lock(&g2.m1); // COMPLIANT + mtx_lock(g3); // COMPLIANT +} + +void root3_initializes_and_uses_global_mutexes() { + // Init global mutex with an allocated storage duration object. The existence + // of this malloc() is not checked by the query, but if its exists, the object + // and its uses should be trackable as a nice bonus. + g3 = malloc(sizeof(mtx_t)); + mtx_init(&g1, mtx_plain); + mtx_init(&g2.m1, mtx_plain); + mtx_init(g3, mtx_plain); + from_root3_use_global_mutexes(); +} + +void from_root4_use_global_mutex(void *arg) { + mtx_lock(&g1); // NON-COMPLIANT +} + +void root4_call_thread_without_initialization() { + thrd_t t; + thrd_create(&t, &from_root4_use_global_mutex, NULL); +} + +void from_root5_use_global_mutex(void *arg) { + mtx_lock(&g1); // COMPLIANT +} + +void root5_thread_with_initialization() { + mtx_init(&g1, mtx_plain); + thrd_t t; + thrd_create(&t, &from_root5_use_global_mutex, NULL); +} + +// Set up two functions such that a calls b and b calls a. This means there is +// no root function, but we should still report unused locals. +void island2_call_island1(); + +void island1_use_uninitialized_mutex() { + mtx_t l1; + mtx_lock(&l1); // NON-COMPLIANT + + // Globals are hard to detect + mtx_lock(&g1); // NON-COMPLIANT[False negative] + + island2_call_island1(); +} + +void island2_call_island1() { island1_use_uninitialized_mutex(); } + +_Thread_local mtx_t g5; +void root6_use_thread_local() { + mtx_lock(&g5); // NON-COMPLIANT + mtx_init(&g5, mtx_plain); + mtx_lock(&g5); // COMPLIANT +} + +void from_root7_use_thread_local() { + // Invalid, thread local g5 hasn't been initialized in this thread. + mtx_lock(&g5); // NON-COMPLIANT + + // Violates recommendation, mutexes initialized within a thread. + mtx_init(&g5, mtx_plain); // NON-COMPLIANT + + // Valid if we except the above initialization. + mtx_lock(&g5); // COMPLIANT +} + +void root7_spawn_thread_uninitialized_thread_local() { + thrd_t t; + thrd_create(&t, &from_root7_use_thread_local, NULL); +} + +void root8_uninitialized_cnd() { + cnd_t c; + mtx_t m; + cnd_wait(&c, &m); // NON-COMPLIANT + + mtx_init(&m, mtx_plain); + cnd_wait(&c, &m); // NON-COMPLIANT + + cnd_init(&c); + cnd_wait(&c, &m); // COMPLIANT +} + +void invalid_mtx_init_types() { + mtx_t m; + mtx_init(&m, mtx_plain); // COMPLIANT + mtx_init(&m, mtx_plain | mtx_recursive); // COMPLIANT + mtx_init(&m, mtx_timed); // COMPLIANT + mtx_init(&m, mtx_timed | mtx_recursive); // COMPLIANT + + mtx_init(&m, mtx_recursive); // NON-COMPLIANT + mtx_init(&m, mtx_plain | mtx_timed); // NON-COMPLIANT + mtx_init(&m, mtx_plain | mtx_plain); // NON-COMPLIANT + mtx_init(&m, mtx_plain & mtx_recursive); // NON-COMPLIANT + mtx_init(&m, mtx_plain * mtx_recursive); // NON-COMPLIANT + mtx_init(&m, -1); // NON-COMPLIANT +} + +void function_pointer_uses_global_mutexes() { + // If the function has been used as a function pointer, we don't attempt to + // analyze this. + mtx_lock(&g1); // COMPLIANT + mtx_lock(&g2.m1); // COMPLIANT + mtx_lock(g3); // COMPLIANT +} + +void take_function_pointer() { + void (*f)(void) = function_pointer_uses_global_mutexes; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.expected b/c/misra/test/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.expected new file mode 100644 index 0000000000..49f1b74c15 --- /dev/null +++ b/c/misra/test/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.expected @@ -0,0 +1,12 @@ +| test.c:16:3:16:13 | call to mtx_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:3:7:3:8 | g1 | g1 | test.c:64:3:64:13 | call to thrd_create | t2_use_all | +| test.c:16:3:16:13 | call to mtx_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:3:7:3:8 | g1 | g1 | test.c:72:3:72:13 | call to thrd_create | t2_use_all | +| test.c:16:3:16:13 | call to mtx_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:3:7:3:8 | g1 | g1 | test.c:91:3:91:10 | call to mtx_lock | main thread | +| test.c:17:3:17:12 | call to tss_delete | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:4:7:4:8 | g2 | g2 | test.c:64:3:64:13 | call to thrd_create | t2_use_all | +| test.c:17:3:17:12 | call to tss_delete | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:4:7:4:8 | g2 | g2 | test.c:72:3:72:13 | call to thrd_create | t2_use_all | +| test.c:17:3:17:12 | call to tss_delete | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:4:7:4:8 | g2 | g2 | test.c:92:3:92:9 | call to tss_get | main thread | +| test.c:18:3:18:13 | call to cnd_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:5:7:5:8 | g3 | g3 | test.c:64:3:64:13 | call to thrd_create | t2_use_all | +| test.c:18:3:18:13 | call to cnd_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:5:7:5:8 | g3 | g3 | test.c:72:3:72:13 | call to thrd_create | t2_use_all | +| test.c:18:3:18:13 | call to cnd_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:5:7:5:8 | g3 | g3 | test.c:93:3:93:10 | call to cnd_wait | main thread | +| test.c:42:3:42:13 | call to mtx_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:3:7:3:8 | g1 | g1 | test.c:41:3:41:13 | call to thrd_create | t2_use_all | +| test.c:43:3:43:12 | call to tss_delete | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:4:7:4:8 | g2 | g2 | test.c:41:3:41:13 | call to thrd_create | t2_use_all | +| test.c:44:3:44:13 | call to cnd_destroy | Thread resource $@ disposed before joining thread $@ which uses it. | test.c:5:7:5:8 | g3 | g3 | test.c:41:3:41:13 | call to thrd_create | t2_use_all | diff --git a/c/misra/test/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.qlref b/c/misra/test/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.qlref new file mode 100644 index 0000000000..809eae6faf --- /dev/null +++ b/c/misra/test/rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.qlref @@ -0,0 +1 @@ +rules/RULE-22-15/ThreadResourceDisposedBeforeThreadsJoined.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-15/test.c b/c/misra/test/rules/RULE-22-15/test.c new file mode 100644 index 0000000000..7679730fc9 --- /dev/null +++ b/c/misra/test/rules/RULE-22-15/test.c @@ -0,0 +1,113 @@ +#include "threads.h" + +mtx_t g1; +tss_t g2; +cnd_t g3; + +int t1_use_none(void *p) { return 0; } + +int t2_use_all(void *p) { + mtx_lock(&g1); + tss_get(&g2); + cnd_wait(&g3, &g1); +} + +int t3_dispose_all(void *p) { + mtx_destroy(&g1); + tss_delete(&g2); + cnd_destroy(&g3); +} + +int t4_use_then_dispose(void *p) { + mtx_lock(&g1); + tss_get(&g2); + cnd_wait(&g3, &g1); + + mtx_destroy(&g1); + tss_delete(&g2); + cnd_destroy(&g3); +} + +void f1() { + thrd_t t; + thrd_create(&t, t1_use_none, NULL); + mtx_destroy(&g1); + tss_delete(&g2); + cnd_destroy(&g3); +} + +void f2() { + thrd_t t; + thrd_create(&t, t2_use_all, NULL); + mtx_destroy(&g1); // NON-COMPLIANT + tss_delete(&g2); // NON-COMPLIANT + cnd_destroy(&g3); // NON-COMPLIANT +} + +void f3() { + thrd_t t; + thrd_create(&t, t2_use_all, NULL); // COMPLIANT +} + +void f4() { + thrd_t t; + thrd_create(&t, t2_use_all, NULL); // COMPLIANT + thrd_join(&t, NULL); + mtx_destroy(&g1); // COMPLIANT + tss_delete(&g2); // COMPLIANT + cnd_destroy(&g3); // COMPLIANT +} + +void f5() { + thrd_t t1; + thrd_t t2; + thrd_create(&t1, t2_use_all, NULL); // COMPLIANT + thrd_create(&t2, t3_dispose_all, NULL); // NON-COMPLIANT +} + +void f6() { + thrd_t t1; + thrd_t t2; + thrd_create(&t1, t3_dispose_all, NULL); // NON-COMPLIANT + thrd_create(&t2, t2_use_all, NULL); // COMPLIANT +} + +void f7() { + thrd_t t1; + thrd_t t2; + thrd_create(&t1, t2_use_all, NULL); // COMPLIANT + thrd_join(&t1, NULL); + thrd_create(&t2, t3_dispose_all, NULL); // COMPLIANT +} + +void f8() { + thrd_t t; + thrd_create(&t, t4_use_then_dispose, NULL); // COMPLIANT +} + +void f9() { + thrd_t t; + thrd_create(&t, t3_dispose_all, NULL); // NON-COMPLIANT + mtx_lock(&g1); + tss_get(&g2); + cnd_wait(&g3, &g1); +} + +void f10() { + thrd_t t; + mtx_lock(&g1); + tss_get(&g2); + cnd_wait(&g3, &g1); + thrd_create(&t, t3_dispose_all, NULL); // COMPLIANT +} + +void f11() { + thrd_t t; + thrd_create(&t, t1_use_none, NULL); + mtx_lock(&g1); + tss_get(&g2); + cnd_wait(&g3, &g1); + mtx_destroy(&g1); // COMPLIANT + tss_delete(&g2); // COMPLIANT + cnd_destroy(&g3); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.expected b/c/misra/test/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.expected new file mode 100644 index 0000000000..46a295d75f --- /dev/null +++ b/c/misra/test/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.expected @@ -0,0 +1,10 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (MutexObjectsNotAlwaysUnlocked.ql:22,52-60) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (MutexObjectsNotAlwaysUnlocked.ql:30,42-50) +| test.c:16:3:16:10 | call to mtx_lock | Mutex 'm' is locked here and may not always be subsequently unlocked. | +| test.c:21:3:21:10 | call to mtx_lock | Mutex 'm' is locked here and may not always be subsequently unlocked. | +| test.c:39:3:39:10 | call to mtx_lock | Mutex 'm' is locked here and may not always be subsequently unlocked. | +| test.c:55:3:55:10 | call to mtx_lock | Mutex 'm' is locked here and may not always be subsequently unlocked. | +| test.c:72:3:72:10 | call to mtx_lock | Mutex 'g1' is locked here and may not always be subsequently unlocked. | +| test.c:79:3:79:10 | call to mtx_lock | Mutex 'm' is locked here and may not always be subsequently unlocked. | +| test.c:101:5:101:12 | call to mtx_lock | Mutex 'm' is locked here and may not always be subsequently unlocked. | +| test.c:113:3:113:10 | call to mtx_lock | Mutex 'ptr_m1' is locked here and may not always be subsequently unlocked. | diff --git a/c/misra/test/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.qlref b/c/misra/test/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.qlref new file mode 100644 index 0000000000..a1877f944d --- /dev/null +++ b/c/misra/test/rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.qlref @@ -0,0 +1 @@ +rules/RULE-22-16/MutexObjectsNotAlwaysUnlocked.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-16/test.c b/c/misra/test/rules/RULE-22-16/test.c new file mode 100644 index 0000000000..723516509f --- /dev/null +++ b/c/misra/test/rules/RULE-22-16/test.c @@ -0,0 +1,115 @@ +#include "threads.h" + +void f1() { + mtx_t m; + mtx_lock(&m); // COMPLIANT + mtx_unlock(&m); +} + +void f2() { + mtx_t m; + mtx_unlock(&m); // COMPLIANT +} + +void f3() { + mtx_t m; + mtx_lock(&m); // NON-COMPLIANT +} + +void f4(int p) { + mtx_t m; + mtx_lock(&m); // NON-COMPLIANT + if (p) { + mtx_unlock(&m); + } +} + +void f5(int p) { + mtx_t m; + mtx_lock(&m); // COMPLIANT + if (p) { + mtx_unlock(&m); + } else { + mtx_unlock(&m); + } +} + +void f6(int p) { + mtx_t m; + mtx_lock(&m); // NON-COMPLIANT + if (p) { + goto skipped; + } + mtx_unlock(&m); +skipped:; +} + +void f7(int p) { + mtx_t *m; + mtx_lock(m); // COMPLIANT + mtx_unlock(m); +} + +void f8(int p) { + mtx_t *m; + mtx_lock(m); // NON-COMPLIANT +} + +void f9(int p) { + mtx_t m; + mtx_lock(&m); // COMPLIANT + mtx_t *ptr_m = &m; + mtx_unlock(ptr_m); +} + +mtx_t g1; +void f10() { + mtx_lock(&g1); // COMPLIANT + mtx_unlock(&g1); +} + +void f11() { + mtx_lock(&g1); // NON-COMPLIANT +} + +void f12() { + struct { + mtx_t m; + } s; + mtx_lock(&s.m); // NON-COMPLIANT +} + +void f13() { + struct { + mtx_t m; + } s; + mtx_lock(&s.m); // COMPLIANT + mtx_unlock(&s.m); +} + +void f14() { + for (;;) { + mtx_t m; + mtx_lock(&m); // COMPLIANT + mtx_unlock(&m); + } +} + +void f15(int p) { + for (;;) { + mtx_t m; + mtx_lock(&m); // NON-COMPLIANT + if (p) { + break; + } + mtx_unlock(&m); + } +} + +void f16(int p) { + mtx_t *ptr; + mtx_t *ptr_m1 = ptr; + mtx_t *ptr_m2 = ptr; + mtx_lock(ptr_m1); // COMPLIANT[FALSE_POSITIVE] + mtx_unlock(ptr_m2); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.expected b/c/misra/test/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.expected new file mode 100644 index 0000000000..254d55adc2 --- /dev/null +++ b/c/misra/test/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.expected @@ -0,0 +1,16 @@ +| test.c:19:3:19:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:11:9:11:10 | l1 | l1 | +| test.c:20:3:20:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:11:9:11:10 | l1 | l1 | +| test.c:25:3:25:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:14:5:14:6 | l2 | l2.m1 | +| test.c:26:3:26:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:14:5:14:6 | l2 | l2.m1 | +| test.c:31:3:31:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:3:7:3:8 | g1 | g1 | +| test.c:32:3:32:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:3:7:3:8 | g1 | g1 | +| test.c:37:3:37:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:6:3:6:4 | g2 | g2.m1 | +| test.c:38:3:38:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:6:3:6:4 | g2 | g2.m1 | +| test.c:47:3:47:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:11:9:11:10 | l1 | l1 | +| test.c:48:3:48:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:14:5:14:6 | l2 | l2.m1 | +| test.c:49:3:49:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:3:7:3:8 | g1 | g1 | +| test.c:50:3:50:10 | call to cnd_wait | Invalid operation on mutex '$@' not locked by the current thread. | test.c:6:3:6:4 | g2 | g2.m1 | +| test.c:51:3:51:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:11:9:11:10 | l1 | l1 | +| test.c:52:3:52:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:14:5:14:6 | l2 | l2.m1 | +| test.c:53:3:53:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:3:7:3:8 | g1 | g1 | +| test.c:54:3:54:12 | call to mtx_unlock | Invalid operation on mutex '$@' not locked by the current thread. | test.c:6:3:6:4 | g2 | g2.m1 | diff --git a/c/misra/test/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.qlref b/c/misra/test/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.qlref new file mode 100644 index 0000000000..4ac06f10ed --- /dev/null +++ b/c/misra/test/rules/RULE-22-17/InvalidOperationOnUnlockedMutex.qlref @@ -0,0 +1 @@ +rules/RULE-22-17/InvalidOperationOnUnlockedMutex.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-17/test.c b/c/misra/test/rules/RULE-22-17/test.c new file mode 100644 index 0000000000..fc841bb3e1 --- /dev/null +++ b/c/misra/test/rules/RULE-22-17/test.c @@ -0,0 +1,70 @@ +#include "threads.h" + +mtx_t g1; +struct { + mtx_t m1; +} g2; + +cnd_t cnd; + +void f1(int p) { + mtx_t l1; + struct { + mtx_t m1; + } l2; + + mtx_lock(&l1); + cnd_wait(&cnd, &l1); // COMPLIANT + mtx_unlock(&l1); // COMPLIANT + cnd_wait(&cnd, &l1); // NON-COMPLIANT + mtx_unlock(&l1); // NON-COMPLIANT + + mtx_lock(&l2.m1); + cnd_wait(&cnd, &l2.m1); // COMPLIANT + mtx_unlock(&l2.m1); // COMPLIANT + cnd_wait(&cnd, &l2.m1); // NON-COMPLIANT + mtx_unlock(&l2.m1); // NON-COMPLIANT + + mtx_lock(&g1); + cnd_wait(&cnd, &g1); // COMPLIANT + mtx_unlock(&g1); // COMPLIANT + cnd_wait(&cnd, &g1); // NON-COMPLIANT + mtx_unlock(&g1); // NON-COMPLIANT + + mtx_lock(&g2.m1); + cnd_wait(&cnd, &g2.m1); // COMPLIANT + mtx_unlock(&g2.m1); // COMPLIANT + cnd_wait(&cnd, &g2.m1); // NON-COMPLIANT + mtx_unlock(&g2.m1); // NON-COMPLIANT + + // We should report when a mutex is unlocked in the wrong block: + if (p) { + mtx_lock(&l1); + mtx_lock(&l2.m1); + mtx_lock(&g1); + mtx_lock(&g2.m1); + } + cnd_wait(&cnd, &l1); // NON-COMPLIANT + cnd_wait(&cnd, &l2.m1); // NON-COMPLIANT + cnd_wait(&cnd, &g1); // NON-COMPLIANT + cnd_wait(&cnd, &g2.m1); // NON-COMPLIANT + mtx_unlock(&l1); // NON-COMPLIANT + mtx_unlock(&l2.m1); // NON-COMPLIANT + mtx_unlock(&g1); // NON-COMPLIANT + mtx_unlock(&g2.m1); // NON-COMPLIANT + + // The above requires dominance analysis. Check dominator sets don't cause + // false positives: + if (p) { + mtx_lock(&l1); + } else { + mtx_lock(&l1); + } + mtx_unlock(&l1); // COMPLIANT + + // Invalid but satisfies the rule: + mtx_lock(&l1); + if (p) { + mtx_unlock(&l1); // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.expected b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.expected new file mode 100644 index 0000000000..fd947dee51 --- /dev/null +++ b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.expected @@ -0,0 +1,2 @@ +| test.c:19:3:19:10 | call to mtx_lock | Non-recursive mutex nonrec locked after it is $@. | test.c:18:3:18:10 | call to mtx_lock | already locked | +| test.c:22:3:22:10 | call to mtx_lock | Non-recursive mutex s.m locked after it is $@. | test.c:21:3:21:10 | call to mtx_lock | already locked | diff --git a/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.qlref b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.qlref new file mode 100644 index 0000000000..131e0476bf --- /dev/null +++ b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.qlref @@ -0,0 +1 @@ +rules/RULE-22-18/NonRecursiveMutexRecursivelyLocked.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.expected b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.expected new file mode 100644 index 0000000000..e268f5367e --- /dev/null +++ b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.expected @@ -0,0 +1,6 @@ +| test.c:44:3:44:10 | call to mtx_lock | Mutex locked after it was already $@. | test.c:43:3:43:10 | call to mtx_lock | previously locked | +| test.c:49:3:49:10 | call to mtx_lock | Mutex locked after it was already $@. | test.c:48:3:48:10 | call to mtx_lock | previously locked | +| test.c:54:3:54:10 | call to mtx_lock | Mutex locked after it was already $@. | test.c:53:3:53:10 | call to mtx_lock | previously locked | +| test.c:59:3:59:10 | call to mtx_lock | Mutex locked after it was already $@. | test.c:58:3:58:10 | call to mtx_lock | previously locked | +| test.c:76:3:76:10 | call to mtx_lock | Mutex locked after it was already $@. | test.c:75:3:75:10 | call to mtx_lock | previously locked | +| test.c:81:3:81:10 | call to mtx_lock | Mutex locked after it was already $@. | test.c:80:3:80:10 | call to mtx_lock | previously locked | diff --git a/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.qlref b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.qlref new file mode 100644 index 0000000000..77a81deb69 --- /dev/null +++ b/c/misra/test/rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.qlref @@ -0,0 +1 @@ +rules/RULE-22-18/NonRecursiveMutexRecursivelyLockedAudit.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-18/test.c b/c/misra/test/rules/RULE-22-18/test.c new file mode 100644 index 0000000000..f71066b1bc --- /dev/null +++ b/c/misra/test/rules/RULE-22-18/test.c @@ -0,0 +1,122 @@ +#include "threads.h" + +mtx_t rec; +mtx_t nonrec; +mtx_t both; +mtx_t unknown; + +struct { + mtx_t m; +} s; + +mtx_t arr[2]; + +int t1(void *arg) { + mtx_lock(&rec); // COMPLIANT + mtx_lock(&rec); // COMPLIANT + + mtx_lock(&nonrec); // COMPLIANT + mtx_lock(&nonrec); // NON-COMPLIANT + + mtx_lock(&s.m); // COMPLIANT + mtx_lock(&s.m); // NON-COMPLIANT +} + +void f1() { + mtx_init(&rec, mtx_plain | mtx_recursive); + mtx_init(&nonrec, mtx_plain); + mtx_init(&both, mtx_plain); + mtx_init(&both, mtx_plain | mtx_recursive); + // Do not initialize `unknown`. + mtx_init(&s.m, mtx_plain); + mtx_init(&arr[0], mtx_plain); + mtx_init(&arr[1], mtx_plain); + + thrd_t t; + thrd_create(t, t1, NULL); +} + +mtx_t *p; + +// Results for the audit query: +void t2(void *arg) { + mtx_lock(&arr[0]); + mtx_lock(&arr[(int)arg]); // NON-COMPLIANT +} + +void t3(void *arg) { + mtx_lock(arg); + mtx_lock(p); // NON-COMPLIANT +} + +void t4() { + mtx_lock(&both); + mtx_lock(&both); // NON-COMPLIANT +} + +void t5() { + mtx_lock(&unknown); + mtx_lock(&unknown); // NON-COMPLIANT +} + +void t6() { + // Cannot be locks of the same mutex: + mtx_lock(&nonrec); + mtx_lock(&unknown); // COMPLIANT +} + +void t7() { + mtx_lock(p); + // Definitely a recursive mutex: + mtx_lock(&rec); // COMPLIANT +} + +void t8() { + mtx_lock(p); + mtx_lock(&nonrec); // NON-COMPLIANT +} + +void t9() { + mtx_lock(&nonrec); + mtx_lock(p); // NON-COMPLIANT +} + +void f2() { + thrd_t t; + thrd_create(t, t2, NULL); +} + +void f3() { + thrd_t t; + thrd_create(t, t3, &rec); +} + +void f4() { + thrd_t t; + thrd_create(t, t4, NULL); +} + +void f5() { + thrd_t t; + thrd_create(t, t5, NULL); +} + +void f6() { + thrd_t t; + thrd_create(t, t6, NULL); +} + +void f7() { + thrd_t t; + thrd_create(t, t7, NULL); +} + +void f8() { + thrd_t t; + thrd_create(t, t8, NULL); +} + +void f9() { + thrd_t t; + thrd_create(t, t9, NULL); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.expected b/c/misra/test/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.expected new file mode 100644 index 0000000000..c9785067c6 --- /dev/null +++ b/c/misra/test/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.expected @@ -0,0 +1,2 @@ +| test.c:19:3:19:10 | call to cnd_wait | Condition variable $@ associated with multiple mutexes, operation uses mutex $@ while $@ uses other mutex $@. | test.c:16:9:16:12 | cnd1 | cnd1 | test.c:17:9:17:12 | mtx1 | mtx1 | test.c:19:3:19:10 | call to cnd_wait | another operation | test.c:18:9:18:12 | mtx2 | mtx2 | +| test.c:41:3:41:10 | call to cnd_wait | Condition variable $@ associated with multiple mutexes, operation uses mutex $@ while $@ uses other mutex $@. | test.c:37:7:37:11 | gcnd1 | gcnd1 | test.c:38:7:38:11 | gmtx1 | gmtx1 | test.c:41:3:41:10 | call to cnd_wait | another operation | test.c:39:7:39:11 | gmtx2 | gmtx2 | diff --git a/c/misra/test/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.qlref b/c/misra/test/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.qlref new file mode 100644 index 0000000000..d43a824ec8 --- /dev/null +++ b/c/misra/test/rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.qlref @@ -0,0 +1 @@ +rules/RULE-22-19/ConditionVariableUsedWithMultipleMutexes.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-19/test.c b/c/misra/test/rules/RULE-22-19/test.c new file mode 100644 index 0000000000..f4b46d4077 --- /dev/null +++ b/c/misra/test/rules/RULE-22-19/test.c @@ -0,0 +1,46 @@ +#include "threads.h" + +void f1(void) { + cnd_t cnd1; + mtx_t mtx1; + cnd_wait(&cnd1, &mtx1); // COMPLIANT + cnd_wait(&cnd1, &mtx1); // COMPLIANT + + cnd_t cnd2; + mtx_t mtx2; + cnd_wait(&cnd2, &mtx2); // COMPLIANT + cnd_wait(&cnd2, &mtx2); // COMPLIANT +} + +void f2(void) { + cnd_t cnd1; + mtx_t mtx1; + mtx_t mtx2; + cnd_wait(&cnd1, &mtx1); // NON-COMPLIANT + cnd_wait(&cnd1, &mtx2); // NON-COMPLIANT +} + +void f3(void) { + cnd_t cnd1; + cnd_t cnd2; + mtx_t mtx1; + cnd_wait(&cnd1, &mtx1); // COMPLIANT + cnd_wait(&cnd2, &mtx1); // COMPLIANT +} + +void f4(cnd_t *cnd1, mtx_t *mtx1, mtx_t *mtx2) { + cnd_wait(cnd1, mtx1); // COMPLIANT + // Compliant, mtx1 and mtx2 may point to the same object + cnd_wait(cnd1, mtx2); // COMPLIANT +} + +cnd_t gcnd1; +mtx_t gmtx1; +mtx_t gmtx2; +void f5(void) { + cnd_wait(&gcnd1, &gmtx1); // NON-COMPLIANT +} + +void f6(void) { + cnd_wait(&gcnd1, &gmtx2); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-2/OnlyFreeMemoryAllocatedDynamicallyMisra.testref b/c/misra/test/rules/RULE-22-2/OnlyFreeMemoryAllocatedDynamicallyMisra.testref new file mode 100644 index 0000000000..edf7c5cc3b --- /dev/null +++ b/c/misra/test/rules/RULE-22-2/OnlyFreeMemoryAllocatedDynamicallyMisra.testref @@ -0,0 +1 @@ +c/common/test/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.expected b/c/misra/test/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.expected new file mode 100644 index 0000000000..301debd7e8 --- /dev/null +++ b/c/misra/test/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.expected @@ -0,0 +1,5 @@ +| test.c:6:11:6:13 | & ... | Thread specific storage pointer '$@' used before initialization from entry point function '$@'. | test.c:5:9:5:10 | l1 | l1 | test.c:4:6:4:19 | use_local_mtxs | use_local_mtxs | +| test.c:11:11:11:12 | l4 | Thread specific storage pointer '$@' used before initialization from entry point function '$@'. | test.c:10:15:10:20 | call to malloc | call to malloc | test.c:16:6:16:31 | root1_calls_use_local_mtxs | root1_calls_use_local_mtxs | +| test.c:25:11:25:13 | & ... | Thread specific storage pointer '$@' used before initialization from entry point function '$@'. | test.c:22:7:22:8 | g1 | g1 | test.c:24:6:24:28 | root2_uses_global_tss_t | root2_uses_global_tss_t | +| test.c:38:11:38:13 | & ... | Thread specific storage pointer '$@' used before initialization from entry point function '$@'. | test.c:22:7:22:8 | g1 | g1 | test.c:41:6:41:45 | root4_call_thread_without_initialization | root4_call_thread_without_initialization | +| test.c:58:11:58:13 | & ... | Thread specific storage pointer '$@' used before initialization from entry point function '$@'. | test.c:56:7:56:8 | g5 | g5 | test.c:67:6:67:50 | root6_spawn_thread_uninitialized_thread_local | root6_spawn_thread_uninitialized_thread_local | diff --git a/c/misra/test/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.qlref b/c/misra/test/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.qlref new file mode 100644 index 0000000000..10d9aadf1b --- /dev/null +++ b/c/misra/test/rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.qlref @@ -0,0 +1 @@ +rules/RULE-22-20/ThreadStorageNotInitializedBeforeUse.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.expected b/c/misra/test/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.expected new file mode 100644 index 0000000000..75e9825074 --- /dev/null +++ b/c/misra/test/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.expected @@ -0,0 +1 @@ +| test.c:61:3:61:12 | call to tss_create | Thread specific storage object initialization reachable from threaded function '$@'. | test.c:57:6:57:41 | from_root6_init_and_use_thread_local | from_root6_init_and_use_thread_local | diff --git a/c/misra/test/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.qlref b/c/misra/test/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.qlref new file mode 100644 index 0000000000..d299808814 --- /dev/null +++ b/c/misra/test/rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.qlref @@ -0,0 +1 @@ +rules/RULE-22-20/ThreadStoragePointerInitializedInsideThread.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-20/test.c b/c/misra/test/rules/RULE-22-20/test.c new file mode 100644 index 0000000000..0fe58abdcd --- /dev/null +++ b/c/misra/test/rules/RULE-22-20/test.c @@ -0,0 +1,70 @@ +#include "stdlib.h" +#include "threads.h" + +void use_local_mtxs(int x, int y) { + tss_t l1; + tss_get(&l1); // NON-COMPLIANT + tss_create(&l1, NULL); + tss_get(&l1); // COMPLIANT + + tss_t *l4 = malloc(sizeof(tss_t)); + tss_get(l4); // NON-COMPLIANT + tss_create(l4, NULL); + tss_get(l4); // COMPLIANT +} + +void root1_calls_use_local_mtxs() { + // Since a function exists which calls use_local_mtxs(), that function is not + // a root function. The query should still report unused locals in this case. + use_local_mtxs(1, 2); +} + +tss_t g1; + +void root2_uses_global_tss_t() { + tss_get(&g1); // NON-COMPLIANT +} + +void from_root3_use_global_tss_t() { + tss_get(&g1); // COMPLIANT +} + +void root3_initializes_and_uses_global_tss_t() { + tss_create(&g1, NULL); + from_root3_use_global_tss_t(); +} + +void from_root4_use_global_tss_t(void *arg) { + tss_get(&g1); // NON-COMPLIANT +} + +void root4_call_thread_without_initialization() { + thrd_t t; + thrd_create(&t, &from_root4_use_global_tss_t, NULL); +} + +void from_root5_use_global_tss_t(void *arg) { + tss_get(&g1); // COMPLIANT +} + +void root5_thread_with_initialization() { + tss_create(&g1, NULL); + thrd_t t; + thrd_create(&t, &from_root5_use_global_tss_t, NULL); +} + +mtx_t g5; +void from_root6_init_and_use_thread_local() { + tss_get(&g5); // NON-COMPLIANT + + // Violates recommendation, tss_t initialized within a thread. + tss_create(&g5, NULL); // NON-COMPLIANT + + // Valid if we except the above initialization. + tss_get(&g5); // COMPLIANT +} + +void root6_spawn_thread_uninitialized_thread_local() { + thrd_t t; + thrd_create(&t, &from_root6_init_and_use_thread_local, NULL); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.expected b/c/misra/test/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.expected index 6111072ba8..0365f4980d 100644 --- a/c/misra/test/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.expected +++ b/c/misra/test/rules/RULE-22-3/FileOpenForReadAndWriteOnDifferentStreams.expected @@ -1,3 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (FileOpenForReadAndWriteOnDifferentStreams.ql:39,9-17) | test.c:6:14:6:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:5:14:5:18 | call to fopen | here | | test.c:17:14:17:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:16:14:16:18 | call to fopen | here | | test.c:33:14:33:18 | call to fopen | The same file was already opened $@. Files should not be read and written at the same time using different streams. | test.c:32:14:32:18 | call to fopen | here | diff --git a/c/misra/test/rules/RULE-22-3/test.c b/c/misra/test/rules/RULE-22-3/test.c index 7e51ee9963..de1a6ecb93 100644 --- a/c/misra/test/rules/RULE-22-3/test.c +++ b/c/misra/test/rules/RULE-22-3/test.c @@ -46,7 +46,7 @@ void f8(void) { void f9(void) { char name[50] = "tmp9"; char ext[] = "txt"; - char file[] = strcat(name, ext); + char *file = strcat(name, ext); FILE *fw = fopen(file, "r+"); FILE *fr = fopen(strcat(name, ext), "r"); // NON_COMPLIANT[FALSE_NEGATIVE] } diff --git a/c/misra/test/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.expected b/c/misra/test/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.expected index 0bfce133c5..dbf08e3d3d 100644 --- a/c/misra/test/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.expected +++ b/c/misra/test/rules/RULE-22-4/AttemptToWriteToAReadOnlyStream.expected @@ -1,2 +1,8 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AttemptToWriteToAReadOnlyStream.ql:19,32-40) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AttemptToWriteToAReadOnlyStream.ql:20,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AttemptToWriteToAReadOnlyStream.ql:25,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AttemptToWriteToAReadOnlyStream.ql:31,21-29) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AttemptToWriteToAReadOnlyStream.ql:33,6-14) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AttemptToWriteToAReadOnlyStream.ql:36,28-36) | test.c:10:3:10:9 | call to fprintf | Attempt to write to a $@ opened as read-only. | test.c:9:14:9:18 | call to fopen | stream | | test.c:15:3:15:9 | call to fprintf | Attempt to write to a $@ opened as read-only. | test.c:18:14:18:18 | call to fopen | stream | diff --git a/c/misra/test/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.expected b/c/misra/test/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.expected index 709d8b002c..210a3a9218 100644 --- a/c/misra/test/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.expected +++ b/c/misra/test/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.expected @@ -1,2 +1,10 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:24,28-36) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:25,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:29,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:38,23-31) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:43,17-25) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:52,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:60,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (EofShallBeComparedWithUnmodifiedReturnValues.ql:60,46-54) | test.c:6:7:6:20 | ... != ... | The check is not reliable as the type of the return value of $@ is converted. | test.c:5:14:5:20 | call to getchar | call to getchar | | test.c:13:7:13:15 | ... != ... | The check is not reliable as the type of the return value of $@ is converted. | test.c:12:14:12:20 | call to getchar | call to getchar | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected new file mode 100644 index 0000000000..03852b7cf2 --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected @@ -0,0 +1,3 @@ +| test.c:2:1:2:31 | #define M1 _Generic(1, int : 1) | Generic macro M1 doesn't refer to a macro parameter in controlling expr '1'. | +| test.c:4:1:4:34 | #define M2(X) _Generic(1, int : X) | Generic macro M2 doesn't refer to a macro parameter in controlling expr '1'. | +| test.c:18:1:18:39 | #define M9(X) g(_Generic((Y), int : 1)) | Generic macro M9 doesn't refer to a macro parameter in controlling expr '(Y)'. | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref new file mode 100644 index 0000000000..1ca5f792fa --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref @@ -0,0 +1 @@ +rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected new file mode 100644 index 0000000000..edc4b9270c --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected @@ -0,0 +1 @@ +| test.c:21:3:21:22 | _Generic | $@ in generic expression does not expand a macro parameter. | test.c:21:12:21:12 | 1 | Controlling expression | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref new file mode 100644 index 0000000000..59fae02b7f --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref @@ -0,0 +1 @@ +rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-1/test.c b/c/misra/test/rules/RULE-23-1/test.c new file mode 100644 index 0000000000..c7d33b1a70 --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/test.c @@ -0,0 +1,25 @@ +// NON_COMPLIANT: +#define M1 _Generic(1, int : 1) +// NON_COMPLIANT: +#define M2(X) _Generic(1, int : X) +// COMPLIANT: +#define M3(X) _Generic((X), int : 1) +// COMPLIANT: +#define M4(X) _Generic((X), int : 1) +// COMPLIANT: +#define M5(X) _Generic((X + X), int : 1) +int f1(int a, int b); +// COMPLIANT: +#define M6(X) _Generic(f(1, (X)), int : 1) +#define M7(X) 1 + _Generic((X), int : 1) +// COMPLIANT: +#define M8(X) g(_Generic((X), int : 1)) +// NON_COMPLIANT: +#define M9(X) g(_Generic((Y), int : 1)) + +void f2() { + _Generic(1, int : 1); // NON_COMPLIANT + M1; // NON_COMPLIANT + M2(1); // NON_COMPLIANT + M3(1); // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected new file mode 100644 index 0000000000..b0a970bbcf --- /dev/null +++ b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected @@ -0,0 +1,3 @@ +| test.c:4:1:4:38 | #define M2(X) _Generic((X)++, int : 1) | Generic selection macro M2 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:4:1:4:38 | #define M2(X) _Generic((X)++, int : 1) | (ignored) | +| test.c:7:1:7:39 | #define M3(X) _Generic(l1++, int : (X)) | Generic selection macro M3 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:7:1:7:39 | #define M3(X) _Generic(l1++, int : (X)) | (ignored) | +| test.c:42:1:44:25 | #define M5(X) static volatile l ## X; _Generic(l ## X, int : 1) | Generic selection in macro M5 contains an invocation-dependent side effect which is not from macro invocation arguments, for example $@. | test.c:47:3:47:7 | _Generic | side effect 'la' | diff --git a/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref new file mode 100644 index 0000000000..bb3e39a58c --- /dev/null +++ b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref @@ -0,0 +1 @@ +rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-2/test.c b/c/misra/test/rules/RULE-23-2/test.c new file mode 100644 index 0000000000..9e4c6ca6b2 --- /dev/null +++ b/c/misra/test/rules/RULE-23-2/test.c @@ -0,0 +1,49 @@ +#define M1(X) _Generic((X), int : 1) + +// NON_COMPLIANT: +#define M2(X) _Generic((X)++, int : 1) + +// NON_COMPLIANT: +#define M3(X) _Generic(l1++, int : (X)) + +// COMPLIANT: +#define M3_WRAPPER(X) M3(X) + +#define M4(X) _Generic((X)(), int : 1) + +void f1() { + int l1; + + _Generic(1, int : 1); // COMPLIANT + M1(1); // COMPLIANT + _Generic(l1, int : 1); // COMPLIANT + M1(l1); // COMPLIANT + + _Generic(l1++, + int : 1); // COMPLIANT: side effect is not from a macro argument. + M1(l1++); // COMPLIANT + M2(l1); // NON-COMPLIANT: at macro definition + M3(1); // NON-COMPLIANT: at macro definition + M3_WRAPPER(1); // NON-COMPLIANT: at definition of M3 +} + +int g1; +int pure() { return g1; } + +int impure() { return g1++; } + +void f2() { + M1(pure()); // COMPLIANT + M1(impure()); // COMPLIANT + M4(pure); // COMPLIANT + M4(impure); // NON_COMPLIANT[False negative] +} + +#define M5(X) \ + static volatile l##X; \ + _Generic(l##X, int : 1) + +void f3() { + M5(a); // NON-COMPLIANT + M5(b); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected new file mode 100644 index 0000000000..6a56026947 --- /dev/null +++ b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected @@ -0,0 +1,2 @@ +| test.c:2:1:2:36 | #define M1 _Generic(1, default : 1); | Generic selection contains no non-default association. | +| test.c:14:3:14:26 | _Generic | Generic selection contains no non-default association. | diff --git a/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref new file mode 100644 index 0000000000..b44de9083e --- /dev/null +++ b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref @@ -0,0 +1 @@ +rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-3/test.c b/c/misra/test/rules/RULE-23-3/test.c new file mode 100644 index 0000000000..616c14bc80 --- /dev/null +++ b/c/misra/test/rules/RULE-23-3/test.c @@ -0,0 +1,23 @@ +// NON-COMPLIANT +#define M1 _Generic(1, default : 1); +// COMPLIANT +#define M2 _Generic(1, int : 1); +// COMPLIANT +#define M3 _Generic(1, int : 1, default : 1); +// COMPLIANT +#define M4 _Generic(1, int : 1, long : 1); + +void f() { + // Invalid generics: + // _Generic(1); + // _Generic(1, void: 1); + _Generic(1, default : 1); // NON-COMPLIANT + _Generic(1, int : 1); // COMPLIANT + _Generic(1, int : 1, default : 1); // COMPLIANT + _Generic(1, int : 1, long : 1); // COMPLIANT + + M1; + M2; + M3; + M4; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected new file mode 100644 index 0000000000..132bb82979 --- /dev/null +++ b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected @@ -0,0 +1,13 @@ +| test.c:11:24:11:24 | 1 | Generic selection uses unselectable type 'const int', due to qualifiers removed'. | test.c:11:24:11:24 | 1 | side effect | +| test.c:12:27:12:27 | 1 | Generic selection uses unselectable type 'volatile int', due to qualifiers removed'. | test.c:12:27:12:27 | 1 | side effect | +| test.c:13:26:13:26 | 1 | Generic selection uses unselectable type '_Atomic(int)', due to qualifiers removed'. | test.c:13:26:13:26 | 1 | side effect | +| test.c:16:33:16:33 | 1 | Generic selection uses unselectable type 'const volatile int', due to qualifiers removed'. | test.c:16:33:16:33 | 1 | side effect | +| test.c:18:24:18:24 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:18:24:18:24 | 1 | side effect | +| test.c:19:26:19:26 | 1 | Generic selection uses unselectable type 'struct *', due to containing an anonymous struct or union type'. | test.c:19:26:19:26 | 1 | side effect | +| test.c:24:23:24:23 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:24:23:24:23 | 1 | side effect | +| test.c:25:25:25:25 | 1 | Generic selection uses unselectable type 'union *', due to containing an anonymous struct or union type'. | test.c:25:25:25:25 | 1 | side effect | +| test.c:31:21:31:21 | 1 | Generic selection uses unselectable type 'int[3]', due to array-to-pointer decay'. | test.c:31:21:31:21 | 1 | side effect | +| test.c:40:1:40:55 | #define M1(X) _Generic((X), const int : 1, default : 0) | Generic in macro M1 has unselectable type 'const int', due to qualifiers removed. | test.c:40:1:40:55 | #define M1(X) _Generic((X), const int : 1, default : 0) | (ignored) | +| test.c:42:1:42:48 | #define M2(X) _Generic(1, X[3] : 1, default : 0) | Generic in macro M2 has an invocation-dependent unselectable type, for example $@. | test.c:49:3:49:10 | 1 | 'char[3]', due to array-to-pointer decay | +| test.c:52:3:52:15 | M3(X) | Generic resulting from invocation of macro $@ contains an unselectable type 'const int', due to qualifiers removed. | test.c:44:1:44:45 | #define M3(X) _Generic(1, X : 1, default : 0) | M3 | +| test.c:64:24:64:24 | 1 | Generic selection uses unselectable type 'const_int', due to qualifiers removed'. | test.c:64:24:64:24 | 1 | side effect | diff --git a/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref new file mode 100644 index 0000000000..1214be7ce2 --- /dev/null +++ b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref @@ -0,0 +1 @@ +rules/RULE-23-4/GenericAssociationWithUnselectableType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-4/test.c b/c/misra/test/rules/RULE-23-4/test.c new file mode 100644 index 0000000000..cfef26318c --- /dev/null +++ b/c/misra/test/rules/RULE-23-4/test.c @@ -0,0 +1,73 @@ +typedef struct { +} empty_struct_t; +struct empty_struct {}; +typedef union { +} empty_union_t; +union empty_union {}; + +void f() { + _Generic(1, + int : 1, // COMPLIANT + const int : 1, // NON-COMPLIANT + volatile int : 1, // NON-COMPLIANT + _Atomic int : 1, // NON-COMPLIANT + int * : 1, // COMPLIANT + int const * : 1, // COMPLIANT + const volatile int : 1, // NON-COMPLIANT + int volatile const * : 1, // COMPLIANT + struct {} : 1, // NON-COMPLIANT + struct {} * : 1, // NON-COMPLIANT + empty_struct_t : 1, // COMPLIANT + struct empty_struct : 1, // COMPLIANT + empty_struct_t * : 1, // COMPLIANT + struct empty_struct * : 1, // COMPLIANT + union {} : 1, // NON-COMPLIANT + union {} * : 1, // NON-COMPLIANT + empty_union_t : 1, // COMPLIANT + union empty_union : 1, // COMPLIANT + empty_union_t * : 1, // COMPLIANT + union empty_union * : 1, // COMPLIANT + // int[]: 1, // compile error + int[3] : 1, // NON-COMPLIANT + int(*)[3] : 1, // COMPLIANT: pointer to array OK + // int (int*): 1, // compile error + int (*)(int *) : 1, // COMPLIANT: function pointers OK + default : 1 // COMPLIANT + ); +} + +// NON-COMPLIANT +#define M1(X) _Generic((X), const int : 1, default : 0) +// NON-COMPLIANT +#define M2(X) _Generic(1, X[3] : 1, default : 0) +// COMPLIANT +#define M3(X) _Generic(1, X : 1, default : 0) + +void f2() { + M1(1); + M2(int); + M2(char); + + M3(int); // COMPLIANT + M3(const int); // NON-COMPLIANT +} + +typedef int int_t; +typedef int *int_ptr; +const typedef int const_int; +const typedef int *const_int_ptr; +typedef long const *long_const_ptr; + +void f3() { + _Generic(1, + int_t : 1, // COMPLIANT + const_int : 1, // NON-COMPLIANT + const_int_ptr : 1, // COMPLIANT + long_const_ptr : 1, // COMPLIANT + const int_ptr : 1, // COMPLIANT + default : 1 // COMPLIANT + ); +} + +// Type written here so it gets added to the database, see LvalueConversion.qll. +char *g; \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected new file mode 100644 index 0000000000..3ed6b3f26b --- /dev/null +++ b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected @@ -0,0 +1,84 @@ +| test.c:41:3:41:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:41:3:41:46 | _Generic | | +| test.c:42:3:42:49 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:42:3:42:49 | _Generic | | +| test.c:43:3:43:55 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:43:3:43:55 | _Generic | | +| test.c:44:3:44:41 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:44:3:44:41 | _Generic | | +| test.c:45:3:45:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:45:3:45:47 | _Generic | | +| test.c:46:3:46:56 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:46:3:46:56 | _Generic | | +| test.c:48:3:48:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:48:3:48:40 | _Generic | | +| test.c:50:3:50:41 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:50:3:50:41 | _Generic | | +| test.c:51:3:51:47 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:51:3:51:47 | _Generic | | +| test.c:52:3:52:56 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:52:3:52:56 | _Generic | | +| test.c:57:3:57:55 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:57:3:57:55 | _Generic | | +| test.c:59:3:59:40 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:59:3:59:40 | _Generic | | +| test.c:61:3:61:55 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:61:3:61:55 | _Generic | | +| test.c:62:3:62:41 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:62:3:62:41 | _Generic | | +| test.c:63:3:63:56 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:63:3:63:56 | _Generic | | +| test.c:69:3:69:40 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:69:3:69:40 | _Generic | | +| test.c:70:3:70:46 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:70:3:70:46 | _Generic | | +| test.c:71:3:71:49 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:71:3:71:49 | _Generic | | +| test.c:73:3:73:41 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:73:3:73:41 | _Generic | | +| test.c:74:3:74:47 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:74:3:74:47 | _Generic | | +| test.c:75:3:75:56 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:75:3:75:56 | _Generic | | +| test.c:77:3:77:40 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:77:3:77:40 | _Generic | | +| test.c:78:3:78:46 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const int *. | test.c:78:3:78:46 | _Generic | | +| test.c:79:3:79:49 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to volatile int *. | test.c:79:3:79:49 | _Generic | | +| test.c:80:3:80:55 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile int *. | test.c:80:3:80:55 | _Generic | | +| test.c:82:3:82:47 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const void *. | test.c:82:3:82:47 | _Generic | | +| test.c:83:3:83:56 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile void *. | test.c:83:3:83:56 | _Generic | | +| test.c:85:3:85:40 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to int *. | test.c:85:3:85:40 | _Generic | | +| test.c:86:3:86:46 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const int *. | test.c:86:3:86:46 | _Generic | | +| test.c:87:3:87:55 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile int *. | test.c:87:3:87:55 | _Generic | | +| test.c:88:3:88:41 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to void *. | test.c:88:3:88:41 | _Generic | | +| test.c:90:3:90:56 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile void *. | test.c:90:3:90:56 | _Generic | | +| test.c:94:3:94:40 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to int *. | test.c:94:3:94:40 | _Generic | | +| test.c:95:3:95:46 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const int *. | test.c:95:3:95:46 | _Generic | | +| test.c:96:3:96:49 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to volatile int *. | test.c:96:3:96:49 | _Generic | | +| test.c:97:3:97:55 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const volatile int *. | test.c:97:3:97:55 | _Generic | | +| test.c:98:3:98:41 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to void *. | test.c:98:3:98:41 | _Generic | | +| test.c:99:3:99:47 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const void *. | test.c:99:3:99:47 | _Generic | | +| test.c:119:3:119:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:119:3:119:47 | _Generic | | +| test.c:120:3:120:50 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:120:3:120:50 | _Generic | | +| test.c:121:3:121:56 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:121:3:121:56 | _Generic | | +| test.c:122:3:122:42 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:122:3:122:42 | _Generic | | +| test.c:123:3:123:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:123:3:123:48 | _Generic | | +| test.c:124:3:124:57 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:124:3:124:57 | _Generic | | +| test.c:126:3:126:41 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:126:3:126:41 | _Generic | | +| test.c:128:3:128:42 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:128:3:128:42 | _Generic | | +| test.c:129:3:129:48 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:129:3:129:48 | _Generic | | +| test.c:130:3:130:57 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:130:3:130:57 | _Generic | | +| test.c:135:3:135:56 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:135:3:135:56 | _Generic | | +| test.c:137:3:137:41 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:137:3:137:41 | _Generic | | +| test.c:139:3:139:56 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:139:3:139:56 | _Generic | | +| test.c:140:3:140:42 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:140:3:140:42 | _Generic | | +| test.c:141:3:141:57 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:141:3:141:57 | _Generic | | +| test.c:147:3:147:41 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:147:3:147:41 | _Generic | | +| test.c:148:3:148:47 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:148:3:148:47 | _Generic | | +| test.c:149:3:149:50 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:149:3:149:50 | _Generic | | +| test.c:151:3:151:42 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:151:3:151:42 | _Generic | | +| test.c:152:3:152:48 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:152:3:152:48 | _Generic | | +| test.c:153:3:153:57 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:153:3:153:57 | _Generic | | +| test.c:156:3:156:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:156:3:156:47 | _Generic | | +| test.c:157:3:157:50 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:157:3:157:50 | _Generic | | +| test.c:158:3:158:56 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:158:3:158:56 | _Generic | | +| test.c:159:3:159:42 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:159:3:159:42 | _Generic | | +| test.c:160:3:160:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:160:3:160:48 | _Generic | | +| test.c:161:3:161:57 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:161:3:161:57 | _Generic | | +| test.c:163:3:163:41 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:163:3:163:41 | _Generic | | +| test.c:165:3:165:42 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:165:3:165:42 | _Generic | | +| test.c:166:3:166:48 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:166:3:166:48 | _Generic | | +| test.c:167:3:167:57 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:167:3:167:57 | _Generic | | +| test.c:172:3:172:56 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:172:3:172:56 | _Generic | | +| test.c:180:3:180:50 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:180:3:180:50 | _Generic | | +| test.c:188:3:192:16 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:188:3:192:16 | _Generic | | +| test.c:201:3:201:49 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:201:3:201:49 | _Generic | | +| test.c:202:3:202:49 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:202:3:202:49 | _Generic | | +| test.c:216:3:216:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:216:3:216:46 | _Generic | | +| test.c:217:3:217:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int_t *. | test.c:217:3:217:48 | _Generic | | +| test.c:218:3:218:44 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to c_int_t *. | test.c:218:3:218:44 | _Generic | | +| test.c:222:3:222:47 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int *. | test.c:222:3:222:47 | _Generic | | +| test.c:223:3:223:49 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int_t *. | test.c:223:3:223:49 | _Generic | | +| test.c:224:3:224:45 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to c_int_t *. | test.c:224:3:224:45 | _Generic | | +| test.c:226:3:226:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:226:3:226:40 | _Generic | | +| test.c:227:3:227:42 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int_t *. | test.c:227:3:227:42 | _Generic | | +| test.c:232:3:232:41 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int *. | test.c:232:3:232:41 | _Generic | | +| test.c:233:3:233:43 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int_t *. | test.c:233:3:233:43 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref new file mode 100644 index 0000000000..c6b02b6273 --- /dev/null +++ b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref @@ -0,0 +1 @@ +rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-5/test.c b/c/misra/test/rules/RULE-23-5/test.c new file mode 100644 index 0000000000..5d48cc58bd --- /dev/null +++ b/c/misra/test/rules/RULE-23-5/test.c @@ -0,0 +1,237 @@ +void f1(); + +void f2() { + int l1; + int *l2; + const int *l3; + volatile int *l4; + volatile const int *l5; + void *l6; + const void *l7; + volatile void *l8; + const volatile void *l9; + + // No violation for missing pointer/integral conversions: + _Generic(l1, // COMPLIANT + int *: f1, + const int *: f1, + volatile int *: f1, + void *: f1, + const void *: f1, + default: f1); // COMPLIANT + _Generic(l2, int : f1, default : f1); // COMPLIANT + _Generic(l3, int : f1, default : f1); // COMPLIANT + _Generic(l4, int : f1, default : f1); // COMPLIANT + _Generic(l5, int : f1, default : f1); // COMPLIANT + + // Compliant, default case is not matched + _Generic(l1, int : f1); // COMPLIANT + _Generic(l2, int * : f1); // COMPLIANT + _Generic(l3, const int * : f1); // COMPLIANT + _Generic(l4, volatile int * : f1); // COMPLIANT + _Generic(l5, volatile const int * : f1); // COMPLIANT + _Generic(l6, void * : f1); // COMPLIANT + _Generic(l7, const void * : f1); // COMPLIANT + _Generic(l8, volatile void * : f1); // COMPLIANT + _Generic(l9, const volatile void * : f1); // COMPLIANT + + // Violation, match default case due to lack of pointer to pointer + // conversions: + _Generic(l2, int * : f1, default : f1); // COMPLIANT + _Generic(l2, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l3, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const int * : f1, default : f1); // COMPLIANT + _Generic(l3, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const volatile void * : f1, default : f1); // NON-COMPLIANT + // Obviously not volatile: + _Generic(l3, volatile int * : f1, default : f1); // COMPLIANT + // Debatable, but volatile const int* is assignable to const int* so its + // considered risky + _Generic(l3, const volatile int * : f1, default : f1); // NON-COMPLIANT + + _Generic(l4, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l4, volatile int * : f1, default : f1); // COMPLIANT + _Generic(l4, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l4, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l4, const volatile void * : f1, default : f1); // NON-COMPLIANT + // Debatable, but volatile int* isn't assignable to const int* or vice versa. + _Generic(l4, const int * : f1, default : f1); // COMPLIANT + // Debatable, but volatile int* isn't assignable to const void* or vice versa. + _Generic(l4, const void * : f1, default : f1); // COMPLIANT + + _Generic(l5, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const volatile int * : f1, default : f1); // COMPLIANT + _Generic(l5, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l6, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, void * : f1, default : f1); // COMPLIANT + _Generic(l6, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l7, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, const void * : f1, default : f1); // COMPLIANT + _Generic(l7, const volatile void * : f1, default : f1); // NON-COMPLIANT + // Debatable, but const void* isn't assignable to volatile int* or vice versa. + _Generic(l7, volatile int * : f1, default : f1); // COMPLIANT + + _Generic(l9, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const volatile void * : f1, default : f1); // COMPLIANT + + /** + * Edge case 1: The controlling expression undergoes lvalue conversion, so + * arrays become pointers and qualifiers on pointers are stripped. + */ + int l10[3]; + const int l11[3]; + volatile int l12[3]; + const volatile int l13[3]; + int *const l14; + const int *const l15; + + _Generic(l10, int * : f1, default : f1); // COMPLIANT + _Generic(l11, const int * : f1, default : f1); // COMPLIANT + _Generic(l12, volatile int * : f1, default : f1); // COMPLIANT + _Generic(l13, const volatile int * : f1, default : f1); // COMPLIANT + + _Generic(l10, int * : f1, default : f1); // COMPLIANT + _Generic(l10, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l11, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l11, const int * : f1, default : f1); // COMPLIANT + _Generic(l11, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l11, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l11, const volatile void * : f1, default : f1); // NON-COMPLIANT + // Obviously not volatile: + _Generic(l11, volatile int * : f1, default : f1); // COMPLIANT + // Debatable, but volatile const int* is assignable to const int* so its + // considered risky + _Generic(l11, const volatile int * : f1, default : f1); // NON-COMPLIANT + + _Generic(l12, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l12, volatile int * : f1, default : f1); // COMPLIANT + _Generic(l12, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l12, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l12, const volatile void * : f1, default : f1); // NON-COMPLIANT + // Debatab12e, but volatile int* isn't assignable to const int* or vice versa. + _Generic(l12, const int * : f1, default : f1); // COMPLIANT + // Debatable, but volatile int* isn't assignable to const void* or vice versa. + _Generic(l12, const void * : f1, default : f1); // COMPLIANT + + _Generic(l13, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const volatile int * : f1, default : f1); // COMPLIANT + _Generic(l13, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l14, int * : f1, default : f1); // COMPLIANT + _Generic(l14, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l15, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l15, const int * : f1, default : f1); // COMPLIANT + _Generic(l15, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l15, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l15, const volatile void * : f1, default : f1); // NON-COMPLIANT + // Obviously not volatile: + _Generic(l15, volatile int * : f1, default : f1); // COMPLIANT + // Debatable, but volatile const int* is assignable to const int* so its + // considered risky + _Generic(l15, const volatile int * : f1, default : f1); // NON-COMPLIANT + + /** + * Edge case 2: Types don't have to be identical to be compatible. + */ + int(*l16)[3]; + + // This is a risky conversion that should be reported: + _Generic(l16, int(*const)[3] : f1, default : f1); // NON-COMPLIANT + // However, in this one, there is a match on the second selector, because it + // it is an array type with a compatible element type, and sizes only have to + // match if both arrays have a constant size. Therefore, the default selector + // is not chosen and this is not a violation. + _Generic(l16, int(*const)[3] : f1, int(*)[] : f1, default : f1); // COMPLIANT + // In this case, the second selector is not a compatible type because the + // array has a constant size that doesn't match, and this should be reported. + _Generic(l16, int(*const)[3] + : f1, int(*)[4] + : f1, + default + : f1); // NON-COMPLIANT + + /** + * Edge case 3: Conversion on _Generic, make sure we use the fully converted + * type when considering compliance. + */ + int *l17; + void *l18; + _Generic((void *)l17, void * : f1, default : f1); // COMPLIANT + _Generic((void *)l17, int * : f1, default : f1); // NON-COMPLIANT + _Generic((int *)l18, void * : f1, default : f1); // NON-COMPLIANT + _Generic((int *)l18, int * : f1, default : f1); // COMPLIANT + + /** + * Edge case 4: Typedefs must be resolved properly. + */ + typedef int int_t; + const typedef int c_int_t; + int_t *l19; + c_int_t *l20; + volatile c_int_t *l21; + + _Generic(l2, int * : f1, default : f1); // COMPLIANT + _Generic(l2, int_t * : f1, default : f1); // COMPLIANT + _Generic(l2, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, c_int_t * : f1, default : f1); // NON-COMPLIANT + + _Generic(l19, int * : f1, default : f1); // COMPLIANT + _Generic(l19, int_t * : f1, default : f1); // COMPLIANT + _Generic(l19, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l19, const int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l19, c_int_t * : f1, default : f1); // NON-COMPLIANT + + _Generic(l3, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const int * : f1, default : f1); // COMPLIANT + _Generic(l3, const int_t * : f1, default : f1); // COMPLIANT + _Generic(l3, c_int_t * : f1, default : f1); // COMPLIANT + + _Generic(l20, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l20, int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l20, const int * : f1, default : f1); // COMPLIANT + _Generic(l20, const int_t * : f1, default : f1); // COMPLIANT + _Generic(l20, c_int_t * : f1, default : f1); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected new file mode 100644 index 0000000000..4f02d039ce --- /dev/null +++ b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected @@ -0,0 +1,4 @@ +| test.c:11:3:11:8 | _Generic | Controlling expression in generic macro $@ has standard type (unnamed enum), which doesn't match its essential type (unnamed enum). | test.c:6:1:6:75 | #define M1(X) _Generic((X), int : 1, unsigned int : 1, short : 2, long : 3) | M1 | +| test.c:15:3:15:13 | _Generic | Controlling expression in generic macro $@ has standard type int, which doesn't match its essential type short. | test.c:6:1:6:75 | #define M1(X) _Generic((X), int : 1, unsigned int : 1, short : 2, long : 3) | M1 | +| test.c:18:3:18:24 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type char. | test.c:18:3:18:24 | _Generic | | +| test.c:19:3:19:55 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type short. | test.c:19:3:19:55 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref new file mode 100644 index 0000000000..b91bbefec6 --- /dev/null +++ b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref @@ -0,0 +1 @@ +rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-6/test.c b/c/misra/test/rules/RULE-23-6/test.c new file mode 100644 index 0000000000..2b18f4cd2c --- /dev/null +++ b/c/misra/test/rules/RULE-23-6/test.c @@ -0,0 +1,33 @@ +short l1; +int l2; +long l3; +enum { E1 } l4; + +#define M1(X) _Generic((X), int : 1, unsigned int : 1, short : 2, long : 3) +void f1() { + M1(l1); // COMPLIANT + M1(l2); // COMPLIANT + M1(l3); // COMPLIANT + M1(l4); // NON-COMPLIANT + + M1(1); // COMPLIANT + M1(1u); // COMPLIANT + M1(l1 + l1); // NON-COMPLIANT + M1((int)(l1 + l1)); // COMPLIANT + M1('c'); // NON-COMPLIANT[false negative] + _Generic('c', int : 1); // NON-COMPLIANT + _Generic(_Generic(0, default : l1 + l1), default : 1); // NON-COMPLIANT + _Generic(((short)_Generic(0, default : (l1 + l1))), default : 1); // COMPLIANT +} + +void f2() { + // Edge case: lvalue conversion of a const struct yields an implicit + // conversion to a non-const struct which is ignored by EssentialTypes.qll, + // meaning the essential type does not match the static type. However, we + // shouldn't report an issue here as the static/essential types are not one + // of the essential type categories. + struct S1 { + int m1; + }; + _Generic((const struct S1){.m1 = 0}, default : 1); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected new file mode 100644 index 0000000000..47a8acce92 --- /dev/null +++ b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected @@ -0,0 +1,12 @@ +| test.c:9:1:9:53 | #define M3(X) _Generic((X), int : f1(X), default : 0) | Generic macro M3 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | +| test.c:10:1:10:63 | #define M4(X) (X) + _Generic((X), int : f1(X), default : f1(X)) | Generic macro M4 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:11:1:11:63 | #define M5(X) _Generic((X), int : f1(X), default : f1(X)) + (X) | Generic macro M5 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:12:1:12:65 | #define M6(X) _Generic((X), int : f1((X) + (X)), default : f1(X)) | Generic macro M6 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | +| test.c:21:1:21:37 | #define M9(X) _Generic((X), int : f1) | Generic macro M9 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | +| test.c:23:1:23:41 | #define M10(X) _Generic((X), int : f1(1)) | Generic macro M10 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | +| test.c:32:1:32:60 | #define M12(X) _Generic((X) + (X), int : f1(X), default : 1) | Generic macro M12 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | +| test.c:33:1:33:70 | #define M13(X) _Generic((X) + (X), int : f1(X), default : f1(X)) + (X) | Generic macro M13 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:43:1:43:79 | #define M17(X,Y) _Generic((X) + (Y), int : f2((X), (Y)), default : f2((X), 1)) | Generic macro M17 may have unexpected behavior from side effects in parameter Y, as it is not expanded in generic selection 2. | +| test.c:68:1:68:80 | #define M26(X) _Generic((X), int : IGNORE_2ND(X, X), default : IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | +| test.c:68:1:68:80 | #define M26(X) _Generic((X), int : IGNORE_2ND(X, X), default : IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 2 more than once. | +| test.c:69:1:69:77 | #define M27(X) _Generic((X), int : f1(IGNORE(X)), default : f1(IGNORE(X)))(X) | Generic macro M27 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | diff --git a/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref new file mode 100644 index 0000000000..3156bdce91 --- /dev/null +++ b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref @@ -0,0 +1 @@ +rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-7/test.c b/c/misra/test/rules/RULE-23-7/test.c new file mode 100644 index 0000000000..e2f5e98ee5 --- /dev/null +++ b/c/misra/test/rules/RULE-23-7/test.c @@ -0,0 +1,72 @@ +int f1(int p1); +int f2(int p1, int p2); + +// COMPLIANT -- standard correct cases: +#define M1(X) _Generic((X), int : f1, default : f1)(X) +#define M2(X) _Generic((X), int : f1(X), default : f1(X)) + +// NON-COMPLIANT -- standard incorrect cases: +#define M3(X) _Generic((X), int : f1(X), default : 0) +#define M4(X) (X) + _Generic((X), int : f1(X), default : f1(X)) +#define M5(X) _Generic((X), int : f1(X), default : f1(X)) + (X) +#define M6(X) _Generic((X), int : f1((X) + (X)), default : f1(X)) + +// Compliant by exception +// COMPLIANT +#define M7(X) _Generic((X), int : 1, default : 0) +// NON-COMPLIANT[FALSE NEGATIVE] -- Without an expansion, we can't tell if this +// macro has only constant expressions or not. +#define M8(X) _Generic((X), int : f1(1)) +// NON-COMPLIANT -- If the macro is expanded we can detect constant expressions +#define M9(X) _Generic((X), int : f1) +// NON-COMPLIANT -- If the macro is expanded we can detect constant expressions +#define M10(X) _Generic((X), int : f1(1)) +void f3() { + M9(1); + M10(1); +} + +// COMPLIANT -- multiple uses in the controlling expression is OK: +#define M11(X) _Generic((X) + (X), int : f1(X), default : f1(X)) +// NON-COMPLIANT -- the rule should still be enforced otherwise: +#define M12(X) _Generic((X) + (X), int : f1(X), default : 1) +#define M13(X) _Generic((X) + (X), int : f1(X), default : f1(X)) + (X) + +// COMPLIANT -- the argument is not used in the controlling expression: +#define M14(X) _Generic(1, int : f1((X) + (X)), default : f1(X)) +#define M15(X) _Generic(1, int : f1(X), default : f1(X)) + (X) + +// Test cases with more than one argument: +// COMPLIANT -- Y is not used in the controlling expression: +#define M16(X, Y) _Generic((X), int : f2((X), (Y)), default : f2((X), 1)) +// NON-COMPLIANT -- Y is used in the controlling expression +#define M17(X, Y) _Generic((X) + (Y), int : f2((X), (Y)), default : f2((X), 1)) +// COMPLIANT -- Y is used in the controlling expression correctly +#define M18(X, Y) \ + _Generic((X) + (Y), int : f2((X), (Y)), default : f2((X), (Y))) + +// Test unevaluated contexts: +// COMPLIANT -- sizeof is not evaluated: +#define M19(X) _Generic((X), int[sizeof(X)] : f1, default : f1)(X) +#define M20(X) _Generic((X), int : f1(sizeof(X)), default : f1)(X) +#define M21(X) _Generic((X), int : f1(X), default : f1(X)) + sizeof(X) +// NON-COMPLIANT[FALSE NEGATIVE] -- sizeof plus evaluated context +#define M22(X) _Generic((X), int : f1(sizeof(X) + X), default : f1(X))(X) +// NON-COMPLIANT[FALSE NEGATIVE] -- array type sizes may be evaluated +#define M23(X) _Generic((X), int[X] : f1, default : f1)(X) +// COMPLIANT -- alignof, typeof are not evaluated: +#define M24(X) _Generic((X), int[X] : f1, default : f1)(X) + +// Nested macros: +#define ONCE(X) (X) +#define TWICE(X) (X) + (X) +#define IGNORE(X) (1) +#define IGNORE_2ND(X, Y) (X) +// COMPLIANT +#define M25(X) _Generic((X), int: ONCE(f1(X)), default: ONCE(f1(X)) +// COMPLIANT[FALSE POSITIVE] +#define M26(X) _Generic((X), int : IGNORE_2ND(X, X), default : IGNORE_2ND(X, X)) +#define M27(X) _Generic((X), int : f1(IGNORE(X)), default : f1(IGNORE(X)))(X) +// NON-COMPLIANT[FASE NEGATIVE] +#define M28(X) _Generic((X), int : f1(IGNORE(X)), default : f1(IGNORE(X))) +#define M29(X) _Generic((X), int : TWICE(f1(X)), default : TWICE(f1(X))) \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected new file mode 100644 index 0000000000..fb407e2ff1 --- /dev/null +++ b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected @@ -0,0 +1,4 @@ +| test.c:11:1:11:67 | #define M4(X) _Generic((X), int : 1, default : 0, unsigned int : 2) | Generic macro M4 has default as 2nd association, which is not first or last. | test.c:11:1:11:67 | #define M4(X) _Generic((X), int : 1, default : 0, unsigned int : 2) | (ignored) | +| test.c:17:1:17:62 | #define M5(__VA_ARGS__...) _Generic(0, __VA_ARGS__, default : 0, int : 1) | Generic macro M5 has a default association which is not first or last, for example $@. | test.c:30:3:30:22 | _Generic | 2nd | +| test.c:37:3:37:27 | M6(__VA_ARGS__...) | Generic macro $@, in this expansion, has default as 2nd association, which is not first or last. | test.c:19:1:19:49 | #define M6(__VA_ARGS__...) _Generic(0, __VA_ARGS__, int : 1) | M6 | +| test.c:48:3:48:53 | _Generic | Generic has default as 2nd association, which is not first or last. | test.c:48:3:48:53 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref new file mode 100644 index 0000000000..06fe786e7d --- /dev/null +++ b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref @@ -0,0 +1 @@ +rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-8/test.c b/c/misra/test/rules/RULE-23-8/test.c new file mode 100644 index 0000000000..340ddb16db --- /dev/null +++ b/c/misra/test/rules/RULE-23-8/test.c @@ -0,0 +1,49 @@ +/** + * Cases where the macro itself is always compliant or non compliant: + */ +// COMPLIANT +#define M1(X) _Generic((X), int : 1, unsigned int : 2) +// COMPLIANT +#define M2(X) _Generic((X), int : 1, unsigned int : 2, default : 0) +// COMPLIANT +#define M3(X) _Generic((X), default : 0, int : 1, unsigned int : 2) +// NON-COMPLIANT +#define M4(X) _Generic((X), int : 1, default : 0, unsigned int : 2) + +/** + * Macros that are compliant or not based on use: + */ +// NON-COMPLIANT: because every use is non compliant +#define M5(...) _Generic(0, __VA_ARGS__, default : 0, int : 1) +// COMPLIANT: because some uses are compliant +#define M6(...) _Generic(0, __VA_ARGS__, int : 1) + +void f1() { + M1(0); // COMPLIANT + M2(0); // COMPLIANT + M3(0); // COMPLIANT + M4(0); // COMPLIANT: the macro invocation is compliant, the macro definition + // is not. + + // COMPLIANT: all invocations of M5 are non compliant so the macro is reported + // instead. + M5(unsigned int : 1); + M5(unsigned int : 1, long : 2); + + // Some invocations of M6() will be compliant, so we'll report the issue at + // each invocation. + M6(default : 0); // COMPLIANT + M6(default : 0, long : 1); // COMPLIANT + M6(long : 1, default : 0); // NON-COMPLIANT +} + +/** + * For completeness, non macro cases, though these are not likely and violate + * RULE-23-1. + */ +void f2() { + _Generic(0, int : 1, unsigned int : 2); // COMPLIANT + _Generic(0, int : 1, unsigned int : 2, default : 0); // COMPLIANT + _Generic(0, default : 0, int : 1, unsigned int : 2); // COMPLIANT + _Generic(0, int : 1, default : 0, unsigned int : 2); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.expected b/c/misra/test/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.expected index 5e876cecc3..3c4cab00b1 100644 --- a/c/misra/test/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.expected +++ b/c/misra/test/rules/RULE-3-1/CharacterSequencesAndUsedWithinAComment.expected @@ -1,3 +1,6 @@ | test.c:9:1:9:8 | /* /* */ | Comment contains an illegal sequence '/*' | | test.c:12:1:12:8 | /* // */ | Comment contains an illegal sequence '//' | | test.c:21:1:21:7 | // /* | Comment contains an illegal sequence '/*' | +| test.c:30:1:30:27 | /* https://github.com // */ | Comment contains an illegal sequence '//' | +| test.c:33:1:33:60 | /* a://b, a://b., ://a.b, a://b., a://.b, ://, a://, ://b */ | Comment contains an illegal sequence '//' | +| test.c:42:1:42:8 | ///* foo | Comment contains an illegal sequence '/*' | diff --git a/c/misra/test/rules/RULE-3-1/test.c b/c/misra/test/rules/RULE-3-1/test.c index c1a135f972..fd7a6574dd 100644 --- a/c/misra/test/rules/RULE-3-1/test.c +++ b/c/misra/test/rules/RULE-3-1/test.c @@ -20,4 +20,25 @@ // NON_COMPLIANT // /* +// COMPLIANT +/* https://github.com */ + +// COMPLIANT +/* https://name-with-hyphen-and-num-12345.com */ + +// NON_COMPLIANT +/* https://github.com // */ + +// NON_COMPLIANT +/* a://b, a://b., ://a.b, a://b., a://.b, ://, a://, ://b */ + +// COMPLIANT +// https://github.com + +// COMPLIANT +//* foo + +// NON_COMPLIANT +///* foo + void f(){} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.expected b/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.expected deleted file mode 100644 index 39d5aa5d85..0000000000 --- a/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.expected +++ /dev/null @@ -1,21 +0,0 @@ -| test.c:9:18:9:24 | \u001aG | Invalid hexadecimal escape in string literal at '\\x1AG"'. | -| test.c:12:18:12:23 | \u00029 | Invalid octal escape in string literal at '\\029"'. | -| test.c:15:18:15:24 | \n7 | Invalid octal escape in string literal at '\\0127"'. | -| test.c:16:18:16:24 | \r7 | Invalid octal escape in string literal at '\\0157"'. | -| test.c:18:19:18:29 | \n\n9 | Invalid octal escape in string literal at '\\0129"'. | -| test.c:19:19:19:28 | \n\u00019 | Invalid octal escape in string literal at '\\019"'. | -| test.c:22:19:22:31 | \nAAA\u000f | Invalid octal escape in string literal at '\\012AAA\\017"'. | -| test.c:25:19:25:39 | Some Data \n\u000fA | Invalid octal escape in string literal at '\\017A"'. | -| test.c:26:19:27:21 | Some Data \n\u000fA5 | Invalid octal escape in string literal at '\\017A"\n "5"'. | -| test.c:28:19:30:25 | Some Data \n\u000fA\n1 | Invalid octal escape in string literal at '\\0121"'. | -| test.c:34:19:35:26 | \u0011G\u00012 | Invalid octal escape in string literal at '\\0012"'. | -| test.c:36:19:37:25 | \u0011GG\u0001 | Invalid hexadecimal escape in string literal at '\\x11G"\n "G\\001"'. | -| test.c:38:19:39:26 | \u0011GG\u00013 | Invalid hexadecimal escape in string literal at '\\x11G"\n "G\\0013"'. | -| test.c:38:19:39:26 | \u0011GG\u00013 | Invalid octal escape in string literal at '\\0013"'. | -| test.c:45:18:45:42 | Some Data \n\u000fA5 | Invalid octal escape in string literal at '\\017A" "5"'. | -| test.c:46:18:46:49 | Some Data \n\u000fA\n1 | Invalid octal escape in string literal at '\\0121"'. | -| test.c:48:18:48:32 | \u0011G\u00012 | Invalid octal escape in string literal at '\\0012"'. | -| test.c:49:18:49:32 | \u0011GG\u0001 | Invalid hexadecimal escape in string literal at '\\x11G" "G\\001"'. | -| test.c:50:18:50:33 | \u0011GG\u00013 | Invalid hexadecimal escape in string literal at '\\x11G" "G\\0013"'. | -| test.c:50:18:50:33 | \u0011GG\u00013 | Invalid octal escape in string literal at '\\0013"'. | -| test.c:53:11:53:16 | 10 | Invalid hexadecimal escape in string literal at '\\x0a''. | diff --git a/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.qlref b/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.qlref deleted file mode 100644 index fbdd187532..0000000000 --- a/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.testref b/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.testref new file mode 100644 index 0000000000..f8b5396a9c --- /dev/null +++ b/c/misra/test/rules/RULE-4-1/OctalAndHexadecimalEscapeSequencesNotTerminated.testref @@ -0,0 +1 @@ +c/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-1/test.c b/c/misra/test/rules/RULE-4-1/test.c deleted file mode 100644 index 4a0dcaa6ac..0000000000 --- a/c/misra/test/rules/RULE-4-1/test.c +++ /dev/null @@ -1,53 +0,0 @@ -const char *a1 = "\x11" - "G"; // COMPLIANT - -const char *a2 = "\x1" - "G"; // COMPLIANT - -const char *a3 = "\x1A"; // COMPLIANT - -const char *a4 = "\x1AG"; // NON_COMPLIANT - -const char *a5 = "\021"; // COMPLIANT -const char *a6 = "\029"; // NON_COMPLIANT -const char *a7 = "\0" - "0"; // COMPLIANT -const char *a8 = "\0127"; // NON_COMPLIANT -const char *a9 = "\0157"; // NON_COMPLIANT - -const char *a10 = "\012\0129"; // NON_COMPLIANT (1x) -const char *a11 = "\012\019"; // NON_COMPLIANT -const char *a12 = "\012\017"; // COMPLIANT - -const char *a13 = "\012AAA\017"; // NON_COMPLIANT (1x) - -const char *a14 = "Some Data \012\017"; // COMPLIANT -const char *a15 = "Some Data \012\017A"; // NON_COMPLIANT (1x) -const char *a16 = "Some Data \012\017A" - "5"; // NON_COMPLIANT (1x) -const char *a17 = "Some Data \012\017" - "A" - "\0121"; // NON_COMPLIANT (1x) - -const char *a18 = "\x11" - "G\001"; // COMPLIANT -const char *a19 = "\x11" - "G\0012"; // NON_COMPLIANT (1x) -const char *a20 = "\x11G" - "G\001"; // NON_COMPLIANT (1x) -const char *a21 = "\x11G" - "G\0013"; // NON_COMPLIANT (2x) - -// clang-format off -const char *b1 = "\x11" "G"; // COMPLIANT -const char *b2 = "\x1" "G"; // COMPLIANT -const char *b3 = "\0" "0"; // COMPLIANT -const char *b4 = "Some Data \012\017A" "5"; // NON_COMPLIANT (1x) -const char *b5 = "Some Data \012\017" "A" "\0121"; // NON_COMPLIANT (1x) -const char *b6 = "\x11" "G\001"; // COMPLIANT -const char *b7 = "\x11" "G\0012"; // NON_COMPLIANT (1x) -const char *b8 = "\x11G" "G\001"; // NON_COMPLIANT (1x) -const char *b9 = "\x11G" "G\0013"; // NON_COMPLIANT (2x) - -char c1 = '\023'; // COMPLIANT -char c2 = '\x0a'; // COMPLIANT diff --git a/c/misra/test/rules/RULE-4-10/NonUniqueIncludeGuards.testref b/c/misra/test/rules/RULE-4-10/NonUniqueIncludeGuards.testref deleted file mode 100644 index e38907a2fc..0000000000 --- a/c/misra/test/rules/RULE-4-10/NonUniqueIncludeGuards.testref +++ /dev/null @@ -1 +0,0 @@ -c/common/test/rules/nonuniqueincludeguardsused/NonUniqueIncludeGuardsUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.qlref b/c/misra/test/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.qlref deleted file mode 100644 index a0602b1905..0000000000 --- a/c/misra/test/rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-4-12/StdLibDynamicMemoryAllocationUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.qlref b/c/misra/test/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.qlref deleted file mode 100644 index 4a5c410c38..0000000000 --- a/c/misra/test/rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-4-8/ObjectWithNoPointerDereferenceShouldBeOpaque.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.expected b/c/misra/test/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.expected new file mode 100644 index 0000000000..b7d33ba120 --- /dev/null +++ b/c/misra/test/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.expected @@ -0,0 +1 @@ +| test.c:8:5:8:68 | iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyB | Identifer iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyB is nondistinct in characters at or over 63 limit, compared to $@ | test.c:2:5:2:68 | iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA | iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA | diff --git a/c/misra/test/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.qlref b/c/misra/test/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.qlref new file mode 100644 index 0000000000..59fc518cf7 --- /dev/null +++ b/c/misra/test/rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.qlref @@ -0,0 +1 @@ +rules/RULE-5-2/IdentifiersDeclaredInTheSameScopeNotDistinct.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-2/test.c b/c/misra/test/rules/RULE-5-2/test.c new file mode 100644 index 0000000000..e299e514bc --- /dev/null +++ b/c/misra/test/rules/RULE-5-2/test.c @@ -0,0 +1,47 @@ +extern int + iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA; // NON_COMPLIANT + // - + // length + // 64 + +static int + iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyB; // NON_COMPLIANT + // - + // length + // 64 + +void f() { + int iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyC; // COMPLIANT + // - + // length + // 64 + // but + // diff + // scope +} + +static int + iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjy_C; // COMPLIANT length <63 +static int + iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjy_D; // COMPLIANT length <63 + +#define iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA // COMPLIANT + // - + // this + // rule + // does + // not + // consider + // macros +extern int + iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA; // COMPLIANT + // - this + // rule + // does + // not + // consider + // when + // both + // identifiers + // are + // external \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-4/MacroIdentifiersNotDistinct.expected b/c/misra/test/rules/RULE-5-4/MacroIdentifiersNotDistinct.expected index 12507b2d3f..b079b7e94d 100644 --- a/c/misra/test/rules/RULE-5-4/MacroIdentifiersNotDistinct.expected +++ b/c/misra/test/rules/RULE-5-4/MacroIdentifiersNotDistinct.expected @@ -1,2 +1,4 @@ +| header3.h:7:1:7:24 | #define MULTIPLE_INCLUDE | Definition of macro MULTIPLE_INCLUDE is not distinct from alternative definition of $@ in header4.h. | header4.h:1:1:1:24 | #define MULTIPLE_INCLUDE | MULTIPLE_INCLUDE | +| header3.h:14:1:14:21 | #define NOT_PROTECTED | Definition of macro NOT_PROTECTED is not distinct from alternative definition of $@ in header4.h. | header4.h:12:1:12:23 | #define NOT_PROTECTED 1 | NOT_PROTECTED | | test.c:2:1:2:72 | #define iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyB | Macro identifer iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyB is nondistinct in first 63 characters, compared to $@. | test.c:1:1:1:72 | #define iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA | iltiqzxgfqsgigwfuyntzghvzltueatcxqnqofnnvjyszmcsylyohvqaosjbqyyA | -| test.c:8:1:8:31 | #define FUNCTION_MACRO(X) X + 1 | Macro identifer FUNCTION_MACRO is nondistinct in first 63 characters, compared to $@. | test.c:7:1:7:57 | #define FUNCTION_MACRO(FUNCTION_MACRO) FUNCTION_MACRO + 1 | FUNCTION_MACRO | +| test.c:8:1:8:31 | #define FUNCTION_MACRO(X) X + 1 | Definition of macro FUNCTION_MACRO is not distinct from alternative definition of $@ in test.c. | test.c:7:1:7:57 | #define FUNCTION_MACRO(FUNCTION_MACRO) FUNCTION_MACRO + 1 | FUNCTION_MACRO | diff --git a/c/misra/test/rules/RULE-5-4/conditional.h b/c/misra/test/rules/RULE-5-4/conditional.h new file mode 100644 index 0000000000..d30701c8e0 --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/conditional.h @@ -0,0 +1,11 @@ +#ifdef FOO +#include "header1.h" +#else +#include "header2.h" +#endif + +#ifdef FOO +#define A_MACRO 1 // COMPLIANT +#else +#define A_MACRO 2 // COMPLIANT +#endif \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-4/header1.h b/c/misra/test/rules/RULE-5-4/header1.h new file mode 100644 index 0000000000..526f4fa659 --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/header1.h @@ -0,0 +1 @@ +#define REPEATED 11 // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-4/header2.h b/c/misra/test/rules/RULE-5-4/header2.h new file mode 100644 index 0000000000..bd5dde123d --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/header2.h @@ -0,0 +1 @@ +#define REPEATED 1 // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-4/header3.h b/c/misra/test/rules/RULE-5-4/header3.h new file mode 100644 index 0000000000..3aa82f5fd7 --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/header3.h @@ -0,0 +1,16 @@ +#ifndef HEADER3_H +#define HEADER3_H + +// We should ignore the header guards in this file + +// This is defined unconditionally by both header3.h and header4.h +#define MULTIPLE_INCLUDE // NON_COMPLIANT + +// This is redefined in header3.h, but only if it isn't already defined +#define PROTECTED // COMPLIANT + +// This is redefined in header3.h, but is conditional on some other condition, +// so this is redefined +#define NOT_PROTECTED // NON_COMPLIANT + +#endif \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-4/header4.h b/c/misra/test/rules/RULE-5-4/header4.h new file mode 100644 index 0000000000..8fa6e8b5e8 --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/header4.h @@ -0,0 +1,13 @@ +#define MULTIPLE_INCLUDE // NON_COMPLIANT + +// This case is triggered from root2.c +// because PROTECTED isn't defined in +// that case +#ifndef PROTECTED +#define PROTECTED // COMPLIANT - checked by guard +#endif + +// Always enabled, so conflicts in root1.c case +#ifdef MULTIPLE_INCLUDE +#define NOT_PROTECTED 1 // NON_COMPLIANT +#endif diff --git a/c/misra/test/rules/RULE-5-4/root1.c b/c/misra/test/rules/RULE-5-4/root1.c new file mode 100644 index 0000000000..98a94abed5 --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/root1.c @@ -0,0 +1,6 @@ +#define FOO 1 +#include "conditional.h" + +// Both headers define MULTIPLE_INCLUDE +#include "header3.h" +#include "header4.h" \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-4/root2.c b/c/misra/test/rules/RULE-5-4/root2.c new file mode 100644 index 0000000000..39926b9b1e --- /dev/null +++ b/c/misra/test/rules/RULE-5-4/root2.c @@ -0,0 +1,3 @@ +#include "conditional.h" + +#include "header4.h" \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.expected b/c/misra/test/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.expected new file mode 100644 index 0000000000..b9f237be3f --- /dev/null +++ b/c/misra/test/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.expected @@ -0,0 +1,2 @@ +| test1.c:1:13:1:13 | f | Identifier conflicts with external identifier $@ | test.c:3:6:3:6 | f | f | +| test1.c:2:7:2:7 | g | Identifier conflicts with external identifier $@ | test.c:1:5:1:5 | g | g | diff --git a/c/misra/test/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.qlref b/c/misra/test/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.qlref new file mode 100644 index 0000000000..1eb56b955d --- /dev/null +++ b/c/misra/test/rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.qlref @@ -0,0 +1 @@ +rules/RULE-5-8/IdentifiersWithExternalLinkageNotUnique.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-8/test.c b/c/misra/test/rules/RULE-5-8/test.c new file mode 100644 index 0000000000..119c4548c4 --- /dev/null +++ b/c/misra/test/rules/RULE-5-8/test.c @@ -0,0 +1,3 @@ +int g; +extern int g1; // COMPLIANT +void f() { int i; } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-8/test1.c b/c/misra/test/rules/RULE-5-8/test1.c new file mode 100644 index 0000000000..8c138f0bb4 --- /dev/null +++ b/c/misra/test/rules/RULE-5-8/test1.c @@ -0,0 +1,5 @@ +static void f() { // NON_COMPLIANT + int g; // NON_COMPLIANT + int i; // COMPLIANT +} +int g1; // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.expected b/c/misra/test/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.expected new file mode 100644 index 0000000000..f5e22f36f3 --- /dev/null +++ b/c/misra/test/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.expected @@ -0,0 +1,4 @@ +| test1.c:2:12:2:13 | g1 | Identifier conflicts with identifier $@ with internal linkage. | test.c:1:12:1:13 | g1 | g1 | +| test1.c:4:13:4:13 | f | Identifier conflicts with identifier $@ with internal linkage. | test.c:2:13:2:13 | f | f | +| test1.c:5:7:5:7 | g | Identifier conflicts with identifier $@ with internal linkage. | test1.c:1:12:1:12 | g | g | +| test1.c:10:7:10:7 | g | Identifier conflicts with identifier $@ with internal linkage. | test1.c:1:12:1:12 | g | g | diff --git a/c/misra/test/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.qlref b/c/misra/test/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.qlref new file mode 100644 index 0000000000..becc4e9e43 --- /dev/null +++ b/c/misra/test/rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.qlref @@ -0,0 +1 @@ +rules/RULE-5-9/IdentifiersWithInternalLinkageNotUnique.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-9/test.c b/c/misra/test/rules/RULE-5-9/test.c new file mode 100644 index 0000000000..cd5e2ad888 --- /dev/null +++ b/c/misra/test/rules/RULE-5-9/test.c @@ -0,0 +1,2 @@ +static int g1; // NON_COMPLIANT +static void f(); // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-5-9/test1.c b/c/misra/test/rules/RULE-5-9/test1.c new file mode 100644 index 0000000000..0fa1261614 --- /dev/null +++ b/c/misra/test/rules/RULE-5-9/test1.c @@ -0,0 +1,12 @@ +static int g; // COMPLIANT +static int g1; // NON_COMPLIANT + +static void f() { // NON_COMPLIANT + int g; // NON_COMPLIANT + int g2; // COMPLIANT +} + +void f1() { // COMPLIANT + int g; // NON_COMPLIANT + int g2; // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected b/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected new file mode 100644 index 0000000000..4ff4c2aaa1 --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected @@ -0,0 +1,4 @@ +| test.c:6:7:6:8 | x1 | Bit-field 'x1' is declared on type 'int'. | +| test.c:10:15:10:16 | x5 | Bit-field 'x5' is declared on type 'signed long'. | +| test.c:12:15:12:16 | x6 | Bit-field 'x6' is declared on type 'signed char'. | +| test.c:14:14:14:15 | x7 | Bit-field 'x7' is declared on type 'Color'. | diff --git a/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref b/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref new file mode 100644 index 0000000000..7000f50ab1 --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref @@ -0,0 +1 @@ +rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.testref b/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.testref new file mode 100644 index 0000000000..9d02a25700 --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.testref @@ -0,0 +1 @@ +c/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/clang/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected b/c/misra/test/rules/RULE-6-1/clang/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/c/misra/test/rules/RULE-6-1/clang/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref b/c/misra/test/rules/RULE-6-1/clang/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref new file mode 100644 index 0000000000..7000f50ab1 --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/clang/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref @@ -0,0 +1 @@ +rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/clang/options b/c/misra/test/rules/RULE-6-1/clang/options new file mode 100644 index 0000000000..d37493684d --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/clang/options @@ -0,0 +1 @@ +semmle-extractor-options:--mimic clang --std=c11 --edg --diag_error=implicit_func_decl -nostdinc -I../../../../common/test/includes/standard-library \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/clang/test.c b/c/misra/test/rules/RULE-6-1/clang/test.c new file mode 100644 index 0000000000..c8b377b0fd --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/clang/test.c @@ -0,0 +1,15 @@ +typedef unsigned int UINT16; + +enum Color { R, G, B }; + +struct SampleStruct { + int x1 : 2; // COMPLIANT + unsigned int x2 : 2; // COMPLIANT - explicitly unsigned + signed int x3 : 2; // COMPLIANT - explicitly signed + UINT16 x4 : 2; // COMPLIANT - type alias resolves to a compliant type + signed long x5 : 2; // COMPLIANT + signed char x6 : 2; // COMPLIANT + enum Color x7 : 3; // COMPLIANT + //_Atomic(int) x8 : 2; // NON_COMPLIANT[COMPILER_CHECKED] - atomic types are + // not permitted for bit-fields. +} sample_struct; diff --git a/c/misra/test/rules/RULE-6-1/gcc/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected b/c/misra/test/rules/RULE-6-1/gcc/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/c/misra/test/rules/RULE-6-1/gcc/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref b/c/misra/test/rules/RULE-6-1/gcc/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref new file mode 100644 index 0000000000..7000f50ab1 --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/gcc/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.qlref @@ -0,0 +1 @@ +rules/RULE-6-1/BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/gcc/options b/c/misra/test/rules/RULE-6-1/gcc/options new file mode 100644 index 0000000000..b2d1a92f7a --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/gcc/options @@ -0,0 +1 @@ +semmle-extractor-options:--mimic gcc --std=c11 --edg --diag_error=implicit_func_decl -nostdinc -I../../../../common/test/includes/standard-library \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/gcc/test.c b/c/misra/test/rules/RULE-6-1/gcc/test.c new file mode 100644 index 0000000000..c8b377b0fd --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/gcc/test.c @@ -0,0 +1,15 @@ +typedef unsigned int UINT16; + +enum Color { R, G, B }; + +struct SampleStruct { + int x1 : 2; // COMPLIANT + unsigned int x2 : 2; // COMPLIANT - explicitly unsigned + signed int x3 : 2; // COMPLIANT - explicitly signed + UINT16 x4 : 2; // COMPLIANT - type alias resolves to a compliant type + signed long x5 : 2; // COMPLIANT + signed char x6 : 2; // COMPLIANT + enum Color x7 : 3; // COMPLIANT + //_Atomic(int) x8 : 2; // NON_COMPLIANT[COMPILER_CHECKED] - atomic types are + // not permitted for bit-fields. +} sample_struct; diff --git a/c/misra/test/rules/RULE-6-1/options b/c/misra/test/rules/RULE-6-1/options new file mode 100644 index 0000000000..52175e51f9 --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/options @@ -0,0 +1 @@ +semmle-extractor-options:--no-clang --std=c11 --edg --diag_error=implicit_func_decl -nostdinc -I../../../../common/test/includes/standard-library \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-1/test.c b/c/misra/test/rules/RULE-6-1/test.c new file mode 100644 index 0000000000..be9d2a13ca --- /dev/null +++ b/c/misra/test/rules/RULE-6-1/test.c @@ -0,0 +1,15 @@ +typedef unsigned int UINT16; + +enum Color { R, G, B }; + +struct SampleStruct { + int x1 : 2; // NON_COMPLIANT - not explicitly signed or unsigned + unsigned int x2 : 2; // COMPLIANT - explicitly unsigned + signed int x3 : 2; // COMPLIANT - explicitly signed + UINT16 x4 : 2; // COMPLIANT - type alias resolves to a compliant type + signed long x5 : 2; // NON_COMPLIANT - cannot declare bit field for long, even + // if it's signed + signed char x6 : 2; // NON_COMPLIANT - cannot declare bit field for char, even + // if it's signed + enum Color x7 : 3; // NON_COMPLIANT - cannot declare bit field for enum +} sample_struct; diff --git a/c/misra/test/rules/RULE-6-2/SingleBitNamedBitFieldsOfASignedType.testref b/c/misra/test/rules/RULE-6-2/SingleBitNamedBitFieldsOfASignedType.testref new file mode 100644 index 0000000000..edc2f5a16d --- /dev/null +++ b/c/misra/test/rules/RULE-6-2/SingleBitNamedBitFieldsOfASignedType.testref @@ -0,0 +1 @@ +c/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.expected b/c/misra/test/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.expected new file mode 100644 index 0000000000..7c39484796 --- /dev/null +++ b/c/misra/test/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.expected @@ -0,0 +1,2 @@ +| test.c:7:7:7:7 | x | Union member x is declared as a bit field which relies on implementation-specific behavior. | +| test.c:20:7:20:7 | (unnamed bitfield) | Union member (unnamed bitfield) is declared as a bit field which relies on implementation-specific behavior. | diff --git a/c/misra/test/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.qlref b/c/misra/test/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.qlref new file mode 100644 index 0000000000..21c43d4826 --- /dev/null +++ b/c/misra/test/rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.qlref @@ -0,0 +1 @@ +rules/RULE-6-3/BitFieldDeclaredAsMemberOfAUnion.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-6-3/test.c b/c/misra/test/rules/RULE-6-3/test.c new file mode 100644 index 0000000000..1de648d294 --- /dev/null +++ b/c/misra/test/rules/RULE-6-3/test.c @@ -0,0 +1,21 @@ +union U1 { + int x; // COMPLIANT + char y; // COMPLIANT +}; + +union U2 { + int x : 2; // NON-COMPLIANT + char y; // COMPLIANT +}; + +union U3 { + struct str { + int x : 4; // COMPLIANT + int y : 2; // COMPLIANT + }; +}; + +union U4 { + char x; + int : 0; // NON-COMPLIANT +}; \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-1/OctalConstantsUsed.expected b/c/misra/test/rules/RULE-7-1/OctalConstantsUsed.expected deleted file mode 100644 index deecdf994c..0000000000 --- a/c/misra/test/rules/RULE-7-1/OctalConstantsUsed.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.c:7:3:7:5 | 10 | Use of banned $@ constant. | test.c:7:3:7:5 | 10 | octal | -| test.c:8:3:8:5 | 44 | Use of banned $@ constant. | test.c:8:3:8:5 | 44 | octal | diff --git a/c/misra/test/rules/RULE-7-1/OctalConstantsUsed.qlref b/c/misra/test/rules/RULE-7-1/OctalConstantsUsed.qlref deleted file mode 100644 index 7d66675dad..0000000000 --- a/c/misra/test/rules/RULE-7-1/OctalConstantsUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-7-1/OctalConstantsUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-1/test.c b/c/misra/test/rules/RULE-7-1/test.c deleted file mode 100644 index fb0f2e0d36..0000000000 --- a/c/misra/test/rules/RULE-7-1/test.c +++ /dev/null @@ -1,10 +0,0 @@ -void test_non_zero_octal() { - '\0'; // COMPLIANT - octal zero escape sequence permitted - '\012'; // COMPLIANT - '\054'; // COMPLIANT - '\0149'; // COMPLIANT - 0; // COMPLIANT - octal literal zero permitted - 012; // NON_COMPLIANT - 054; // NON_COMPLIANT - "\0"; // COMPLIANT - octal zero escape sequence permitted -} diff --git a/c/misra/test/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.expected b/c/misra/test/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.expected index 4a131f4eaa..0d5504ba03 100644 --- a/c/misra/test/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.expected +++ b/c/misra/test/rules/RULE-7-2/UOrUSuffixRepresentedInUnsignedType.expected @@ -1,5 +1,12 @@ -| test.c:8:20:8:21 | 0 | Unsigned literal does not explicitly express sign with a 'U' or 'u' suffix. | -| test.c:9:20:9:22 | 0 | Unsigned literal does not explicitly express sign with a 'U' or 'u' suffix. | -| test.c:33:6:33:6 | 1 | Unsigned literal does not explicitly express sign with a 'U' or 'u' suffix. | -| test.c:35:6:35:9 | 1 | Unsigned literal does not explicitly express sign with a 'U' or 'u' suffix. | -| test.c:37:6:37:8 | 1 | Unsigned literal does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:111:3:111:12 | 2147483648 | Unsigned literal 0x80000000 does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:116:3:116:20 | 9223372036854775808 | Unsigned literal 0x8000000000000000 does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:139:3:139:21 | 9223372036854775808 | Unsigned literal 0x8000000000000000l does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:162:3:162:21 | 9223372036854775808 | Unsigned literal 0x8000000000000000L does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:185:3:185:22 | 9223372036854775808 | Unsigned literal 0x8000000000000000ll does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:208:3:208:22 | 9223372036854775808 | Unsigned literal 0x8000000000000000LL does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:227:3:227:14 | 2147483648 | Unsigned literal 020000000000 does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:232:3:232:25 | 9223372036854775808 | Unsigned literal 01000000000000000000000 does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:249:3:249:26 | 9223372036854775808 | Unsigned literal 01000000000000000000000l does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:266:3:266:26 | 9223372036854775808 | Unsigned literal 01000000000000000000000L does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:283:3:283:27 | 9223372036854775808 | Unsigned literal 01000000000000000000000ll does not explicitly express sign with a 'U' or 'u' suffix. | +| test.c:300:3:300:27 | 9223372036854775808 | Unsigned literal 01000000000000000000000LL does not explicitly express sign with a 'U' or 'u' suffix. | diff --git a/c/misra/test/rules/RULE-7-2/test.c b/c/misra/test/rules/RULE-7-2/test.c index da62825755..170e822023 100644 --- a/c/misra/test/rules/RULE-7-2/test.c +++ b/c/misra/test/rules/RULE-7-2/test.c @@ -1,39 +1,319 @@ +// Assumed platform in qltest is linux_x86_64, so +// int, long, long long sizes are assumed to be 32, 64, 64 bits respectively -long a1 = 0L; // COMPLIANT -long a2 = 0LL; // COMPLIANT -long a3 = 0uL; // COMPLIANT -long a4 = 0Lu; // COMPLIANT -long a5 = 0LU; // COMPLIANT - -unsigned long b1 = 0L; // NON_COMPLIANT -unsigned long b2 = 0LL; // NON_COMPLIANT -unsigned long b3 = 0uL; // COMPLIANT -unsigned long b4 = 0Lu; // COMPLIANT -unsigned long b5 = 0LU; // COMPLIANT - -signed long c1 = 0L; // COMPLIANT -signed long c2 = 0LL; // COMPLIANT -signed long c3 = 0uL; // COMPLIANT -signed long c4 = 0Lu; // COMPLIANT -signed long c5 = 0LU; // COMPLIANT - -void f0(int a) {} - -void f1(unsigned int a) {} - -void f2() { - - f0(1); // COMPLIANT - f0(1U); // COMPLIANT - f0(0x01); // COMPLIANT - f0(0x01U); // COMPLIANT - f0(001); // COMPLIANT - f0(001U); // COMPLIANT - - f1(1); // NON_COMPLIANT - f1(1U); // COMPLIANT - f1(0x01); // NON_COMPLIANT - f1(0x01U); // COMPLIANT - f1(001); // NON_COMPLIANT - f1(001U); // COMPLIANT +// The type of an integer constant is determined by "6.4.4.1 Integer constants" +// in the C11 Standard. The principle is that any decimal integer constant will +// be signed, unless it has the `U` or `u` suffix. Any hexadecimal integer will +// depend on whether it is larger than the maximum value of the smallest signed +// integer value that can hold the value. So the signedness depends on the +// magnitude of the constant. + +void test_decimal_constants() { + 0; // COMPLIANT + 2147483648; // COMPLIANT - larger than int, but decimal constants never use + // unsigned without the suffix, so will be `long` + 4294967296; // COMPLIANT - larger than unsigned int, still `long` + 9223372036854775807; // COMPLIANT - max long int + // 9223372036854775808; Not a valid integer constant, out of signed range + 0U; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648U; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296U; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807U; // COMPLIANT - max long int + 9223372036854775808U; // COMPLIANT - explicitly unsigned, so can go large than + // max long int + 0u; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648u; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296u; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807u; // COMPLIANT - max long int + 9223372036854775808u; // COMPLIANT - explicitly unsigned, so can go large than + // max long int + + // l suffix + 0l; // COMPLIANT + 2147483648l; // COMPLIANT - within the range of long int + 4294967296l; // COMPLIANT - within the range of long int + 9223372036854775807l; // COMPLIANT - max long int + // 9223372036854775808l; Not a valid integer constant, out of signed range + 0lU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648lU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296lU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807lU; // COMPLIANT - max long int + 9223372036854775808lU; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + 0lu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648lu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296lu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807lu; // COMPLIANT - max long int + 9223372036854775808lu; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + + // L suffix + 0L; // COMPLIANT + 2147483648L; // COMPLIANT - within the range of long int + 4294967296L; // COMPLIANT - within the range of long int + 9223372036854775807L; // COMPLIANT - max long int + // 9223372036854775808L; Not a valid integer constant, out of signed range + 0LU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648LU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296LU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807LU; // COMPLIANT - max long int + 9223372036854775808LU; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + 0Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807Lu; // COMPLIANT - max long int + 9223372036854775808Lu; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + + // ll suffix + 0ll; // COMPLIANT + 2147483648ll; // COMPLIANT - within the range of long long int + 4294967296ll; // COMPLIANT - within the range of long long int + 9223372036854775807ll; // COMPLIANT - max long long int + // 9223372036854775808ll; Not a valid integer constant, out of signed range + 0llU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648llU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296llU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807llU; // COMPLIANT - max long long int + 9223372036854775808llU; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int + 0llu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648llu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296llu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807llu; // COMPLIANT - max long long int + 9223372036854775808llu; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int + + // LL suffix + 0LL; // COMPLIANT + 2147483648LL; // COMPLIANT - within the range of long long int + 4294967296LL; // COMPLIANT - within the range of long long int + 9223372036854775807LL; // COMPLIANT - max long long int + // 9223372036854775808LL; Not a valid integer constant, out of signed range + 0LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807LLU; // COMPLIANT - max long long int + 9223372036854775808LLU; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int + 0LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807LLu; // COMPLIANT - max long long int + 9223372036854775808LLu; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int } + +void test_hexadecimal_constants() { + 0x0; // COMPLIANT - uses signed int + 0x7FFFFFFF; // COMPLIANT - max value held by signed int + 0x80000000; // NON_COMPLIANT - larger than max signed int, so will be unsigned + // int + 0x100000000; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0x7FFFFFFFFFFFFFFF; // COMPLIANT - max long int + 0x8000000000000000; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 0x0U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0u; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000u; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000u; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000u; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `l` suffix + 0x0l; // COMPLIANT - uses signed int + 0x7FFFFFFFl; // COMPLIANT - max value held by signed int + 0x80000000l; // COMPLIANT - larger than max signed int, but smaller than long + // int + 0x100000000l; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0x7FFFFFFFFFFFFFFFl; // COMPLIANT - max long int + 0x8000000000000000l; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 0x0lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFlU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFlU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFlu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFlu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000lu; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `L` suffix + 0x0L; // COMPLIANT - uses signed int + 0x7FFFFFFFL; // COMPLIANT - max value held by signed int + 0x80000000L; // COMPLIANT - larger than max signed int, but smaller than long + // int + 0x100000000L; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0x7FFFFFFFFFFFFFFFL; // COMPLIANT - max long int + 0x8000000000000000L; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 0x0LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000Lu; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `ll` suffix + 0x0ll; // COMPLIANT - uses signed int + 0x7FFFFFFFll; // COMPLIANT - max value held by signed int + 0x80000000ll; // COMPLIANT - larger than max signed int, but smaller than long + // long int + 0x100000000ll; // COMPLIANT - larger than unsigned int, but smaller than long + // long int + 0x7FFFFFFFFFFFFFFFll; // COMPLIANT - max long long int + 0x8000000000000000ll; // NON_COMPLIANT - larger than long long int, so will be + // unsigned long long int + 0x0llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFllU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFllU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0llu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFllu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000llu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000llu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFllu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000llu; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `LL` suffix + 0x0LL; // COMPLIANT - uses signed int + 0x7FFFFFFFLL; // COMPLIANT - max value held by signed int + 0x80000000LL; // COMPLIANT - larger than max signed int, but smaller than long + // long int + 0x100000000LL; // COMPLIANT - larger than unsigned int, but smaller than long + // long int + 0x7FFFFFFFFFFFFFFFLL; // COMPLIANT - max long long int + 0x8000000000000000LL; // NON_COMPLIANT - larger than long long int, so will be + // unsigned long long int + 0x0LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000LLu; // COMPLIANT - unsigned, but uses the suffix correctly +} + +void test_octal_constants() { + 00; // COMPLIANT - uses signed int + 017777777777; // COMPLIANT - max value held by signed int + 020000000000; // NON_COMPLIANT - larger than max signed int, so will be + // unsigned int + 040000000000; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0777777777777777777777; // COMPLIANT - max long int + 01000000000000000000000; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00U; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777U; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777U; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000U; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `l` suffix + 00l; // COMPLIANT - uses signed long + 017777777777l; // COMPLIANT - uses signed long + 020000000000l; // COMPLIANT - uses signed long + 040000000000l; // COMPLIANT - uses signed long + 0777777777777777777777l; // COMPLIANT - max long int + 01000000000000000000000l; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777Ul; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000Ul; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `L` suffix + 00L; // COMPLIANT - uses signed long + 017777777777L; // COMPLIANT - uses signed long + 020000000000L; // COMPLIANT - uses signed long + 040000000000L; // COMPLIANT - uses signed long + 0777777777777777777777L; // COMPLIANT - COMPLIANT - uses signed long + 01000000000000000000000L; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00UL; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777UL; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000UL; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000UL; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777UL; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000UL; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `ll` suffix + 00ll; // COMPLIANT - uses signed long long + 017777777777ll; // COMPLIANT - uses signed long long + 020000000000ll; // COMPLIANT - uses signed long long + 040000000000ll; // COMPLIANT - uses signed long long + 0777777777777777777777ll; // COMPLIANT - max long int + 01000000000000000000000ll; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777Ull; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000Ull; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `LL` suffix + 00LL; // COMPLIANT - uses signed long long + 017777777777LL; // COMPLIANT - uses signed long long + 020000000000LL; // COMPLIANT - uses signed long long + 040000000000LL; // COMPLIANT - uses signed long long + 0777777777777777777777LL; // COMPLIANT - max long int + 01000000000000000000000LL; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777ULL; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000ULL; // COMPLIANT - unsigned, but uses the suffix + // correctly +} + +#define COMPLIANT_VAL 0x80000000U +#define NON_COMPLIANT_VAL 0x80000000 + +void test_macro() { + COMPLIANT_VAL; // COMPLIANT + NON_COMPLIANT_VAL; // NON_COMPLIANT[FALSE_NEGATIVE] - cannot determine suffix + // in macro expansions +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.expected b/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.expected deleted file mode 100644 index 279fd7e621..0000000000 --- a/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.expected +++ /dev/null @@ -1,16 +0,0 @@ -| test.c:3:10:3:11 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:4:10:4:12 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:7:10:7:12 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:8:10:8:12 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:13:11:13:12 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:14:11:14:13 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:17:11:17:13 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:18:11:18:13 | 0 | Lowercase 'l' used as a literal suffix. | -| test.c:23:10:23:14 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:24:10:24:15 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:27:10:27:15 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:28:10:28:15 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:33:11:33:14 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:34:11:34:15 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:37:11:37:15 | 1 | Lowercase 'l' used as a literal suffix. | -| test.c:38:11:38:15 | 1 | Lowercase 'l' used as a literal suffix. | diff --git a/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.qlref b/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.qlref deleted file mode 100644 index 464efc3b2f..0000000000 --- a/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.testref b/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.testref new file mode 100644 index 0000000000..1fc7164d80 --- /dev/null +++ b/c/misra/test/rules/RULE-7-3/LowercaseCharacterLUsedInLiteralSuffix.testref @@ -0,0 +1 @@ +c/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-3/test.c b/c/misra/test/rules/RULE-7-3/test.c deleted file mode 100644 index 00a61817aa..0000000000 --- a/c/misra/test/rules/RULE-7-3/test.c +++ /dev/null @@ -1,44 +0,0 @@ - -int a1 = 0L; // COMPLIANT -int a2 = 0l; // NON_COMPLIANT -int a3 = 0ll; // NON_COMPLIANT -int a4 = 0LL; // COMPLIANT -int a5 = 0uL; // COMPLIANT -int a6 = 0ul; // NON_COMPLIANT -int a7 = 0lu; // NON_COMPLIANT -int a8 = 0Lu; // COMPLIANT -int a9 = 0LU; // COMPLIANT - -long b1 = 0L; // COMPLIANT -long b2 = 0l; // NON_COMPLIANT -long b3 = 0ll; // NON_COMPLIANT -long b4 = 0LL; // COMPLIANT -long b5 = 0uL; // COMPLIANT -long b6 = 0ul; // NON_COMPLIANT -long b7 = 0lu; // NON_COMPLIANT -long b8 = 0Lu; // COMPLIANT -long b9 = 0LU; // COMPLIANT - -int c1 = 0x01L; // COMPLIANT -int c2 = 0x01l; // NON_COMPLIANT -int c3 = 0x01ll; // NON_COMPLIANT -int c4 = 0x01LL; // COMPLIANT -int c5 = 0x01uL; // COMPLIANT -int c6 = 0x01ul; // NON_COMPLIANT -int c7 = 0x01lu; // NON_COMPLIANT -int c8 = 0x01Lu; // COMPLIANT -int c9 = 0x01LU; // COMPLIANT - -long d1 = 001L; // COMPLIANT -long d2 = 001l; // NON_COMPLIANT -long d3 = 001ll; // NON_COMPLIANT -long d4 = 001LL; // COMPLIANT -long d5 = 001uL; // COMPLIANT -long d6 = 001ul; // NON_COMPLIANT -long d7 = 001lu; // NON_COMPLIANT -long d8 = 001Lu; // COMPLIANT -long d9 = 001LU; // COMPLIANT - -char *e1 = ""; -char *e2 = "ul"; -char *e3 = "UL"; diff --git a/c/misra/test/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.expected b/c/misra/test/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.expected new file mode 100644 index 0000000000..208e98f632 --- /dev/null +++ b/c/misra/test/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.expected @@ -0,0 +1,9 @@ +| test.c:11:9:11:10 | s3 | char * variable s3 is declared with a string literal. | +| test.c:13:3:14:15 | ... = ... | char * variable s3 is assigned a string literal. | +| test.c:23:12:23:14 | ws3 | wchar_t * variable ws3 is declared with a string literal. | +| test.c:25:3:25:23 | ... = ... | wchar_t * variable ws3 is assigned a string literal. | +| test.c:50:5:50:21 | return ... | char * function sample3 is returning a string literal. | +| test.c:58:5:58:22 | return ... | wchar_t * function w_sample3 is returning a string literal. | +| test.c:69:3:69:9 | call to sample4 | char * parameter of sample4 is passed a string literal. | +| test.c:78:3:78:11 | call to w_sample4 | wchar_t * parameter of w_sample4 is passed a string literal. | +| test.c:91:3:91:11 | call to w_sample7 | char * parameter of w_sample7 is passed a string literal. | diff --git a/c/misra/test/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.qlref b/c/misra/test/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.qlref new file mode 100644 index 0000000000..2a430a0c42 --- /dev/null +++ b/c/misra/test/rules/RULE-7-4/StringLiteralAssignedToNonConstChar.qlref @@ -0,0 +1 @@ +rules/RULE-7-4/StringLiteralAssignedToNonConstChar.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-4/test.c b/c/misra/test/rules/RULE-7-4/test.c new file mode 100644 index 0000000000..ab7ea21ce9 --- /dev/null +++ b/c/misra/test/rules/RULE-7-4/test.c @@ -0,0 +1,94 @@ +#include +#include + +void sample1() { + /* Test for plain char type */ + const char *s1 = + "string1"; // COMPLIANT: string literal assigned to a const char* variable + const register volatile char *s2 = + "string2"; // COMPLIANT: string literal assigned to a const char* + // variable, don't care about the qualifiers + char *s3 = "string3"; // NON_COMPLIANT: char* variable declared to hold a + // string literal + s3 = + "string4"; // NON_COMPLIANT: char* variable assigned a string literal + // (not likely to be seen in production, since there is strcpy) + + /* Test for wide char type */ + const wchar_t *ws1 = L"wide string1"; // COMPLIANT: string literal assigned to + // a const char* variable + const register volatile wchar_t *ws2 = + L"wide string2"; // COMPLIANT: string literal assigned to a const char* + // variable, don't care about the qualifiers + wchar_t *ws3 = L"wide string3"; // NON_COMPLIANT: char* variable declared to + // hold a string literal + ws3 = L"wide string4"; // NON_COMPLIANT: char* variable assigned a string + // literal (not likely to be seen in production, since + // there is strcpy) +} + +/* Testing returning a plain string literal */ +const char *sample2(int x) { + if (x == 1) + return "string5"; // COMPLIANT: can return a string literal with return type + // being const char* being const char* + else + return NULL; +} + +/* Testing returning a wide string literal */ +const wchar_t *w_sample2(int x) { + if (x == 1) + return L"string5"; // COMPLIANT: can return a string literal with return + // type being const char* being const char* + else + return NULL; +} + +char *sample3(int x) { + if (x == 1) + return "string6"; // NON_COMPLIANT: can return a string literal with return + // type being char* + else + return NULL; +} + +wchar_t *w_sample3(int x) { + if (x == 1) + return L"string6"; // NON_COMPLIANT: can return a string literal with return + // type being char* + else + return NULL; +} + +void sample4(char *string) {} + +void sample5(const char *string) {} + +void call45() { + sample4("string8"); // NON_COMPLIANT: can't pass string literal to char* + sample5("string9"); // COMPLIANT: passing string literal to const char* +} + +void w_sample4(wchar_t *string) {} + +void w_sample5(const wchar_t *string) {} + +void w_call45() { + w_sample4(L"string8"); // NON_COMPLIANT: can't pass string literal to char* + w_sample5(L"string9"); // COMPLIANT: passing string literal to const char* +} + +void w_sample6(int x, ...) {} + +void w_call6() { + w_sample6(1, "string10"); // COMPLIANT by first (and only) exception +} + +void w_sample7(char *x, ...) {} + +void w_call7() { + w_sample7("string11", 1); // NON_COMPLIANT, does not fit exceptional case +} + +int main() { return 0; } diff --git a/c/misra/test/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.expected b/c/misra/test/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.expected new file mode 100644 index 0000000000..d3724e21a4 --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.expected @@ -0,0 +1,31 @@ +| test.c:17:13:17:16 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:19:13:19:18 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:21:13:21:16 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:37:13:37:15 | 256 | Value provided to integer constant macro UINT8_C is outside of the allowed range 0..255 | +| test.c:38:13:38:16 | 256 | Value provided to integer constant macro UINT8_C is outside of the allowed range 0..255 | +| test.c:39:13:39:17 | 256 | Value provided to integer constant macro UINT8_C is outside of the allowed range 0..255 | +| test.c:42:13:42:14 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:43:13:43:15 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:44:13:44:15 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:45:13:45:17 | - ... | Value provided to integer constant macro UINT8_C cannot be negative | +| test.c:70:12:70:14 | 128 | Value provided to integer constant macro INT8_C is outside of the allowed range -128..127 | +| test.c:71:12:71:15 | 128 | Value provided to integer constant macro INT8_C is outside of the allowed range -128..127 | +| test.c:72:12:72:15 | 128 | Value provided to integer constant macro INT8_C is outside of the allowed range -128..127 | +| test.c:76:12:76:15 | - ... | Value provided to integer constant macro INT8_C is outside of the allowed range -128..127 | +| test.c:77:12:77:16 | - ... | Value provided to integer constant macro INT8_C is outside of the allowed range -128..127 | +| test.c:78:12:78:16 | - ... | Value provided to integer constant macro INT8_C is outside of the allowed range -128..127 | +| test.c:91:14:91:18 | 65536 | Value provided to integer constant macro UINT16_C is outside of the allowed range 0..65535 | +| test.c:92:14:92:20 | 65536 | Value provided to integer constant macro UINT16_C is outside of the allowed range 0..65535 | +| test.c:93:14:93:20 | 65536 | Value provided to integer constant macro UINT16_C is outside of the allowed range 0..65535 | +| test.c:106:13:106:17 | 32768 | Value provided to integer constant macro INT16_C is outside of the allowed range -32768..32767 | +| test.c:107:13:107:19 | 32768 | Value provided to integer constant macro INT16_C is outside of the allowed range -32768..32767 | +| test.c:108:13:108:18 | 32768 | Value provided to integer constant macro INT16_C is outside of the allowed range -32768..32767 | +| test.c:112:13:112:18 | - ... | Value provided to integer constant macro INT16_C is outside of the allowed range -32768..32767 | +| test.c:113:13:113:20 | - ... | Value provided to integer constant macro INT16_C is outside of the allowed range -32768..32767 | +| test.c:114:13:114:19 | - ... | Value provided to integer constant macro INT16_C is outside of the allowed range -32768..32767 | +| test.c:124:14:124:24 | 4294967296 | Value provided to integer constant macro UINT32_C is outside of the allowed range 0..4294967295 | +| test.c:125:14:125:25 | 4294967296 | Value provided to integer constant macro UINT32_C is outside of the allowed range 0..4294967295 | +| test.c:135:13:135:22 | 2147483648 | Value provided to integer constant macro INT32_C is outside of the allowed range -2147483648..2147483647 | +| test.c:136:13:136:22 | 2147483648 | Value provided to integer constant macro INT32_C is outside of the allowed range -2147483648..2147483647 | +| test.c:139:13:139:23 | - ... | Value provided to integer constant macro INT32_C is outside of the allowed range -2147483648..2147483647 | +| test.c:140:13:140:23 | - ... | Value provided to integer constant macro INT32_C is outside of the allowed range -2147483648..2147483647 | diff --git a/c/misra/test/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.qlref b/c/misra/test/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.qlref new file mode 100644 index 0000000000..ca6959acec --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.qlref @@ -0,0 +1 @@ +rules/RULE-7-5/IncorrectlySizedIntegerConstantMacroArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.expected b/c/misra/test/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.expected new file mode 100644 index 0000000000..97a35dd977 --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.expected @@ -0,0 +1,7 @@ +| test.c:25:13:25:14 | 1 | Value suffix 'u' is not allowed on provided argument to integer constant macro UINT8_C. | +| test.c:26:13:26:14 | 2 | Value suffix 'U' is not allowed on provided argument to integer constant macro UINT8_C. | +| test.c:27:13:27:14 | 3 | Value suffix 'l' is not allowed on provided argument to integer constant macro UINT8_C. | +| test.c:28:13:28:14 | 4 | Value suffix 'L' is not allowed on provided argument to integer constant macro UINT8_C. | +| test.c:29:13:29:15 | 5 | Value suffix 'ul' is not allowed on provided argument to integer constant macro UINT8_C. | +| test.c:30:13:30:15 | 5 | Value suffix 'll' is not allowed on provided argument to integer constant macro UINT8_C. | +| test.c:31:13:31:16 | 5 | Value suffix 'ull' is not allowed on provided argument to integer constant macro UINT8_C. | diff --git a/c/misra/test/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.qlref b/c/misra/test/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.qlref new file mode 100644 index 0000000000..afadb6e34b --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.qlref @@ -0,0 +1 @@ +rules/RULE-7-5/IntegerConstantMacroArgumentUsesSuffix.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.expected b/c/misra/test/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.expected new file mode 100644 index 0000000000..b29228b6df --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.expected @@ -0,0 +1,8 @@ +| test.c:48:13:48:17 | ... + ... | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:49:13:49:18 | access to array | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:50:13:50:19 | access to array | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:51:5:51:22 | 255 | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:52:5:52:17 | 1 | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:53:5:53:18 | 0 | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:54:5:54:17 | 0 | Argument to integer constant macro UINT8_C must be an integer literal. | +| test.c:55:5:55:20 | 0 | Argument to integer constant macro UINT8_C must be an integer literal. | diff --git a/c/misra/test/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.qlref b/c/misra/test/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.qlref new file mode 100644 index 0000000000..802f415bc9 --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/InvalidIntegerConstantMacroArgument.qlref @@ -0,0 +1 @@ +rules/RULE-7-5/InvalidIntegerConstantMacroArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.expected b/c/misra/test/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.expected new file mode 100644 index 0000000000..ee5b75cb91 --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.expected @@ -0,0 +1,7 @@ +| test.c:16:13:16:15 | 1.0 | Integer constant macro UINT8_C used with floating point literal argument, only decimal, octal, or hex integer literal allowed. | +| test.c:17:13:17:16 | - ... | Integer constant macro UINT8_C used with floating point literal argument, only decimal, octal, or hex integer literal allowed. | +| test.c:18:13:18:17 | 7 | Integer constant macro UINT8_C used with binary literal argument, only decimal, octal, or hex integer literal allowed. | +| test.c:19:13:19:18 | - ... | Integer constant macro UINT8_C used with binary literal argument, only decimal, octal, or hex integer literal allowed. | +| test.c:20:13:20:15 | 97 | Integer constant macro UINT8_C used with char literal argument, only decimal, octal, or hex integer literal allowed. | +| test.c:21:13:21:16 | - ... | Integer constant macro UINT8_C used with char literal argument, only decimal, octal, or hex integer literal allowed. | +| test.c:22:13:22:16 | 10 | Integer constant macro UINT8_C used with char literal argument, only decimal, octal, or hex integer literal allowed. | diff --git a/c/misra/test/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.qlref b/c/misra/test/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.qlref new file mode 100644 index 0000000000..5584fe8d46 --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.qlref @@ -0,0 +1 @@ +rules/RULE-7-5/InvalidLiteralForIntegerConstantMacroArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-5/test.c b/c/misra/test/rules/RULE-7-5/test.c new file mode 100644 index 0000000000..a3fb4b60e4 --- /dev/null +++ b/c/misra/test/rules/RULE-7-5/test.c @@ -0,0 +1,179 @@ +#include "stdbool.h" +#include "stdint.h" + +#define NULL 0 +#define NULLPTR ((void *)NULL) + +uint_least8_t g1[] = { + // Basic valid + UINT8_C(0), // COMPLIANT + UINT8_C(1), // COMPLIANT + UINT8_C(8), // COMPLIANT + UINT8_C(0x23), // COMPLIANT + UINT8_C(034), // COMPLIANT + + // Incorrect literal types + UINT8_C(1.0), // NON-COMPLIANT + UINT8_C(-1.0), // NON-COMPLIANT + UINT8_C(0b111), // NON-COMPLIANT + UINT8_C(-0b111), // NON-COMPLIANT + UINT8_C('a'), // NON-COMPLIANT + UINT8_C(-'$'), // NON-COMPLIANT + UINT8_C('\n'), // NON-COMPLIANT + + // Suffixes disallowed + UINT8_C(1u), // NON-COMPLIANT + UINT8_C(2U), // NON-COMPLIANT + UINT8_C(3l), // NON-COMPLIANT + UINT8_C(4L), // NON-COMPLIANT + UINT8_C(5ul), // NON-COMPLIANT + UINT8_C(5ll), // NON-COMPLIANT + UINT8_C(5ull), // NON-COMPLIANT + + // Range tests + UINT8_C(255), // COMPLIANT + UINT8_C(0xFF), // COMPLIANT + UINT8_C(0377), // COMPLIANT + UINT8_C(256), // NON-COMPLIANT + UINT8_C(0400), // NON-COMPLIANT + UINT8_C(0x100), // NON-COMPLIANT + + // Signage tests + UINT8_C(-1), // NON-COMPLIANT + UINT8_C(-20), // NON-COMPLIANT + UINT8_C(-33), // NON-COMPLIANT + UINT8_C(-0x44), // NON-COMPLIANT + + // Invalid nonliteral expressions + UINT8_C(0 + 0), // NON-COMPLIANT + UINT8_C("a"[0]), // NON-COMPLIANT + UINT8_C(0 ["a"]), // NON-COMPLIANT + UINT8_C(UINT8_MAX), // NON-COMPLIANT + UINT8_C(true), // NON-COMPLIANT + UINT8_C(false), // NON-COMPLIANT + UINT8_C(NULL), // NON-COMPLIANT + UINT8_C(NULLPTR), // NON-COMPLIANT +}; + +int_least8_t g2[] = { + // Basic valid + INT8_C(0), // COMPLIANT + INT8_C(1), // COMPLIANT + INT8_C(8), // COMPLIANT + INT8_C(0x23), // COMPLIANT + INT8_C(034), // COMPLIANT + + // Range tests + INT8_C(127), // COMPLIANT + INT8_C(0x79), // COMPLIANT + INT8_C(0177), // COMPLIANT + INT8_C(128), // NON-COMPLIANT + INT8_C(0200), // NON-COMPLIANT + INT8_C(0x80), // NON-COMPLIANT + INT8_C(-128), // COMPLIANT + INT8_C(-0x80), // COMPLIANT + INT8_C(-0200), // COMPLIANT + INT8_C(-129), // NON-COMPLIANT + INT8_C(-0201), // NON-COMPLIANT + INT8_C(-0x81), // NON-COMPLIANT +}; + +uint_least16_t g3[] = { + // Basic valid + UINT16_C(0), // COMPLIANT + UINT16_C(0x23), // COMPLIANT + UINT16_C(034), // COMPLIANT + + // Range tests + UINT16_C(65535), // COMPLIANT + UINT16_C(0xFFFF), // COMPLIANT + UINT16_C(0177777), // COMPLIANT + UINT16_C(65536), // NON-COMPLIANT + UINT16_C(0200000), // NON-COMPLIANT + UINT16_C(0x10000), // NON-COMPLIANT +}; + +int_least16_t g4[] = { + // Basic valid + INT16_C(0), // COMPLIANT + INT16_C(0x23), // COMPLIANT + INT16_C(034), // COMPLIANT + + // Range tests + INT16_C(32767), // COMPLIANT + INT16_C(0x7FFF), // COMPLIANT + INT16_C(077777), // COMPLIANT + INT16_C(32768), // NON-COMPLIANT + INT16_C(0100000), // NON-COMPLIANT + INT16_C(0x8000), // NON-COMPLIANT + INT16_C(-32768), // COMPLIANT + INT16_C(-0100000), // COMPLIANT + INT16_C(-0x8000), // COMPLIANT + INT16_C(-32769), // NON-COMPLIANT + INT16_C(-0100001), // NON-COMPLIANT + INT16_C(-0x8001), // NON-COMPLIANT +}; + +uint_least32_t g5[] = { + // Basic valid + UINT32_C(0), // COMPLIANT + + // Range tests + UINT32_C(4294967295), // COMPLIANT + UINT32_C(0xFFFFFFFF), // COMPLIANT + UINT32_C(4294967296), // NON-COMPLIANT + UINT32_C(0x100000000), // NON-COMPLIANT +}; + +int_least32_t g6[] = { + // Basic valid + INT32_C(0), // COMPLIANT + + // Range tests + INT32_C(2147483647), // COMPLIANT + INT32_C(0x7FFFFFFF), // COMPLIANT + INT32_C(2147483648), // NON-COMPLIANT + INT32_C(0x80000000), // NON-COMPLIANT + INT32_C(-2147483648), // COMPLIANT + INT32_C(-0x80000000), // COMPLIANT + INT32_C(-2147483649), // NON-COMPLIANT + INT32_C(-0x80000001), // NON-COMPLIANT +}; + +uint_least64_t g7[] = { + // Basic valid + UINT64_C(0), // COMPLIANT + + // Range tests + UINT64_C(18446744073709551615), // COMPLIANT + UINT64_C(0xFFFFFFFFFFFFFFFF), // COMPLIANT + // Compile time error if we try to create integer literals beyond this. +}; + +int_least64_t g8[] = { + // Basic valid + INT64_C(0), // COMPLIANT + + // Range tests + INT64_C(9223372036854775807), // COMPLIANT + // INT64_C(9223372036854775808) is a compile-time error + + INT64_C(-9223372036854775807), // COMPLIANT + // -9223372036854775808 is correctly sized, but not a valid decimal literal + // value. + // -9223372036854775809 is not correctly sized, and not a valid decimal + // literal value. + + INT64_C(0x7FFFFFFFFFFFFFFF), // COMPLIANT + INT64_C(0x8000000000000000), // NON-COMPLIANT[FALSE NEGATIVE] + INT64_C(-0x8000000000000000), // COMPLIANT + INT64_C(-0x8000000000000001), // NON-COMPLIANT[FALSE NEGATIVE] + INT64_C(-0x8001000000000000), // NON-COMPLIANT[FALSE NEGATIVE] +}; + +// Other edge cases: +void f(void) { + uint32_t l1 = 1; + // `UnrecognizedNumericLiteral` case: + int64_t l2 = ((int32_t)UINT64_C(0x1b2) * (l1)); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.expected b/c/misra/test/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.expected new file mode 100644 index 0000000000..ddf517ed9e --- /dev/null +++ b/c/misra/test/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.expected @@ -0,0 +1,4 @@ +| test.c:3:13:3:24 | INT8_C(c) | Usage of small integer constant macro INT8_C is not allowed. | +| test.c:4:14:4:26 | UINT8_C(c) | Usage of small integer constant macro UINT8_C is not allowed. | +| test.c:5:14:5:28 | INT16_C(c) | Usage of small integer constant macro INT16_C is not allowed. | +| test.c:6:15:6:30 | UINT16_C(c) | Usage of small integer constant macro UINT16_C is not allowed. | diff --git a/c/misra/test/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.qlref b/c/misra/test/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.qlref new file mode 100644 index 0000000000..e41e2912d8 --- /dev/null +++ b/c/misra/test/rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.qlref @@ -0,0 +1 @@ +rules/RULE-7-6/UseOfBannedSmallIntegerConstantMacro.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-7-6/test.c b/c/misra/test/rules/RULE-7-6/test.c new file mode 100644 index 0000000000..9832cdf251 --- /dev/null +++ b/c/misra/test/rules/RULE-7-6/test.c @@ -0,0 +1,10 @@ +#include "stdint.h" + +int8_t g1 = INT8_C(0x12); // NON-COMPLIANT +uint8_t g2 = UINT8_C(0x12); // NON-COMPLIANT +int16_t g3 = INT16_C(0x1234); // NON-COMPLIANT +uint16_t g4 = UINT16_C(0x1234); // NON-COMPLIANT +int32_t g5 = INT32_C(0x1234); // COMPLIANT +uint32_t g6 = UINT32_C(0x1234); // COMPLIANT +int64_t g7 = INT64_C(0x1234); // COMPLIANT +uint64_t g8 = UINT64_C(0x1234); // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.expected b/c/misra/test/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.expected new file mode 100644 index 0000000000..fe6b1799d6 --- /dev/null +++ b/c/misra/test/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.expected @@ -0,0 +1,3 @@ +| test.c:2:20:2:21 | declaration of f1 | Inline function not explicitly declared static. | +| test.c:3:13:3:14 | declaration of f2 | Inline function not explicitly declared static. | +| test.c:4:20:4:20 | declaration of f | Inline function not explicitly declared static. | diff --git a/c/misra/test/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.qlref b/c/misra/test/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.qlref new file mode 100644 index 0000000000..fc081c2570 --- /dev/null +++ b/c/misra/test/rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.qlref @@ -0,0 +1 @@ +rules/RULE-8-10/InlineFunctionNotDeclaredStaticStorage.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-10/test.c b/c/misra/test/rules/RULE-8-10/test.c new file mode 100644 index 0000000000..56048b33ec --- /dev/null +++ b/c/misra/test/rules/RULE-8-10/test.c @@ -0,0 +1,5 @@ +static inline void f(); // COMPLIANT +extern inline void f1(); // NON_COMPLIANT +inline void f2(); // NON_COMPLIANT +extern inline void f(); // NON_COMPLIANT -while this will be internal linkage it + // is less clear than explicitly specifying static diff --git a/c/misra/test/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.expected b/c/misra/test/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.expected new file mode 100644 index 0000000000..1ee0702d2b --- /dev/null +++ b/c/misra/test/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.expected @@ -0,0 +1 @@ +| test.c:2:12:2:13 | declaration of a1 | Array declared without explicit size. | diff --git a/c/misra/test/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.qlref b/c/misra/test/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.qlref new file mode 100644 index 0000000000..4f011bfafe --- /dev/null +++ b/c/misra/test/rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.qlref @@ -0,0 +1 @@ +rules/RULE-8-11/ArrayExternalLinkageSizeExplicitlySpecified.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-11/test.c b/c/misra/test/rules/RULE-8-11/test.c new file mode 100644 index 0000000000..6b7d8be8c7 --- /dev/null +++ b/c/misra/test/rules/RULE-8-11/test.c @@ -0,0 +1,6 @@ +extern int a[1]; // COMPLIANT +extern int a1[]; // NON_COMPLIANT +extern int a2[] = { + 1}; // COMPLIANT - this rule applies to non-defining declarations only +static int a3[]; // COMPLIANT - not external linkage +int a4[]; // COMPLIANT - is a definition \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.testref b/c/misra/test/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.testref new file mode 100644 index 0000000000..7db7d79d72 --- /dev/null +++ b/c/misra/test/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.testref @@ -0,0 +1 @@ +c/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.expected b/c/misra/test/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.expected index 39dbf04763..e3e0963087 100644 --- a/c/misra/test/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.expected +++ b/c/misra/test/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.expected @@ -12,3 +12,4 @@ | test.c:66:23:66:24 | p1 | $@ points to a non-const-qualified type. | test.c:66:23:66:24 | p1 | p1 | | test.c:71:17:71:18 | p1 | $@ points to a non-const-qualified type. | test.c:71:17:71:18 | p1 | p1 | | test.c:75:15:75:16 | p1 | $@ points to a non-const-qualified type. | test.c:75:15:75:16 | p1 | p1 | +| test.c:103:30:103:30 | s | $@ points to a non-const-qualified type. | test.c:103:30:103:30 | s | s | diff --git a/c/misra/test/rules/RULE-8-13/test.c b/c/misra/test/rules/RULE-8-13/test.c index 1ac9e5028c..a2333d2a3d 100644 --- a/c/misra/test/rules/RULE-8-13/test.c +++ b/c/misra/test/rules/RULE-8-13/test.c @@ -75,4 +75,38 @@ char *f16(char *p1) { // NON_COMPLIANT int f17(char *p1) { // NON_COMPLIANT p1++; return 0; +} + +#include + +int16_t +test_r(int16_t *value) { // COMPLIANT - ignored because of the use of ASM + int16_t result; + struct S { + int *x; // COMPLIANT - ignored because of the use of ASM + struct S2 { + int *y; // COMPLIANT - ignored because of the use of ASM + } s2; + }; + __asm__("movb %bh (%eax)"); + return result; +} + +struct S { + int x; +}; + +void test_struct(struct S *s) { // COMPLIANT + s->x = 1; +} + +void test_struct_2(struct S *s) { // NON_COMPLIANT - could be const + s = 0; +} + +void test_no_body(int *p); // COMPLIANT - no body, so cannot evaluate whether it + // should be const + +void increment(int *p) { // COMPLIANT + *p++ = 1; } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.expected b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.expected new file mode 100644 index 0000000000..3479ef1e35 --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.expected @@ -0,0 +1,8 @@ +| test.c:18:8:18:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:18:8:18:15 | alignas(...) | int | test.c:19:8:19:15 | alignas(...) | 4 | +| test.c:19:8:19:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:19:8:19:15 | alignas(...) | 4 | test.c:18:8:18:15 | alignas(...) | int | +| test.c:22:8:22:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:22:8:22:15 | alignas(...) | ... * ... | test.c:23:8:23:15 | alignas(...) | 32 | +| test.c:23:8:23:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:23:8:23:15 | alignas(...) | 32 | test.c:22:8:22:15 | alignas(...) | ... * ... | +| test.c:28:8:28:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:28:8:28:15 | alignas(...) | ... * ... | test.c:29:8:29:15 | alignas(...) | ... * ... | +| test.c:29:8:29:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:29:8:29:15 | alignas(...) | ... * ... | test.c:28:8:28:15 | alignas(...) | ... * ... | +| test.c:34:8:34:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:34:8:34:15 | alignas(...) | signed int | test.c:35:8:35:15 | alignas(...) | unsigned int | +| test.c:35:8:35:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:35:8:35:15 | alignas(...) | unsigned int | test.c:34:8:34:15 | alignas(...) | signed int | diff --git a/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.expected.gcc b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.expected.gcc new file mode 100644 index 0000000000..6f3d414214 --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.expected.gcc @@ -0,0 +1,10 @@ +| test.c:11:8:11:15 | alignas(...) | Variable g4 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:11:8:11:15 | alignas(...) | 16 | test.c:12:8:12:15 | alignas(...) | 32 | +| test.c:12:8:12:15 | alignas(...) | Variable g4 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:12:8:12:15 | alignas(...) | 32 | test.c:11:8:11:15 | alignas(...) | 16 | +| test.c:18:8:18:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:18:8:18:15 | alignas(...) | int | test.c:19:8:19:15 | alignas(...) | 4 | +| test.c:19:8:19:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:19:8:19:15 | alignas(...) | 4 | test.c:18:8:18:15 | alignas(...) | int | +| test.c:22:8:22:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:22:8:22:15 | alignas(...) | ... * ... | test.c:23:8:23:15 | alignas(...) | 32 | +| test.c:23:8:23:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:23:8:23:15 | alignas(...) | 32 | test.c:22:8:22:15 | alignas(...) | ... * ... | +| test.c:28:8:28:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:28:8:28:15 | alignas(...) | ... * ... | test.c:29:8:29:15 | alignas(...) | ... * ... | +| test.c:29:8:29:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:29:8:29:15 | alignas(...) | ... * ... | test.c:28:8:28:15 | alignas(...) | ... * ... | +| test.c:34:8:34:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:34:8:34:15 | alignas(...) | signed int | test.c:35:8:35:15 | alignas(...) | unsigned int | +| test.c:35:8:35:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:35:8:35:15 | alignas(...) | unsigned int | test.c:34:8:34:15 | alignas(...) | signed int | diff --git a/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.qlref b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.qlref new file mode 100644 index 0000000000..08648fd168 --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.qlref @@ -0,0 +1 @@ +rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.expected b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.expected new file mode 100644 index 0000000000..69d2c8bb2d --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.expected @@ -0,0 +1,2 @@ +| test.c:5:12:5:13 | declaration of g2 | Variable g2 declared without explicit alignment to match $@ with alignment $@. | test.c:4:25:4:26 | declaration of g2 | other definition | test.c:4:8:4:15 | alignas(...) | alignas(...) | +| test.c:7:12:7:13 | declaration of g3 | Variable g3 declared without explicit alignment to match $@ with alignment $@. | test.c:8:25:8:26 | declaration of g3 | other definition | test.c:8:8:8:15 | alignas(...) | alignas(...) | diff --git a/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.qlref b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.qlref new file mode 100644 index 0000000000..f5f13e2125 --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.qlref @@ -0,0 +1 @@ +rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-15/test.c b/c/misra/test/rules/RULE-8-15/test.c new file mode 100644 index 0000000000..f97a79d5b6 --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/test.c @@ -0,0 +1,35 @@ +extern _Alignas(16) int g1; // COMPLIANT +extern _Alignas(16) int g1; // COMPLIANT + +extern _Alignas(16) int g2; +extern int g2; // NON_COMPLIANT + +extern int g3; // NON_COMPLIANT +extern _Alignas(16) int g3; + +// Does not compile on clang: +// extern _Alignas(16) int g4; // COMPLIANT +// extern _Alignas(32) int g4; // COMPLIANT + +extern int g5; // COMPLIANT +extern int g5; // COMPLIANT + +// Spec says elements must be lexically identical after macro expansion +extern _Alignas(int) int g6; // NON_COMPLIANT +extern _Alignas(4) int g6; // NON_COMPLIANT + +#define THIRTY_TWO 32 +extern _Alignas(16 * 2) int g7; // NON_COMPLIANT +extern _Alignas(32) int g7; // NON_COMPLIANT + +extern _Alignas(THIRTY_TWO) int g8; // COMPLIANT +extern _Alignas(32) int g8; // COMPLIANT + +extern _Alignas(16 * 2) int g9; // NON_COMPLIANT +extern _Alignas(2 * 16) int g9; // NON_COMPLIANT + +extern _Alignas(int) int g10; // COMPLIANT +extern _Alignas(int) int g10; // COMPLIANT + +extern _Alignas(signed int) int g11; // NON_COMPLIANT +extern _Alignas(unsigned int) int g11; // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-15/test.c.gcc b/c/misra/test/rules/RULE-8-15/test.c.gcc new file mode 100644 index 0000000000..d0f53bf89a --- /dev/null +++ b/c/misra/test/rules/RULE-8-15/test.c.gcc @@ -0,0 +1,35 @@ +extern _Alignas(16) int g1; // COMPLIANT +extern _Alignas(16) int g1; // COMPLIANT + +extern _Alignas(16) int g2; +extern int g2; // NON_COMPLIANT + +extern int g3; // NON_COMPLIANT +extern _Alignas(16) int g3; + +// Does not compile on clang: +extern _Alignas(16) int g4; // COMPLIANT +extern _Alignas(32) int g4; // COMPLIANT + +extern int g5; // COMPLIANT +extern int g5; // COMPLIANT + +// Spec says elements must be lexically identical after macro expansion +extern _Alignas(int) int g6; // NON_COMPLIANT +extern _Alignas(4) int g6; // NON_COMPLIANT + +#define THIRTY_TWO 32 +extern _Alignas(16 * 2) int g7; // NON_COMPLIANT +extern _Alignas(32) int g7; // NON_COMPLIANT + +extern _Alignas(THIRTY_TWO) int g8; // COMPLIANT +extern _Alignas(32) int g8; // COMPLIANT + +extern _Alignas(16 * 2) int g9; // NON_COMPLIANT +extern _Alignas(2 * 16) int g9; // NON_COMPLIANT + +extern _Alignas(int) int g10; // COMPLIANT +extern _Alignas(int) int g10; // COMPLIANT + +extern _Alignas(signed int) int g11; // NON_COMPLIANT +extern _Alignas(unsigned int) int g11; // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.expected b/c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.expected new file mode 100644 index 0000000000..4daa3475ed --- /dev/null +++ b/c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.expected @@ -0,0 +1,4 @@ +| test.c:2:10:2:10 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:2:17:2:18 | g2 | g2 | +| test.c:3:10:3:14 | ... - ... | Invalid alignof() size set to zero for variable $@. | test.c:3:21:3:22 | g3 | g3 | +| test.c:8:12:8:12 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:8:19:8:20 | m2 | m2 | +| test.c:13:12:13:12 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:13:19:13:20 | l2 | l2 | diff --git a/c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.qlref b/c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.qlref new file mode 100644 index 0000000000..c8e19d1fe5 --- /dev/null +++ b/c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.qlref @@ -0,0 +1 @@ +rules/RULE-8-16/AlignmentWithSizeZero.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-16/test.c b/c/misra/test/rules/RULE-8-16/test.c new file mode 100644 index 0000000000..3e96b7b8cc --- /dev/null +++ b/c/misra/test/rules/RULE-8-16/test.c @@ -0,0 +1,14 @@ +_Alignas(8) int g1; // COMPLIANT +_Alignas(0) int g2; // NON-COMPLIANT +_Alignas(8 - 8) int g3; // NON-COMPLIANT +_Alignas(float) int g4; // COMPLIANT + +struct s { + _Alignas(64) int m1; // COMPLIANT + _Alignas(0) int m2; // NON_COMPLIANT +}; + +void f() { + _Alignas(8) int l1; // COMPLIANT + _Alignas(0) int l2; // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.expected b/c/misra/test/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.expected new file mode 100644 index 0000000000..24707ca457 --- /dev/null +++ b/c/misra/test/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.expected @@ -0,0 +1,6 @@ +| test.c:2:30:2:31 | g2 | Variable g2 contains more than one alignment specifier, $@ and $@ | test.c:2:1:2:8 | alignas(...) | alignas(...) | test.c:2:13:2:20 | alignas(...) | alignas(...) | +| test.c:3:29:3:30 | g3 | Variable g3 contains more than one alignment specifier, $@ and $@ | test.c:3:1:3:8 | alignas(...) | alignas(...) | test.c:3:13:3:20 | alignas(...) | alignas(...) | +| test.c:4:35:4:36 | g4 | Variable g4 contains more than one alignment specifier, $@ and $@ | test.c:4:1:4:8 | alignas(...) | alignas(...) | test.c:4:17:4:24 | alignas(...) | alignas(...) | +| test.c:6:53:6:54 | g5 | Variable g5 contains more than one alignment specifier, $@ and $@ | test.c:5:1:5:8 | alignas(...) | alignas(...) | test.c:6:33:6:40 | alignas(...) | alignas(...) | +| test.c:10:35:10:36 | m2 | Variable m2 contains more than one alignment specifier, $@ and $@ | test.c:10:3:10:10 | alignas(...) | alignas(...) | test.c:10:18:10:25 | alignas(...) | alignas(...) | +| test.c:15:35:15:36 | l2 | Variable l2 contains more than one alignment specifier, $@ and $@ | test.c:15:3:15:10 | alignas(...) | alignas(...) | test.c:15:18:15:25 | alignas(...) | alignas(...) | diff --git a/c/misra/test/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.qlref b/c/misra/test/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.qlref new file mode 100644 index 0000000000..7ff11e8a61 --- /dev/null +++ b/c/misra/test/rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.qlref @@ -0,0 +1 @@ +rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-17/test.c b/c/misra/test/rules/RULE-8-17/test.c new file mode 100644 index 0000000000..e2f8b2b44f --- /dev/null +++ b/c/misra/test/rules/RULE-8-17/test.c @@ -0,0 +1,16 @@ +_Alignas(8) int g1; // COMPLIANT +_Alignas(8) _Alignas(16) int g2; // NON-COMPLIANT +_Alignas(8) _Alignas(8) int g3; // NON-COMPLIANT +_Alignas(float) _Alignas(int) int g4; // NON-COMPLIANT +_Alignas(float) _Alignas(float) int g5; // NON-COMPLIANT +_Alignas(float) _Alignas(float) _Alignas(float) int g5; // NON-COMPLIANT + +struct s { + _Alignas(64) int m1; // COMPLIANT + _Alignas(long) _Alignas(16) int m2; // NON_COMPLIANT +}; + +void f() { + _Alignas(8) int l1; // COMPLIANT + _Alignas(long) _Alignas(16) int l2; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-2/FunctionTypesNotInPrototypeForm.testref b/c/misra/test/rules/RULE-8-2/FunctionTypesNotInPrototypeForm.testref new file mode 100644 index 0000000000..1a6a69fc24 --- /dev/null +++ b/c/misra/test/rules/RULE-8-2/FunctionTypesNotInPrototypeForm.testref @@ -0,0 +1 @@ +c/common/test/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected b/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected new file mode 100644 index 0000000000..f2b438aaf1 --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected @@ -0,0 +1,14 @@ +| function1.c:6:6:6:7 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:6:6:6:7 | declaration of f3 | f3 | function1.c:8:4:8:5 | declaration of f3 | f3 | +| function1.c:8:4:8:5 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:8:4:8:5 | declaration of f3 | f3 | function1.c:6:6:6:7 | declaration of f3 | f3 | +| function1.c:8:4:8:5 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:8:4:8:5 | declaration of f3 | f3 | function2.c:4:6:4:7 | declaration of f3 | f3 | +| function1.c:9:6:9:7 | declaration of f4 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:9:6:9:7 | declaration of f4 | f4 | function2.c:5:5:5:6 | declaration of f4 | f4 | +| function1.c:13:5:13:6 | definition of f6 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:13:5:13:6 | definition of f6 | f6 | function2.c:9:6:9:7 | definition of f6 | f6 | +| function1.c:15:5:15:7 | declaration of f20 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function1.c:15:5:15:7 | declaration of f20 | f20 | function2.c:11:5:11:7 | declaration of f20 | f20 | +| function1.c:21:3:21:5 | definition of f21 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function1.c:21:3:21:5 | definition of f21 | f21 | function2.c:17:10:17:12 | declaration of f21 | f21 | +| function1.c:25:6:25:8 | definition of f22 | The parameter names of re-declaration of $@ do not use the same type names as declaration $@ | function1.c:25:6:25:8 | definition of f22 | f22 | function2.c:19:13:19:15 | declaration of f22 | f22 | +| function2.c:4:6:4:7 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function2.c:4:6:4:7 | declaration of f3 | f3 | function1.c:8:4:8:5 | declaration of f3 | f3 | +| function2.c:5:5:5:6 | declaration of f4 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function2.c:5:5:5:6 | declaration of f4 | f4 | function1.c:9:6:9:7 | declaration of f4 | f4 | +| function2.c:9:6:9:7 | definition of f6 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function2.c:9:6:9:7 | definition of f6 | f6 | function1.c:13:5:13:6 | definition of f6 | f6 | +| function2.c:11:5:11:7 | declaration of f20 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function2.c:11:5:11:7 | declaration of f20 | f20 | function1.c:15:5:15:7 | declaration of f20 | f20 | +| function2.c:17:10:17:12 | declaration of f21 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function2.c:17:10:17:12 | declaration of f21 | f21 | function1.c:21:3:21:5 | definition of f21 | f21 | +| function2.c:19:13:19:15 | declaration of f22 | The parameter names of re-declaration of $@ do not use the same type names as declaration $@ | function2.c:19:13:19:15 | declaration of f22 | f22 | function1.c:25:6:25:8 | definition of f22 | f22 | diff --git a/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.qlref b/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.qlref new file mode 100644 index 0000000000..f7c628941f --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.qlref @@ -0,0 +1 @@ +rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected b/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected new file mode 100644 index 0000000000..8b8e7f8a48 --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected @@ -0,0 +1,22 @@ +| object1.c:5:6:5:7 | definition of a3 | The object $@ of type long does not use the same type names as re-declaration $@ of type LL | object1.c:5:6:5:7 | definition of a3 | a3 | object2.c:11:11:11:12 | declaration of a3 | a3 | +| object1.c:6:6:6:7 | definition of a4 | The object $@ of type long does not use the same type names as re-declaration $@ of type int | object1.c:6:6:6:7 | definition of a4 | a4 | object2.c:13:12:13:13 | declaration of a4 | a4 | +| object1.c:7:5:7:6 | definition of a5 | The object $@ of type int does not use the same type names as re-declaration $@ of type long | object1.c:7:5:7:6 | definition of a5 | a5 | object2.c:15:13:15:14 | declaration of a5 | a5 | +| object1.c:8:6:8:7 | definition of a6 | The object $@ of type long does not use the same type names as re-declaration $@ of type int | object1.c:8:6:8:7 | definition of a6 | a6 | object2.c:19:1:19:3 | declaration of a6 | a6 | +| object1.c:9:5:9:6 | definition of a7 | The object $@ of type int does not use the same type names as re-declaration $@ of type LL | object1.c:9:5:9:6 | definition of a7 | a7 | object2.c:21:11:21:12 | declaration of a7 | a7 | +| object1.c:15:5:15:7 | definition of a10 | The object $@ of type int[100] does not use the same type names as re-declaration $@ of type LI[100] | object1.c:15:5:15:7 | definition of a10 | a10 | object2.c:24:4:24:6 | definition of a10 | a10 | +| object1.c:16:5:16:7 | definition of a11 | The object $@ of type int[100] does not use the same type names as re-declaration $@ of type int[101] | object1.c:16:5:16:7 | definition of a11 | a11 | object2.c:25:12:25:14 | declaration of a11 | a11 | +| object1.c:19:12:19:14 | definition of a13 | The object $@ of type int *const does not use the same type names as re-declaration $@ of type int * | object1.c:19:12:19:14 | definition of a13 | a13 | object2.c:28:13:28:15 | declaration of a13 | a13 | +| object1.c:23:10:23:13 | definition of size | The object $@ of type size_t does not use the same type names as re-declaration $@ of type unsigned char | object1.c:23:10:23:13 | definition of size | size | object2.c:32:17:32:20 | definition of size | size | +| object1.c:24:3:24:4 | definition of s0 | The object $@ of type NamedStruct0 does not use the same type names as re-declaration $@ of type NamedStruct0 | object1.c:24:3:24:4 | definition of s0 | s0 | object2.c:33:3:33:4 | definition of s0 | s0 | +| object1.c:29:3:29:4 | definition of s1 | The object $@ of type NamedStruct1 does not use the same type names as re-declaration $@ of type NamedStruct1 | object1.c:29:3:29:4 | definition of s1 | s1 | object2.c:38:3:38:4 | definition of s1 | s1 | +| object2.c:11:11:11:12 | declaration of a3 | The object $@ of type LL does not use the same type names as re-declaration $@ of type long | object2.c:11:11:11:12 | declaration of a3 | a3 | object1.c:5:6:5:7 | definition of a3 | a3 | +| object2.c:13:12:13:13 | declaration of a4 | The object $@ of type int does not use the same type names as re-declaration $@ of type long | object2.c:13:12:13:13 | declaration of a4 | a4 | object1.c:6:6:6:7 | definition of a4 | a4 | +| object2.c:15:13:15:14 | declaration of a5 | The object $@ of type long does not use the same type names as re-declaration $@ of type int | object2.c:15:13:15:14 | declaration of a5 | a5 | object1.c:7:5:7:6 | definition of a5 | a5 | +| object2.c:19:1:19:3 | declaration of a6 | The object $@ of type int does not use the same type names as re-declaration $@ of type long | object2.c:19:1:19:3 | declaration of a6 | a6 | object1.c:8:6:8:7 | definition of a6 | a6 | +| object2.c:21:11:21:12 | declaration of a7 | The object $@ of type LL does not use the same type names as re-declaration $@ of type int | object2.c:21:11:21:12 | declaration of a7 | a7 | object1.c:9:5:9:6 | definition of a7 | a7 | +| object2.c:24:4:24:6 | definition of a10 | The object $@ of type LI[100] does not use the same type names as re-declaration $@ of type int[100] | object2.c:24:4:24:6 | definition of a10 | a10 | object1.c:15:5:15:7 | definition of a10 | a10 | +| object2.c:25:12:25:14 | declaration of a11 | The object $@ of type int[101] does not use the same type names as re-declaration $@ of type int[100] | object2.c:25:12:25:14 | declaration of a11 | a11 | object1.c:16:5:16:7 | definition of a11 | a11 | +| object2.c:28:13:28:15 | declaration of a13 | The object $@ of type int * does not use the same type names as re-declaration $@ of type int *const | object2.c:28:13:28:15 | declaration of a13 | a13 | object1.c:19:12:19:14 | definition of a13 | a13 | +| object2.c:32:17:32:20 | definition of size | The object $@ of type unsigned char does not use the same type names as re-declaration $@ of type size_t | object2.c:32:17:32:20 | definition of size | size | object1.c:23:10:23:13 | definition of size | size | +| object2.c:33:3:33:4 | definition of s0 | The object $@ of type NamedStruct0 does not use the same type names as re-declaration $@ of type NamedStruct0 | object2.c:33:3:33:4 | definition of s0 | s0 | object1.c:24:3:24:4 | definition of s0 | s0 | +| object2.c:38:3:38:4 | definition of s1 | The object $@ of type NamedStruct1 does not use the same type names as re-declaration $@ of type NamedStruct1 | object2.c:38:3:38:4 | definition of s1 | s1 | object1.c:29:3:29:4 | definition of s1 | s1 | diff --git a/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.qlref b/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.qlref new file mode 100644 index 0000000000..132b3d8f29 --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.qlref @@ -0,0 +1 @@ +rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-3/function1.c b/c/misra/test/rules/RULE-8-3/function1.c new file mode 100644 index 0000000000..f65c98c2ac --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/function1.c @@ -0,0 +1,31 @@ +typedef long LL; + +int f1(); // COMPLIANT +int f2(int f2a); // COMPLIANT + +long f3(); // NON_COMPLIANT + +LL f3(); // NON_COMPLIANT +long f4(int f4a); // NON_COMPLIANT + +long f5(int f5a) { return 0; } // COMPLIANT + +int f6(int f6a) { return 0; } // NON_COMPLIANT + +int f20(int f20a); // NON_COMPLIANT + +typedef int wi; +typedef int hi; +typedef long a; + +a f21(wi w, wi h) { // NON_COMPLIANT + return (a)w * h; +} + +void f22(int f22b, int f22a) { // NON_COMPLIANT + return; +} + +void f23(int f23a) { // COMPLIANT + return; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-3/function2.c b/c/misra/test/rules/RULE-8-3/function2.c new file mode 100644 index 0000000000..d31aaf17e5 --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/function2.c @@ -0,0 +1,21 @@ +int f1(); // COMPLIANT +int f2(int f2a); // COMPLIANT + +long f3(); // NON_COMPLIANT +int f4(int f4a); // NON_COMPLIANT + +long f5(int f5a) { return 0; } // COMPLIANT + +long f6(int f6a) { return 0; } // NON_COMPLIANT + +int f20(int f20a, int f20b); // NON_COMPLIANT + +typedef int wi; +typedef int hi; +typedef long a; + +extern a f21(wi w, hi h); // NON_COMPLIANT + +extern void f22(int f22a, int f22b); // NON_COMPLIANT + +extern void f23(int); // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-3/object1.c b/c/misra/test/rules/RULE-8-3/object1.c new file mode 100644 index 0000000000..0a54c791d3 --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/object1.c @@ -0,0 +1,46 @@ +#include + +int a1; // COMPLIANT +int a2; // COMPLIANT +long a3; // NON_COMPLIANT +long a4; // NON_COMPLIANT +int a5; // NON_COMPLIANT +long a6; // NON_COMPLIANT +int a7; // NON_COMPLIANT + +int a8; // COMPLIANT +extern int a8; // COMPLIANT + +int a9[100]; // COMPLIANT +int a10[100]; // NON_COMPLIANT +int a11[100]; // NON_COMPLIANT - different sizes +int a12; // COMPLIANT + +int *const a13; // NON_COMPLIANT + +struct NamedStruct0 { + int val[10]; + size_t size; // NON_COMPLIANT - different type +} s0; // NON_COMPLIANT - different member type + +struct NamedStruct1 { + int val[10]; + size_t size; +} s1; // NON_COMPLIANT - different member name + +struct { + int val[10]; + size_t size; +} s2; // COMPLIANT + +struct OuterStruct { + struct { + int val[10]; // COMPLIANT + size_t size; + } firstArray; + + struct { + int val[10][2]; // COMPLIANT + size_t size; + } secondArray; +}; diff --git a/c/misra/test/rules/RULE-8-3/object2.c b/c/misra/test/rules/RULE-8-3/object2.c new file mode 100644 index 0000000000..ee090b4a0c --- /dev/null +++ b/c/misra/test/rules/RULE-8-3/object2.c @@ -0,0 +1,43 @@ +#include + +extern int a1; // COMPLIANT + +#define EE extern int a2 +EE; // COMPLIANT + +typedef long LL; +typedef int LI; + +extern LL a3; // NON_COMPLIANT + +extern int a4; // NON_COMPLIANT + +extern long a5; // NON_COMPLIANT + +#define EE1 extern int a6 + +EE1; // NON_COMPLIANT + +extern LL a7; // NON_COMPLIANT + +extern int a9[100]; // COMPLIANT +LI a10[100]; // NON_COMPLIANT +extern int a11[101]; // NON_COMPLIANT - different sizes +signed a12; // COMPLIANT + +extern int *a13; // NON_COMPLIANT + +struct NamedStruct0 { + int val[10]; + unsigned char size; // NON_COMPLIANT - different type +} s0; // NON_COMPLIANT - different member type + +struct NamedStruct1 { + int val[10]; + size_t mysize; +} s1; // NON_COMPLIANT - different member name + +struct { + int val[10]; + size_t size; +} s2; // COMPLIANT diff --git a/c/misra/test/rules/RULE-8-3/test.c b/c/misra/test/rules/RULE-8-3/test.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/c/misra/test/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.expected b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.expected new file mode 100644 index 0000000000..9153fafa97 --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.expected @@ -0,0 +1,2 @@ +| function2.c:5:6:5:7 | definition of f3 | No separate compatible declaration found for this definition. | +| function2.c:7:6:7:7 | definition of f4 | No separate compatible declaration found for this definition. | diff --git a/c/misra/test/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.qlref b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.qlref new file mode 100644 index 0000000000..d5c5e458c6 --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.qlref @@ -0,0 +1 @@ +rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-4/CompatibleDeclarationObjectDefined.expected b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationObjectDefined.expected new file mode 100644 index 0000000000..6655c5d6f7 --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationObjectDefined.expected @@ -0,0 +1,3 @@ +| object1.c:4:12:4:13 | definition of i1 | No separate compatible declaration found for this definition. | +| object1.c:6:5:6:6 | definition of i2 | No separate compatible declaration found for this definition. | +| object2.c:1:7:1:8 | definition of i3 | No separate compatible declaration found for this definition. | diff --git a/c/misra/test/rules/RULE-8-4/CompatibleDeclarationObjectDefined.qlref b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationObjectDefined.qlref new file mode 100644 index 0000000000..a277016125 --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/CompatibleDeclarationObjectDefined.qlref @@ -0,0 +1 @@ +rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-4/function1.c b/c/misra/test/rules/RULE-8-4/function1.c new file mode 100644 index 0000000000..952b6c7d0e --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/function1.c @@ -0,0 +1,3 @@ +extern void f1(); // COMPLIANT +extern void f2(int x, int y); // COMPLIANT +extern void f3(int x, int y); // NON_COMPLIANT diff --git a/c/misra/test/rules/RULE-8-4/function2.c b/c/misra/test/rules/RULE-8-4/function2.c new file mode 100644 index 0000000000..00c7c28200 --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/function2.c @@ -0,0 +1,9 @@ +void f1() {} // COMPLIANT + +void f2(int x, int y) {} // COMPLIANT + +void f3(short x, int y) {} // NON_COMPLIANT + +void f4() {} // NON_COMPLIANT + +static void f5() {} // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-4/object1.c b/c/misra/test/rules/RULE-8-4/object1.c new file mode 100644 index 0000000000..7504bb9327 --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/object1.c @@ -0,0 +1,10 @@ +extern int i; +int i = 0; // COMPLIANT + +extern int i1 = 0; // NON_COMPLIANT + +int i2 = 0; // NON_COMPLIANT + +extern int i3; + +extern int i4; diff --git a/c/misra/test/rules/RULE-8-4/object2.c b/c/misra/test/rules/RULE-8-4/object2.c new file mode 100644 index 0000000000..e432fcb29b --- /dev/null +++ b/c/misra/test/rules/RULE-8-4/object2.c @@ -0,0 +1,3 @@ +short i3 = 0; // NON_COMPLIANT + +signed int i4 = 0; // COMPLIANT diff --git a/c/misra/test/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.expected b/c/misra/test/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.expected new file mode 100644 index 0000000000..63276c3831 --- /dev/null +++ b/c/misra/test/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.expected @@ -0,0 +1,2 @@ +| test.c:8:12:8:13 | declaration of g3 | The variable declaration g3 is declared in multiple files and has an additional $@. | test1.c:1:12:1:13 | declaration of g3 | declaration | +| test.h:1:12:1:12 | declaration of g | The variable declaration g is declared in multiple files and has an additional $@. | test1.h:1:12:1:12 | declaration of g | declaration | diff --git a/c/misra/test/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.qlref b/c/misra/test/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.qlref new file mode 100644 index 0000000000..5359406e92 --- /dev/null +++ b/c/misra/test/rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.qlref @@ -0,0 +1 @@ +rules/RULE-8-5/ExternalObjectOrFunctionNotDeclaredInOneFile.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-5/test.c b/c/misra/test/rules/RULE-8-5/test.c new file mode 100644 index 0000000000..89f0a0972d --- /dev/null +++ b/c/misra/test/rules/RULE-8-5/test.c @@ -0,0 +1,8 @@ +#include "test.h" +#include "test1.h" + +int g = 1; // COMPLIANT + +extern int g1; // COMPLIANT + +extern int g3; // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-5/test.h b/c/misra/test/rules/RULE-8-5/test.h new file mode 100644 index 0000000000..fe5e1e5e2e --- /dev/null +++ b/c/misra/test/rules/RULE-8-5/test.h @@ -0,0 +1,3 @@ +extern int g; // NON_COMPLIANT + +int g2; // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-5/test1.c b/c/misra/test/rules/RULE-8-5/test1.c new file mode 100644 index 0000000000..f00c54998e --- /dev/null +++ b/c/misra/test/rules/RULE-8-5/test1.c @@ -0,0 +1 @@ +extern int g3; // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-5/test1.h b/c/misra/test/rules/RULE-8-5/test1.h new file mode 100644 index 0000000000..fe5e1e5e2e --- /dev/null +++ b/c/misra/test/rules/RULE-8-5/test1.h @@ -0,0 +1,3 @@ +extern int g; // NON_COMPLIANT + +int g2; // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-6/IdentifierWithExternalLinkageOneDefinition.testref b/c/misra/test/rules/RULE-8-6/IdentifierWithExternalLinkageOneDefinition.testref new file mode 100644 index 0000000000..4ab2802c0d --- /dev/null +++ b/c/misra/test/rules/RULE-8-6/IdentifierWithExternalLinkageOneDefinition.testref @@ -0,0 +1 @@ +c/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.expected b/c/misra/test/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.expected new file mode 100644 index 0000000000..6610bf236e --- /dev/null +++ b/c/misra/test/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.expected @@ -0,0 +1,4 @@ +| test2.h:2:13:2:14 | f6 | Declaration with external linkage is accessed in only one translation unit $@. | test2.h:3:22:3:23 | call to f6 | call to f6 | +| test.c:3:5:3:6 | i1 | Declaration with external linkage is accessed in only one translation unit $@. | test.c:11:3:11:4 | i1 | i1 | +| test.c:4:5:4:6 | i2 | Declaration with external linkage is accessed in only one translation unit $@. | test.c:12:3:12:4 | i2 | i2 | +| test.c:6:6:6:7 | f2 | Declaration with external linkage is accessed in only one translation unit $@. | test.c:14:3:14:4 | call to f2 | call to f2 | diff --git a/c/misra/test/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.qlref b/c/misra/test/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.qlref new file mode 100644 index 0000000000..8b41068eef --- /dev/null +++ b/c/misra/test/rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.qlref @@ -0,0 +1 @@ +rules/RULE-8-7/ShouldNotBeDefinedWithExternalLinkage.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-7/test.c b/c/misra/test/rules/RULE-8-7/test.c new file mode 100644 index 0000000000..3789a1d269 --- /dev/null +++ b/c/misra/test/rules/RULE-8-7/test.c @@ -0,0 +1,16 @@ +#include "test.h" +int i = 0; +int i1 = 0; +int i2; // NON_COMPLIANT - accessed one translation unit +void f1() {} // Definition +void f2() {} // Definition +static void f3(){}; // COMPLIANT - internal linkage +void f4() {} // Definition +void f() { + i = 0; + i1 = 0; + i2 = 0; + f1(); + f2(); + f3(); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-7/test.h b/c/misra/test/rules/RULE-8-7/test.h new file mode 100644 index 0000000000..782099de02 --- /dev/null +++ b/c/misra/test/rules/RULE-8-7/test.h @@ -0,0 +1,6 @@ +extern int i; // COMPLIANT - accessed multiple translation units +extern int i1; // NON_COMPLIANT - accessed one translation unit +extern void f1(); // COMPLIANT - accessed multiple translation units +extern void f2(); // NON_COMPLIANT - accessed one translation unit +extern void f4(); // COMPLIANT - accessed across translation units +extern void f5(); // COMPLIANT - no definition \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-7/test1.c b/c/misra/test/rules/RULE-8-7/test1.c new file mode 100644 index 0000000000..5c3b3759c9 --- /dev/null +++ b/c/misra/test/rules/RULE-8-7/test1.c @@ -0,0 +1,7 @@ +#include "test2.h" +void f() { + i = 0; + f1(); + f4(); + f5(); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-7/test2.h b/c/misra/test/rules/RULE-8-7/test2.h new file mode 100644 index 0000000000..d203c3259a --- /dev/null +++ b/c/misra/test/rules/RULE-8-7/test2.h @@ -0,0 +1,3 @@ +#include "test.h" +extern void f6() {} // NON_COMPLIANT +static void test() { f6(); } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-8/MissingStaticSpecifierFunctionRedeclarationC.testref b/c/misra/test/rules/RULE-8-8/MissingStaticSpecifierFunctionRedeclarationC.testref new file mode 100644 index 0000000000..7d9f2ebc04 --- /dev/null +++ b/c/misra/test/rules/RULE-8-8/MissingStaticSpecifierFunctionRedeclarationC.testref @@ -0,0 +1 @@ +c/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-8/MissingStaticSpecifierObjectRedeclarationC.testref b/c/misra/test/rules/RULE-8-8/MissingStaticSpecifierObjectRedeclarationC.testref new file mode 100644 index 0000000000..23ed7c9fc5 --- /dev/null +++ b/c/misra/test/rules/RULE-8-8/MissingStaticSpecifierObjectRedeclarationC.testref @@ -0,0 +1 @@ +c/common/test/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-9/UnnecessaryExposedIdentifierDeclarationC.testref b/c/misra/test/rules/RULE-8-9/UnnecessaryExposedIdentifierDeclarationC.testref new file mode 100644 index 0000000000..35c4abc5d4 --- /dev/null +++ b/c/misra/test/rules/RULE-8-9/UnnecessaryExposedIdentifierDeclarationC.testref @@ -0,0 +1 @@ +c/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-1/ObjectWithAutoStorageDurationReadBeforeInit.testref b/c/misra/test/rules/RULE-9-1/ObjectWithAutoStorageDurationReadBeforeInit.testref new file mode 100644 index 0000000000..45f4b5df27 --- /dev/null +++ b/c/misra/test/rules/RULE-9-1/ObjectWithAutoStorageDurationReadBeforeInit.testref @@ -0,0 +1 @@ +c/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-2/InitializerForAggregateOrUnionNotEnclosedInBraces.testref b/c/misra/test/rules/RULE-9-2/InitializerForAggregateOrUnionNotEnclosedInBraces.testref new file mode 100644 index 0000000000..91bc9e1c63 --- /dev/null +++ b/c/misra/test/rules/RULE-9-2/InitializerForAggregateOrUnionNotEnclosedInBraces.testref @@ -0,0 +1 @@ +c/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.expected b/c/misra/test/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.expected new file mode 100644 index 0000000000..25774b2d25 --- /dev/null +++ b/c/misra/test/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.expected @@ -0,0 +1,6 @@ +| test.c:7:15:7:21 | {...} | Aggregate literal for type int[4]$@ is missing an explicit initializer for $@. | file://:0:0:0:0 | int[4] | int[4] | test.c:7:15:7:21 | {...} | the elements in the index range 2 to 3 | +| test.c:8:18:8:26 | {...} | Aggregate literal for type int[2][2]$@ is missing an explicit initializer for $@. | file://:0:0:0:0 | int[2][2] | int[2][2] | test.c:8:18:8:26 | {...} | the element at index 1 | +| test.c:12:18:12:35 | {...} | Aggregate literal for type int[2][2]$@ is missing an explicit initializer for $@. | file://:0:0:0:0 | int[2][2] | int[2][2] | test.c:12:18:12:35 | {...} | the element at index 1 | +| test.c:14:18:15:25 | {...} | Aggregate literal for type int[2][2]$@ is missing an explicit initializer for $@. | file://:0:0:0:0 | int[2][2] | int[2][2] | test.c:14:18:15:25 | {...} | the element at index 1 | +| test.c:20:18:20:32 | {...} | Aggregate literal for type int[2][2]$@ is missing an explicit initializer for $@. | file://:0:0:0:0 | int[2][2] | int[2][2] | test.c:20:18:20:32 | {...} | the element at index 1 | +| test.c:31:43:31:43 | {...} | Aggregate literal for type int[4]$@ is missing an explicit initializer for $@. | file://:0:0:0:0 | int[4] | int[4] | test.c:31:43:31:43 | {...} | the elements in the index range 1 to 3 | diff --git a/c/misra/test/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.qlref b/c/misra/test/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.qlref new file mode 100644 index 0000000000..f4892f425c --- /dev/null +++ b/c/misra/test/rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.qlref @@ -0,0 +1 @@ +rules/RULE-9-3/PartiallyInitializedArrayWithExplicitInitializers.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-3/test.c b/c/misra/test/rules/RULE-9-3/test.c new file mode 100644 index 0000000000..f403bf64c6 --- /dev/null +++ b/c/misra/test/rules/RULE-9-3/test.c @@ -0,0 +1,37 @@ +void test() { + int l01[4] = {1, 2, 3, 4}; // COMPLIANT + int l02[4][2] = {{1, 2}, {3, 4}, {3, 4}, {3, 4}}; // COMPLIANT + int l03[4][2] = {1, 2, 3, 4, 3, 4, 3, 4}; // COMPLIANT + int l04[4][2] = {0}; // COMPLIANT + int l06[4][2] = {{0}, {0}, {0}, {0}}; // COMPLIANT + int l08[4] = {1, 2}; // NON_COMPLIANT + int l09[2][2] = {{1, 2}}; // NON_COMPLIANT + int l10[2][2] = {{1, 2}, [1] = {0}}; // COMPLIANT + int l11[2][2] = {{1, 2}, [1] = 0}; // COMPLIANT + int l12[2][2] = {{1, 2}, [1][0] = 0, [1][1] = 0}; // COMPLIANT + int l13[2][2] = {{0}, [1][0] = 0}; // NON_COMPLIANT - not all elements + // initialized with designated initializer + int l14[2][2] = { + {0}, [1][0] = 0, 0}; // NON_COMPLIANT - not all elements + // initialized with designated initializer + + int l15[2] = {[1] = 0}; // COMPLIANT - sparse matrix initialized with + // designated initializer + int l16[2][2] = {[0] = {0, 1}}; // NON_COMPLIANT - sub-elements not + // initialized with designated initializer + + int l17[4][4] = { + [0][0] = 0, [0][1] = 0, [0][2] = 0, [0][3] = 0, [2][0] = 0, + [2][1] = 0, [2][2] = 0, [2][3] = 0}; // COMPLIANT - sparse matrix + // initialized with designated + // initializer + + int l18[4][4] = { + [0][0] = 0, [0][1] = 0, [0][2] = 0, [0][3] = 0, [2][0] = 0, + [2][1] = 0, [2][2] = 0, [2][3] = 0, 2}; // NON_COMPLIANT - not all + // elements initialized with + // designated initializer + + char str1[4] = "abc"; // COMPLIANT + char str2[5] = "abc"; // COMPLIANT - array initialized by string literal +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.expected b/c/misra/test/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.expected new file mode 100644 index 0000000000..e50cb4d2c7 --- /dev/null +++ b/c/misra/test/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.expected @@ -0,0 +1,4 @@ +| test.c:10:16:10:16 | 1 | $@ repeats initialization of element [0], which is already initialized $@. | test.c:9:14:10:26 | {...} | Array aggregate literal | test.c:10:7:10:7 | 0 | here | +| test.c:12:28:12:28 | 1 | $@ repeats initialization of element [0][0], which is already initialized $@. | test.c:12:17:16:29 | {...} | Array aggregate literal | test.c:16:28:16:28 | 1 | here | +| test.c:25:64:25:64 | 7 | $@ repeats initialization of element [1][1][0], which is already initialized $@. | test.c:24:20:26:35 | {...} | Array aggregate literal | test.c:26:34:26:34 | 1 | here | +| test.c:36:17:37:25 | {...} | $@ repeats initialization of element s1::a. | test.c:36:17:37:25 | {...} | Structure aggregate literal | test.c:36:17:37:25 | {...} | here | diff --git a/c/misra/test/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.qlref b/c/misra/test/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.qlref new file mode 100644 index 0000000000..512ebb1a5a --- /dev/null +++ b/c/misra/test/rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.qlref @@ -0,0 +1 @@ +rules/RULE-9-4/RepeatedInitializationOfAggregateObjectElement.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-4/test.c b/c/misra/test/rules/RULE-9-4/test.c new file mode 100644 index 0000000000..7510155577 --- /dev/null +++ b/c/misra/test/rules/RULE-9-4/test.c @@ -0,0 +1,38 @@ +struct s1 { + int a; + int b; +}; + +void test_arrays(void) { + int a1[2] = {1, 2}; // COMPLIANT + int a2[2] = {[0] = 1, [1] = 2}; // COMPLIANT + int a3[2] = { + 0, [0] = 1, [1] = 2}; // NON_COMPLIANT - repeated initialiation of [0] + int a4[2][2] = {[0][0] = 1, [0][1] = 2, [1][0] = 3, [1][1] = 4}; // COMPLIANT + int a5[2][2] = {[0][0] = 1, + [0][1] = 2, + [1][0] = 3, + [1][1] = 4, + [0][0] = 1}; // NON_COMPLIANT + // - repeated + // initialiation + // of [0][0] + int a6[2][2][2] = { + [0][0][0] = 1, [0][0][1] = 2, [0][1][0] = 3, [0][1][1] = 4, + [1][0][0] = 5, [1][0][1] = 6, [1][1][0] = 7, [1][1][1] = 8}; // COMPLIANT + + int a7[2][2][2] = {[0][0][0] = 1, [0][0][1] = 2, [0][1][0] = 3, [0][1][1] = 4, + [1][0][0] = 5, [1][0][1] = 6, [1][1][0] = 7, [1][1][1] = 8, + [1][1][0] = 1}; // NON_COMPLIANT + // - repeated + // initialiation + // of [0][0][0] +} + +void test_structs(void) { + struct s1 s1 = {0}; // COMPLIANT + struct s1 s2 = {0, 1}; // COMPLIANT + struct s1 s3 = {.a = 0, .b = 1}; // COMPLIANT + struct s1 s4 = {.a = 0, + .a = 1}; // NON_COMPLIANT - repeated initialiation of .a +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-7/UninitializedAtomicObject.expected b/c/misra/test/rules/RULE-9-7/UninitializedAtomicObject.expected new file mode 100644 index 0000000000..f96fc6aa13 --- /dev/null +++ b/c/misra/test/rules/RULE-9-7/UninitializedAtomicObject.expected @@ -0,0 +1,4 @@ +| test.c:24:15:24:16 | definition of l3 | Atomic object 'l3' has no initializer or corresponding use of 'atomic_init()'. | +| test.c:27:15:27:16 | definition of l4 | Atomic object 'l4' has no initializer or corresponding use of 'atomic_init()'. | +| test.c:31:15:31:16 | definition of l5 | Atomic object 'l5' has no initializer or corresponding use of 'atomic_init()'. | +| test.c:41:15:41:16 | definition of l7 | Atomic object 'l7' has no initializer or corresponding use of 'atomic_init()'. | diff --git a/c/misra/test/rules/RULE-9-7/UninitializedAtomicObject.qlref b/c/misra/test/rules/RULE-9-7/UninitializedAtomicObject.qlref new file mode 100644 index 0000000000..11219b0741 --- /dev/null +++ b/c/misra/test/rules/RULE-9-7/UninitializedAtomicObject.qlref @@ -0,0 +1 @@ +rules/RULE-9-7/UninitializedAtomicObject.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-9-7/test.c b/c/misra/test/rules/RULE-9-7/test.c new file mode 100644 index 0000000000..90645f6372 --- /dev/null +++ b/c/misra/test/rules/RULE-9-7/test.c @@ -0,0 +1,46 @@ +#include "stdatomic.h" +#include "threads.h" + +_Atomic int g1; // COMPLIANT +_Atomic int g2 = 0; // COMPLIANT + +int f_thread(void *x); + +void f_starts_thread() { + thrd_t t; + thrd_create(&t, f_thread, 0); +} + +void f_may_initialize_argument(void *p1) {} + +int main(int argc, char *argv[]) { + _Atomic int l1 = 1; // COMPLIANT + f_starts_thread(); + + _Atomic int l2; // COMPLIANT + atomic_init(&l2, 0); + f_starts_thread(); + + _Atomic int l3; // NON-COMPLIANT + f_starts_thread(); + + _Atomic int l4; // NON-COMPLIANT + f_starts_thread(); + atomic_init(&l4, 0); + + _Atomic int l5; // NON-COMPLIANT + if (g1 == 0) { + atomic_init(&l5, 0); + } + f_starts_thread(); + + _Atomic int l6; // COMPLIANT + f_may_initialize_argument(&l6); + f_starts_thread(); + + _Atomic int l7; // NON_COMPLIANT + if (g1 == 0) { + f_may_initialize_argument(&l7); + } + f_starts_thread(); +} \ No newline at end of file diff --git a/change_notes/2022-02-23-a13-5-2-fix-reported-fp-for-a13-5-2.md b/change_notes/2022-02-23-a13-5-2-fix-reported-fp-for-a13-5-2.md new file mode 100644 index 0000000000..5719e30b1d --- /dev/null +++ b/change_notes/2022-02-23-a13-5-2-fix-reported-fp-for-a13-5-2.md @@ -0,0 +1 @@ +- `A13-5-2` - address a false positive where lambda expressions with empty captures were being flagged as having a non-compliant conversion operator. \ No newline at end of file diff --git a/change_notes/2022-02-23-fix-reported-fp-for-a0-1-2-and-a0-1-4.md b/change_notes/2022-02-23-fix-reported-fp-for-a0-1-2-and-a0-1-4.md new file mode 100644 index 0000000000..f10f4acf0b --- /dev/null +++ b/change_notes/2022-02-23-fix-reported-fp-for-a0-1-2-and-a0-1-4.md @@ -0,0 +1,4 @@ +- `A0-1-2` + - Addressed false positives where the return values are cast to `void` in C-style or assigned to `std::ignore`. +- `A0-1-4` + - Addressed false positives where the parameters are marked with attribute `[[maybe_unused]]`, or either cast to `void` in C-style or assigned to `std::ignore` in the function body. \ No newline at end of file diff --git a/change_notes/2022-03-08-update-to-CodeQL-2.10.5.md b/change_notes/2022-03-08-update-to-CodeQL-2.10.5.md new file mode 100644 index 0000000000..7b08c18c41 --- /dev/null +++ b/change_notes/2022-03-08-update-to-CodeQL-2.10.5.md @@ -0,0 +1,3 @@ +- `RULE-8-4` - `CompatibleDeclarationObjectDefined.ql` + - Update rule implementation based on changes in the CodeQL libraries. +- Updated the CodeQL version to `2.10.5`. \ No newline at end of file diff --git a/change_notes/2022-03-16-update-for-dataflow-changes.md b/change_notes/2022-03-16-update-for-dataflow-changes.md new file mode 100644 index 0000000000..af0aaed7ca --- /dev/null +++ b/change_notes/2022-03-16-update-for-dataflow-changes.md @@ -0,0 +1,2 @@ + - `FIO32-C` - `DoNotPerformFileOperationsOnDevices.ql`: + - The query was updated to work with the latest version of the dataflow library. diff --git a/change_notes/2022-06-28-detect-static-namespace-members.md b/change_notes/2022-06-28-detect-static-namespace-members.md new file mode 100644 index 0000000000..05af4deb79 --- /dev/null +++ b/change_notes/2022-06-28-detect-static-namespace-members.md @@ -0,0 +1,6 @@ +- `A2-10-4` - `IdentifierNameOfStaticFunctionReusedInNamespace.ql`: + - Reuse of an identifier name of a static function in a namespace is now detected. +- `A2-10-4` - `IdentifierNameOfStaticNonMemberObjectReusedInNamespace.ql`: + - Reuse of an identifier name of a static non-member object in a namespace is now detected. +- `A2-10-5` - `IdentifierNameOfStaticNonMemberObjectWithExternalOrInternalLinkageIsReused.ql`: + - Reuse of an identifier name of a static non-member object with internal linkage in a namespace is now detected. diff --git a/change_notes/2022-11-02-c-style-casts-library-macros.md b/change_notes/2022-11-02-c-style-casts-library-macros.md new file mode 100644 index 0000000000..92bfa4c435 --- /dev/null +++ b/change_notes/2022-11-02-c-style-casts-library-macros.md @@ -0,0 +1,4 @@ + - `A5-2-2` - `TraditionalCStyleCastsUsed.ql` + - Reduced false positives by excluding casts generated by library macros (i.e. macros defined outside the source location) + - Improved the message to cite the macro which generated the c-style cast, if any. + - Improved the message to cite the type being casted to, to aid with identification and remediation. \ No newline at end of file diff --git a/change_notes/2022-11-02-m0-1-4-single-use-with-templates.md b/change_notes/2022-11-02-m0-1-4-single-use-with-templates.md new file mode 100644 index 0000000000..c6c2c5d99e --- /dev/null +++ b/change_notes/2022-11-02-m0-1-4-single-use-with-templates.md @@ -0,0 +1,2 @@ + - `M0-1-4` - `SingleUseMemberPODVariable.ql` + - Reduce false positives by excluding any constexpr variable whose constant value is used as an argument to a template. \ No newline at end of file diff --git a/change_notes/2022-11-04-refactor-dir-rules.md b/change_notes/2022-11-04-refactor-dir-rules.md new file mode 100644 index 0000000000..303f718ddd --- /dev/null +++ b/change_notes/2022-11-04-refactor-dir-rules.md @@ -0,0 +1,9 @@ +- The following rules have been renamed: + - RULE-4-4 has been renamed to DIR-4-4 to reflect correct naming as per + MISRA C:2012 standard. + - RULE-4-8 has been renamed to DIR-4-8 to reflect correct naming as per + MISRA C:2012 standard. + - RULE-4-10 has been renamed to DIR-4-10 to reflect correct naming as per + MISRA C:2012 standard. + - RULE-4-12 has been renamed to DIR-4-12 to reflect correct naming as per + MISRA C:2012 standard. \ No newline at end of file diff --git a/change_notes/2022-11-07-add-guideline-recategorization-scripts.md b/change_notes/2022-11-07-add-guideline-recategorization-scripts.md new file mode 100644 index 0000000000..137f21216d --- /dev/null +++ b/change_notes/2022-11-07-add-guideline-recategorization-scripts.md @@ -0,0 +1,2 @@ +- Add the Python scripts under `scripts/guideline_recategorization` and the JSON schemas under `schemas`. +- Add the Python scripts under `scripts/shared` relied upon by the analysis report generation. \ No newline at end of file diff --git a/change_notes/2022-11-14-fix-RULE-11-7-message.md b/change_notes/2022-11-14-fix-RULE-11-7-message.md new file mode 100644 index 0000000000..f1cb253f3b --- /dev/null +++ b/change_notes/2022-11-14-fix-RULE-11-7-message.md @@ -0,0 +1,2 @@ + - `RULE-11-7` - `CastBetweenPointerToObjectAndNonIntArithmeticType.ql` + - Corrected the query output message to describe a cast involving a pointer to an object rather than a void pointer. \ No newline at end of file diff --git a/change_notes/2022-11-15-consistent-gvn-library0use.md b/change_notes/2022-11-15-consistent-gvn-library0use.md new file mode 100644 index 0000000000..cb287a172f --- /dev/null +++ b/change_notes/2022-11-15-consistent-gvn-library0use.md @@ -0,0 +1,4 @@ + - `EXP30-C` - `DependenceOnOrderOfFunctionArgumentsForSideEffects.ql`: + - Prefer the `GlobalValueNumbering` CodeQL library over the `GlobalValueNumberingImpl` library, as the former yields higher quality results and the latter is going to be deprecated. This also improves performance when multiple queries are evaluated, due to more sharing of intermediate computations. +- `EXP50-CPP` - `DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql`: + - Prefer the `GlobalValueNumbering` CodeQL library over the `GlobalValueNumberingImpl` library, as the former yields higher quality results and the latter is going to be deprecated. This also improves performance when multiple queries are evaluated, due to more sharing of intermediate computations. diff --git a/change_notes/2022-11-21-null-sizet-not-cstdio.md b/change_notes/2022-11-21-null-sizet-not-cstdio.md new file mode 100644 index 0000000000..8855fa050f --- /dev/null +++ b/change_notes/2022-11-21-null-sizet-not-cstdio.md @@ -0,0 +1,3 @@ + - `M27-0-1` + - `CstdioTypesUsed.ql` - Exclude `size_t` from this rule, as it can be provided by headers other than ``. + - `CstdioMacrosUsed.ql` - Exclude `NULL` from this rule, as it can be provided by headers other than ``. diff --git a/change_notes/2022-12-06-remove-use-of-default-taint-tracking.md b/change_notes/2022-12-06-remove-use-of-default-taint-tracking.md new file mode 100644 index 0000000000..2f0c6706fc --- /dev/null +++ b/change_notes/2022-12-06-remove-use-of-default-taint-tracking.md @@ -0,0 +1,2 @@ + - `FIO32-C` - `DoNotPerformFileOperationsOnDevices.ql`: + - The query was rewritten to no longer depend of the `DefaultTaintTracking` library, which will be deprecated. diff --git a/change_notes/2023-01-09-cstylecasts-template-parameters.md b/change_notes/2023-01-09-cstylecasts-template-parameters.md new file mode 100644 index 0000000000..610c4b01fe --- /dev/null +++ b/change_notes/2023-01-09-cstylecasts-template-parameters.md @@ -0,0 +1,2 @@ + - `A5-2-2` + - `CStyleCasts.ql` - exclude template parameters to avoid false positives when using the "functional notation" syntax. In addition, provide a greater explanation on limitations of this query. diff --git a/change_notes/2023-01-11-dead-code-macros.md b/change_notes/2023-01-11-dead-code-macros.md new file mode 100644 index 0000000000..515b031c02 --- /dev/null +++ b/change_notes/2023-01-11-dead-code-macros.md @@ -0,0 +1 @@ + - `M0-1-9`: This query previously excluded all results which were affected by a macro expansion. This is because a macro may be expanded multiple times with code that is dead in one expansion but live in another. This query has been modified to exclude results only where the entirety of a statement is generated by a macro. This reduces false negatives where the statements liveness is not affected by the macro expansion. \ No newline at end of file diff --git a/change_notes/2023-02-07-extend-getatypeuse.md b/change_notes/2023-02-07-extend-getatypeuse.md new file mode 100644 index 0000000000..1d769d1e04 --- /dev/null +++ b/change_notes/2023-02-07-extend-getatypeuse.md @@ -0,0 +1,2 @@ +`A0-4-1` - `FloatingPointImplementationShallComplyWithIeeeStandard.ql`: + - May return more results due to improvements to underlying `getATypeUse`. \ No newline at end of file diff --git a/change_notes/2023-02-10-refactor-test-cases.md b/change_notes/2023-02-10-refactor-test-cases.md new file mode 100644 index 0000000000..102551c760 --- /dev/null +++ b/change_notes/2023-02-10-refactor-test-cases.md @@ -0,0 +1,2 @@ + - `A1-1-2`: Refactored this test case to support better cross-compiler testing. + - `A1-1-3`: Added support for alternate spelling of compiler flag. \ No newline at end of file diff --git a/change_notes/2023-03-02-simplify-stdlib-queries.md b/change_notes/2023-03-02-simplify-stdlib-queries.md new file mode 100644 index 0000000000..b705ec993f --- /dev/null +++ b/change_notes/2023-03-02-simplify-stdlib-queries.md @@ -0,0 +1,11 @@ + * Improved alert message to avoid reporting locations in standard header files, which cannot be viewed in Code Scanning, in the following queries: + - `Rule 21.4` + - `Rule 21.5` + - `Rule 21.6` + - `Rule 21.7` + - `Rule 21.8` + - `Rule 21.9` + - `Rule 21.10` + - `Rule 21.11` + - `Rule 21.12` + - `Rule 21.21` \ No newline at end of file diff --git a/change_notes/2023-03-06-A13-2-2-message.md b/change_notes/2023-03-06-A13-2-2-message.md new file mode 100644 index 0000000000..5af068a605 --- /dev/null +++ b/change_notes/2023-03-06-A13-2-2-message.md @@ -0,0 +1,2 @@ + - `A13-2-2` - `BinaryOperatorAndBitwiseOperatorReturnAPrvalue.ql` + - The formatting of the query output message has been changed and operators are now displayed starting with the return type instead of ending with it. diff --git a/change_notes/2023-03-06-better-modeling-of-stdatomic.md b/change_notes/2023-03-06-better-modeling-of-stdatomic.md new file mode 100644 index 0000000000..c34b528620 --- /dev/null +++ b/change_notes/2023-03-06-better-modeling-of-stdatomic.md @@ -0,0 +1,6 @@ + - `CON41-C`: Refactored to address compiler compatibility issues. More accurate + modeling of cases where macros are modeled against other macros such as + `atomic_compare_exchange_weak` and `atomic_store`. + - `CON40-C`: Refactored to address compiler compatibility issues. More accurate + modeling of cases where macros are modeled against other macros such as + `atomic_compare_exchange_weak` and `atomic_store`. \ No newline at end of file diff --git a/change_notes/2023-03-07-20-12-perf.md b/change_notes/2023-03-07-20-12-perf.md new file mode 100644 index 0000000000..b42c81ebce --- /dev/null +++ b/change_notes/2023-03-07-20-12-perf.md @@ -0,0 +1 @@ + * `Rule 20.12` - the performance of this rule has been improved. \ No newline at end of file diff --git a/change_notes/2023-03-08-identifier-performance.md b/change_notes/2023-03-08-identifier-performance.md new file mode 100644 index 0000000000..39c2d26bbf --- /dev/null +++ b/change_notes/2023-03-08-identifier-performance.md @@ -0,0 +1,3 @@ + * The performance of the following identifier related rules has been improved: + * MISRA C 2012 `Rule 5.8` + * MISRA C 2012 `Rule 8.7` \ No newline at end of file diff --git a/change_notes/2023-03-09-changed-alert-messages.md b/change_notes/2023-03-09-changed-alert-messages.md new file mode 100644 index 0000000000..4756d7822f --- /dev/null +++ b/change_notes/2023-03-09-changed-alert-messages.md @@ -0,0 +1,2 @@ + - `M6-6-2`: Changed formatting of the alert message. + - `M6-4-2`: Changed formatting of alert message. diff --git a/change_notes/2023-03-13-ctype-improvements.md b/change_notes/2023-03-13-ctype-improvements.md new file mode 100644 index 0000000000..c6eb55c56d --- /dev/null +++ b/change_notes/2023-03-13-ctype-improvements.md @@ -0,0 +1 @@ + * `STR37-C` - reduce false negatives by improving detection when the `` functions are implemented using macros. \ No newline at end of file diff --git a/change_notes/2023-03-13-fp-a16-0-1.md b/change_notes/2023-03-13-fp-a16-0-1.md new file mode 100644 index 0000000000..d1cf580ac6 --- /dev/null +++ b/change_notes/2023-03-13-fp-a16-0-1.md @@ -0,0 +1 @@ + * `A16-0-1` - reduce unneeded results related to `#pragma`, as it's already reported by A16-7-1. \ No newline at end of file diff --git a/change_notes/2023-03-13-fp-dcl51-cpp.md b/change_notes/2023-03-13-fp-dcl51-cpp.md new file mode 100644 index 0000000000..f1a2ee65f8 --- /dev/null +++ b/change_notes/2023-03-13-fp-dcl51-cpp.md @@ -0,0 +1 @@ + * `DCL51-CPP` - reduce false positives related to use of `__func__` \ No newline at end of file diff --git a/change_notes/2023-03-14-fio42-c-fix-logic-error.md b/change_notes/2023-03-14-fio42-c-fix-logic-error.md new file mode 100644 index 0000000000..c4b8019789 --- /dev/null +++ b/change_notes/2023-03-14-fio42-c-fix-logic-error.md @@ -0,0 +1,3 @@ + - `FIO42-C` - `CloseFilesWhenTheyAreNoLongerNeeded.ql`: + - Parentheses have been added to a resolve previously lacking parentheses in the `where` clause, such that the exclusion mechanism only functioned for a certain subset of results. + - The query implementation has been moved to a shared implementation. \ No newline at end of file diff --git a/change_notes/2023-03-14-fp-a2-10-1.md b/change_notes/2023-03-14-fp-a2-10-1.md new file mode 100644 index 0000000000..f6dcd3d865 --- /dev/null +++ b/change_notes/2023-03-14-fp-a2-10-1.md @@ -0,0 +1,2 @@ + * `A2-10-1` - reduce false positives for identifiers in same scope and relating to template variables + * `RULE-5-3`- reduce false positives for identifiers in same scope \ No newline at end of file diff --git a/change_notes/2023-03-15-fix-reported-fp-for-A15-4-4.md b/change_notes/2023-03-15-fix-reported-fp-for-A15-4-4.md new file mode 100644 index 0000000000..c7cb61d2c9 --- /dev/null +++ b/change_notes/2023-03-15-fix-reported-fp-for-A15-4-4.md @@ -0,0 +1,2 @@ +- `A15-4-4` - `MissingNoExcept.ql` + - Exclude call operators embedded in a lambda expression from functions to be declared `noexcept` or `noexcept(false)`. diff --git a/change_notes/2023-03-15-fix-reported-fp-for-A2-7-3.md b/change_notes/2023-03-15-fix-reported-fp-for-A2-7-3.md new file mode 100644 index 0000000000..ce98bab27a --- /dev/null +++ b/change_notes/2023-03-15-fix-reported-fp-for-A2-7-3.md @@ -0,0 +1,2 @@ +- `A2-7-3` - `UndocumentedUserDefinedType.ql`: + - Exclude lambda functions from program elements to be documented. diff --git a/change_notes/2023-03-16-essential-types-performance.md b/change_notes/2023-03-16-essential-types-performance.md new file mode 100644 index 0000000000..115c162e89 --- /dev/null +++ b/change_notes/2023-03-16-essential-types-performance.md @@ -0,0 +1,12 @@ + * The performance of the following queries related to essential types have been improved: + * `Rule 10.1` + * `Rule 10.2` + * `Rule 10.3` + * `Rule 10.4` + * `Rule 10.5` + * `Rule 10.6` + * `Rule 10.7` + * `Rule 10.8` + * `Rule 14.1` + * `Rule 21.14` + * `Rule 21.16` \ No newline at end of file diff --git a/change_notes/2023-03-16-fp-a5-1-1.md b/change_notes/2023-03-16-fp-a5-1-1.md new file mode 100644 index 0000000000..61e4fed11c --- /dev/null +++ b/change_notes/2023-03-16-fp-a5-1-1.md @@ -0,0 +1 @@ + * `A5-1-1` - reduce false positives by omitting literals written into file streams and wrappers around log and stream calls \ No newline at end of file diff --git a/change_notes/2023-03-20-constant-integer-expression-wrap-casted.md b/change_notes/2023-03-20-constant-integer-expression-wrap-casted.md new file mode 100644 index 0000000000..321fff714f --- /dev/null +++ b/change_notes/2023-03-20-constant-integer-expression-wrap-casted.md @@ -0,0 +1,2 @@ + * `M5-19-1`: + - Reduce false negatives by fixing a bug where a constant expression was immediately casted to a signed type. \ No newline at end of file diff --git a/change_notes/2023-03-26-nested-switch-case.md b/change_notes/2023-03-26-nested-switch-case.md new file mode 100644 index 0000000000..0265f46954 --- /dev/null +++ b/change_notes/2023-03-26-nested-switch-case.md @@ -0,0 +1 @@ + * `M6-4-4` - alert message updated for clarity. \ No newline at end of file diff --git a/change_notes/2023-03-27-integer-overflow.md b/change_notes/2023-03-27-integer-overflow.md new file mode 100644 index 0000000000..8c86bae307 --- /dev/null +++ b/change_notes/2023-03-27-integer-overflow.md @@ -0,0 +1,4 @@ + * `A4-7-1` - `IntegerExpressionLeadToDataLoss.ql` - reduce false positives and false negatives by: + - Identifying additional categories of valid guard. + - Excluding guards which were not proven to prevent overflow or underflow. + - Expand coverage to include unary operations and arithmetic assignment operations. \ No newline at end of file diff --git a/change_notes/2023-04-24-fix-compatibility-issues-with-qnx.md b/change_notes/2023-04-24-fix-compatibility-issues-with-qnx.md new file mode 100644 index 0000000000..1eb65975f8 --- /dev/null +++ b/change_notes/2023-04-24-fix-compatibility-issues-with-qnx.md @@ -0,0 +1,9 @@ +* Fix compatibility issues with the `qcc` compiler and standard headers: + * `RULE-21-4`: `longjmp` can be implmented as macro + * `ENV32-C`: exit functions can be implmented as macro + * `ERR33-C` `FIO34-C` `FIO46-C` `RULE-22-6`: the library files `ReadErrorsAndEOF.qll` `DoNotAccessAClosedFile.qll` `FileAccess.qll` have been updated to support different definitions of IO related functions and macros + * `RULE-10-6`: Fix output string format + * `STR37-C`: add support for a different `tolower/toupper` macro implementation + * `EXP43-C`: add explicit support for library functions that are mentioned in the rule description + * `RULE-11-1` `RULE-11-2` `RULE-11-5`: support for a different NULL pointer definition + * `STR38-C`: removed links to library internals in the output message \ No newline at end of file diff --git a/change_notes/2023-04-28-a14-7-2.md b/change_notes/2023-04-28-a14-7-2.md new file mode 100644 index 0000000000..b74b95449f --- /dev/null +++ b/change_notes/2023-04-28-a14-7-2.md @@ -0,0 +1 @@ + * `A14-7-2` - alert messages have been slightly adjusted to refer only to the base name of a file, not the full relative path. \ No newline at end of file diff --git a/change_notes/2023-04-28-dcl56-cpp-perf.md b/change_notes/2023-04-28-dcl56-cpp-perf.md new file mode 100644 index 0000000000..61ce45b133 --- /dev/null +++ b/change_notes/2023-04-28-dcl56-cpp-perf.md @@ -0,0 +1 @@ + * `DCL56-CPP` - performance has been improved for databases with complex initializers. \ No newline at end of file diff --git a/change_notes/2023-05-02-func-c-style.md b/change_notes/2023-05-02-func-c-style.md new file mode 100644 index 0000000000..46e1710010 --- /dev/null +++ b/change_notes/2023-05-02-func-c-style.md @@ -0,0 +1,3 @@ + * Exclude the use of `__func__` from certain queries, as it is the proscribed way to return the name of the current function: + * `A27-0-4` - Use of the value returned by `__func__` is no longer flagged as a use of C-style strings. + * `A18-1-1` - `__func__` is no longer flagged as a declaration of a variable using C-style arrays. \ No newline at end of file diff --git a/change_notes/2023-05-02-single-reserved-prefix-generated.md b/change_notes/2023-05-02-single-reserved-prefix-generated.md new file mode 100644 index 0000000000..59bde6cca0 --- /dev/null +++ b/change_notes/2023-05-02-single-reserved-prefix-generated.md @@ -0,0 +1 @@ + * `DCL51-CPP` - `cpp/cert/use-of-single-underscore-reserved-prefix` - remove false positives which were compiler generated, such as the function `_FUN` generated by the compiler for lambdas converted to function pointers. \ No newline at end of file diff --git a/change_notes/2023-05-22-pass-compiler-tests-qcc.md b/change_notes/2023-05-22-pass-compiler-tests-qcc.md new file mode 100644 index 0000000000..f6222fa30b --- /dev/null +++ b/change_notes/2023-05-22-pass-compiler-tests-qcc.md @@ -0,0 +1,18 @@ +Fix issues emerged running the test suite when compiled with `qcc`: +- Fix False Negatives issues + - `A1-1-1`: restrict alerts to mentioned types + - `A5-2-5`: get type for `value_type` + - `A18-1-2` `A18-1-3` `A18-9-1`: support std inline namespaces (`std::__1`) + - `A23-0-1` `A23-0-2`: functions in `std` might be defined in inline namespaces + - `M0-1-4`: removed test case + - `M6-5-2`: equality operator alternative implementations + - `M17-0-5`: `longjmp` might be a macro + - `CTR51-CPP` `CTR53-CPP` `ERR50-CPP` `ERR52-CPP` `STR52-CPP`: fixed by library changes + - `MSC51-CPP`: `time` can be in the global scope + - `STR51-CPP`: String constructor might have 1 parameter. +- Fix False Positives issues + - `STR53-CPP`: compute initial container size for copy constructor + - `A0-4-1`: numeric limits might be defined in `__libcpp_numeric_limits` + - `A0-4-3`: the rule now only checks the last `-std` compilation flag +- Fix exclusion criteria using the `isExcluded()` predicate + - `A2-13-3` `A8-4-4` diff --git a/change_notes/2023-06-28-unused-local-function-use-cases.md b/change_notes/2023-06-28-unused-local-function-use-cases.md new file mode 100644 index 0000000000..41a8833252 --- /dev/null +++ b/change_notes/2023-06-28-unused-local-function-use-cases.md @@ -0,0 +1,6 @@ +- `A0-1-3` - Considered the following additional use cases while reporting a local function as "unused". + - The address of a function is taken + - The operand of an expression in an unevaluated context + - Functions marked with [[maybe_unused]] + - Explicitly deleted functions e.g. =delete + - Use of any overload of a function in an overload set constitute a use of all members of the set. An overload set is a set of functions with the same name that differ in the number, type and/or qualifiers of their parameters, and, for the purpose of this query, are limited to functions which are declared in the same scope (namespace or class). diff --git a/change_notes/2023-07-04-remove-compiler-generated-vars.md b/change_notes/2023-07-04-remove-compiler-generated-vars.md new file mode 100644 index 0000000000..b81909405b --- /dev/null +++ b/change_notes/2023-07-04-remove-compiler-generated-vars.md @@ -0,0 +1,2 @@ + * `A7-1-5` - exclude compiler generated variables, such as those generated by for loops. + * `M8-0-1` - exclude compiler generated variables, such as those generated by for loops. \ No newline at end of file diff --git a/change_notes/2023-07-11-lambda-expr-without-param-list.md b/change_notes/2023-07-11-lambda-expr-without-param-list.md new file mode 100644 index 0000000000..d5f50e6853 --- /dev/null +++ b/change_notes/2023-07-11-lambda-expr-without-param-list.md @@ -0,0 +1 @@ + - `A5-1-3` - Only consider lambdas that have zero arguments, since any lambda with non-zero arguments will have an explicit argument list. diff --git a/change_notes/2023-07-26-unused-local-variable.md b/change_notes/2023-07-26-unused-local-variable.md new file mode 100644 index 0000000000..1f71a5b67f --- /dev/null +++ b/change_notes/2023-07-26-unused-local-variable.md @@ -0,0 +1 @@ + - `M0-1-3` - Consider constexpr variables used in template instantiations as "used". diff --git a/change_notes/2023-07-28-rule-11-4-improvements.md b/change_notes/2023-07-28-rule-11-4-improvements.md new file mode 100644 index 0000000000..3c385359a8 --- /dev/null +++ b/change_notes/2023-07-28-rule-11-4-improvements.md @@ -0,0 +1,12 @@ + - `RULE-11-1` - `ConversionBetweenFunctionPointerAndOtherType.ql`: + - Fixed issue #331 - consider `0` a null pointer constant. + - `RULE-11-4` - `ConversionBetweenPointerToObjectAndIntegerType.ql`: + - Fixed issue #331 - consider `0` a null pointer constant. + - Improve reporting of the order of the cast and the actual types involved. + - Improve reporting where the result is expanded from a macro by either reporting the macro itself (if it is not dependent on the context) or by including a link to the macro in the alert message. + - `RULE-11-5` - `ConversionFromPointerToVoidIntoPointerToObject.ql`: + - Fixed issue #331 - consider `0` a null pointer constant. + - `RULE-11-6` - `CastBetweenPointerToVoidAndArithmeticType.ql`: + - Fixed issue #331 - accept integer constant expressions with value `0` instead of null pointer constants. + - `RULE-11-9` - `MacroNullNotUsedAsIntegerNullPointerConstant.ql`: + - Remove false positives in branches of ternary expressions, where `0` was used correctly. \ No newline at end of file diff --git a/change_notes/2023-07-30-update-to-2.11.6.md b/change_notes/2023-07-30-update-to-2.11.6.md new file mode 100644 index 0000000000..57664a7ac0 --- /dev/null +++ b/change_notes/2023-07-30-update-to-2.11.6.md @@ -0,0 +1 @@ + - Updated the supported CodeQL version to `2.11.6`. \ No newline at end of file diff --git a/change_notes/2023-07-5-fix-suppression-ids.md b/change_notes/2023-07-5-fix-suppression-ids.md new file mode 100644 index 0000000000..44de5dd607 --- /dev/null +++ b/change_notes/2023-07-5-fix-suppression-ids.md @@ -0,0 +1,6 @@ + * A number of rules had the wrong query ids attached for deviation purposes. This means they could not be deviated against using the correct ID, but could be incidentally suppressed when deviating a different rule. We have fixed this behavior for the following rules: + - `RULE-11-4` + - `DIR-4-12` + - `RULE-21-6` + - `RULE-21-9` + - `MEM51-CPP` \ No newline at end of file diff --git a/change_notes/2023-08-02-a8-4-13-false-positives.md b/change_notes/2023-08-02-a8-4-13-false-positives.md new file mode 100644 index 0000000000..1effc84cb7 --- /dev/null +++ b/change_notes/2023-08-02-a8-4-13-false-positives.md @@ -0,0 +1,2 @@ + - `A8-4-13` + - Address false positives caused by missing modelling of modifying operations for smart pointers for some standard libraries (such as libstdc++). \ No newline at end of file diff --git a/change_notes/2023-08-02-smart-pointers.md b/change_notes/2023-08-02-smart-pointers.md new file mode 100644 index 0000000000..f26d1ca27c --- /dev/null +++ b/change_notes/2023-08-02-smart-pointers.md @@ -0,0 +1,5 @@ + - `A20-8-1`/`MEM56-CPP` + - Address false negatives caused by lack of modelling of flow through smart pointers. + - Reduce flow paths through standard library headers to simplify results. + - `A18-1-4` + - Address false positives caused by missing modelling of modifying operations for smart pointers for some standard libraries (such as libstdc++). \ No newline at end of file diff --git a/change_notes/2023-08-03-string-replace.md b/change_notes/2023-08-03-string-replace.md new file mode 100644 index 0000000000..58e5800003 --- /dev/null +++ b/change_notes/2023-08-03-string-replace.md @@ -0,0 +1,2 @@ + - `STR51-CPP` + - Address false negatives caused by incomplete modelling of the `std::string::replace()` function. \ No newline at end of file diff --git a/change_notes/2023-08-04-a15-5-1-noexcept.md b/change_notes/2023-08-04-a15-5-1-noexcept.md new file mode 100644 index 0000000000..3f8c43c64d --- /dev/null +++ b/change_notes/2023-08-04-a15-5-1-noexcept.md @@ -0,0 +1,3 @@ + - `A15-5-1` + - Rephrase alert message for `noalert(false)` special functions to clarify that this permits exceptions. + - Additional results for implicit `noexcept(true)` special functions highlighting that the specification should be made explicit. diff --git a/change_notes/2023-08-16-update-to-2.12.7.md b/change_notes/2023-08-16-update-to-2.12.7.md new file mode 100644 index 0000000000..b56574638a --- /dev/null +++ b/change_notes/2023-08-16-update-to-2.12.7.md @@ -0,0 +1 @@ + - Updated the supported CodeQL version to `2.12.7`. \ No newline at end of file diff --git a/change_notes/2023-08-30-a15-2-2-no-zero-paths.md b/change_notes/2023-08-30-a15-2-2-no-zero-paths.md new file mode 100644 index 0000000000..aef91c3d3b --- /dev/null +++ b/change_notes/2023-08-30-a15-2-2-no-zero-paths.md @@ -0,0 +1 @@ + - `A15-2-2` - all results now include an associated exception flow path to avoid a CodeQL CLI bug in 2.12.7. This includes results where an exception is thrown directly in the constructor. \ No newline at end of file diff --git a/change_notes/2023-09-28-a7-3-1-updates.md b/change_notes/2023-09-28-a7-3-1-updates.md new file mode 100644 index 0000000000..f56d706e74 --- /dev/null +++ b/change_notes/2023-09-28-a7-3-1-updates.md @@ -0,0 +1,7 @@ + * `A7-3-1` - `HiddenInheritedNonOverridableMemberFunction.ql`: + - Reduce duplication by reporting only a single location for each declaration of a problematic element. + - Reduce duplication when reporting the hidden function by reporting only one declaration entry. + - Improve performance by eliminating a number of bad join orders. + - Fix false positives where the using declaration occurred after the function declaration. + - Exclude special member functions, which cannot be inherited. + - Exclude private member functions, which cannot be inherited. \ No newline at end of file diff --git a/change_notes/2023-10-04-aggregate-literals-from-variadic-templates.md b/change_notes/2023-10-04-aggregate-literals-from-variadic-templates.md new file mode 100644 index 0000000000..7d5c36df93 --- /dev/null +++ b/change_notes/2023-10-04-aggregate-literals-from-variadic-templates.md @@ -0,0 +1,2 @@ + `M8-5-2` - `AggregateLiteralEnhancements.qll`: + - recognise aggregate literals initialized with parameters from variadic templates. \ No newline at end of file diff --git a/change_notes/2023-10-04-m5-0-20-exclude-pointer-bitwise.md b/change_notes/2023-10-04-m5-0-20-exclude-pointer-bitwise.md new file mode 100644 index 0000000000..dc8d47f064 --- /dev/null +++ b/change_notes/2023-10-04-m5-0-20-exclude-pointer-bitwise.md @@ -0,0 +1 @@ + * `M5-0-20`, `M5-0-21`, `RULE-10-1` - exclude pointer assignment operators as bitwise operators. \ No newline at end of file diff --git a/change_notes/2023-10-10-add-certification-kit.md b/change_notes/2023-10-10-add-certification-kit.md new file mode 100644 index 0000000000..d143eaa61b --- /dev/null +++ b/change_notes/2023-10-10-add-certification-kit.md @@ -0,0 +1 @@ +- The release artifacts now include a certification kit used for ISO26262 certification. \ No newline at end of file diff --git a/change_notes/2023-10-24-a7-1-5-non-fundamental.md b/change_notes/2023-10-24-a7-1-5-non-fundamental.md new file mode 100644 index 0000000000..f0c9802f5c --- /dev/null +++ b/change_notes/2023-10-24-a7-1-5-non-fundamental.md @@ -0,0 +1,8 @@ + * `A7-1-5` - exclude auto variables initialized with an expression of non-fundamental type. Typically this occurs when using range based for loops with arrays of non-fundamental types. For example: + ``` + void iterate(Foo values[]) { + for (auto value : values) { // COMPLIANT (previously false positive) + // ... + } + } + ``` \ No newline at end of file diff --git a/change_notes/2023-10-25-a0-1-1.md b/change_notes/2023-10-25-a0-1-1.md new file mode 100644 index 0000000000..bd85f52143 --- /dev/null +++ b/change_notes/2023-10-25-a0-1-1.md @@ -0,0 +1,4 @@ + * `A0-1-1` - address a number of false positive issues: + * Exclude compiler-generated variables, such as those generated for range-based for loops. + * Exclude variables in uninstantiated templates, for which we have no precise data on uses. + * Deviations should now be applied to the useless assignment instead of the variable itself. diff --git a/change_notes/2023-10-26-a15-4-4-noexcept.md b/change_notes/2023-10-26-a15-4-4-noexcept.md new file mode 100644 index 0000000000..e778264cc3 --- /dev/null +++ b/change_notes/2023-10-26-a15-4-4-noexcept.md @@ -0,0 +1 @@ + * `A15-4-4`: remove false positives reported on uninsantiated templates. \ No newline at end of file diff --git a/change_notes/2023-11-03-identifier-hiding-improvements.md b/change_notes/2023-11-03-identifier-hiding-improvements.md new file mode 100644 index 0000000000..e1e9f8b85f --- /dev/null +++ b/change_notes/2023-11-03-identifier-hiding-improvements.md @@ -0,0 +1,3 @@ + - `A2-10-1`, `RULE-5-3`: + - Reduce false positives by considering point of declaration for local variables. + - Reduce false negatives by considering catch block parameters to be in scope in the catch block. \ No newline at end of file diff --git a/change_notes/2023-11-05-m6-5-5-const-refs.md b/change_notes/2023-11-05-m6-5-5-const-refs.md new file mode 100644 index 0000000000..4069bbfaa8 --- /dev/null +++ b/change_notes/2023-11-05-m6-5-5-const-refs.md @@ -0,0 +1,3 @@ + - `M6-5-5`: + - Reduce false positives by no longer considering the taking of a const reference as a modification. + - Improve detection of non-local modification of loop iteration variables to reduce false positives. \ No newline at end of file diff --git a/change_notes/2023-11-07-use-misra-underlying-type.md b/change_notes/2023-11-07-use-misra-underlying-type.md new file mode 100644 index 0000000000..e322e8c2dd --- /dev/null +++ b/change_notes/2023-11-07-use-misra-underlying-type.md @@ -0,0 +1,2 @@ +- `M5-0-20` - `BitwiseOperatorOperandsHaveDifferentUnderlyingType.ql`: + - Use the Misra definition of underlying type. diff --git a/change_notes/2023-11-22-remove-parameters-a7-1-1.md b/change_notes/2023-11-22-remove-parameters-a7-1-1.md new file mode 100644 index 0000000000..415e2ba332 --- /dev/null +++ b/change_notes/2023-11-22-remove-parameters-a7-1-1.md @@ -0,0 +1 @@ + * `A7-1-1` - no longer report parameters as contravening this rule. This is inline with the rule intent as described in the referenced C++ Core Guidelines rule [CON.1](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#con1-by-default-make-objects-immutable), which states "To avoid confusion and lots of false positives, don’t enforce this rule for function parameters." \ No newline at end of file diff --git a/change_notes/2023-11-24-a2-7-3-remove-function-scope.md b/change_notes/2023-11-24-a2-7-3-remove-function-scope.md new file mode 100644 index 0000000000..cfd50f8ab8 --- /dev/null +++ b/change_notes/2023-11-24-a2-7-3-remove-function-scope.md @@ -0,0 +1,3 @@ + * `A2-7-3` - `UndocumentedUserDefinedType.ql`: + - Excluding declarations in function scope. The rationale is that these declarations are not exposed outside the scope of the function. + \ No newline at end of file diff --git a/change_notes/2023-12-05-a7-2-1-typo.md b/change_notes/2023-12-05-a7-2-1-typo.md new file mode 100644 index 0000000000..f87fc7cf8b --- /dev/null +++ b/change_notes/2023-12-05-a7-2-1-typo.md @@ -0,0 +1 @@ + * `A7-2-1` - fix typo in some alert messages. \ No newline at end of file diff --git a/change_notes/2023-12-06-m16-1-1-perf.md b/change_notes/2023-12-06-m16-1-1-perf.md new file mode 100644 index 0000000000..9603f84e4f --- /dev/null +++ b/change_notes/2023-12-06-m16-1-1-perf.md @@ -0,0 +1,4 @@ + - `M16-1-1` - `DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql`: + - Optimize query to improve performance + - Improve detection of macros whose body contains the `defined` operator after the start of the macro (e.g. `#define X Y || defined(Z)`). + - Enable exclusions to be applied for this rule. \ No newline at end of file diff --git a/change_notes/2023-12-07-fix-deviations.md b/change_notes/2023-12-07-fix-deviations.md new file mode 100644 index 0000000000..a0f1b1fcfb --- /dev/null +++ b/change_notes/2023-12-07-fix-deviations.md @@ -0,0 +1,2 @@ + - The following queries have been updated to address issues with applying deviations: + - `A18-5-11`, `A23-0-1`, `A9-3-1`, `M0-1-2`, `M3-1-2`, `M3-2-1`, `M3-2-3`, `M3-9-1`, `M4-5-3`, `M5-0-2`, `M5-2-10`, `A23-0-2`, `CTR51-CPP`, `STR52-CPP` \ No newline at end of file diff --git a/change_notes/2024-01-12-fix-reported-fp-a3-9-1.md b/change_notes/2024-01-12-fix-reported-fp-a3-9-1.md new file mode 100644 index 0000000000..fa9d9dbe74 --- /dev/null +++ b/change_notes/2024-01-12-fix-reported-fp-a3-9-1.md @@ -0,0 +1,5 @@ +- `A3-9-1` - `VariableWidthIntegerTypesUsed.ql`: + - Exclude the plain char type. Still includes `signed char` and `unsigned char`. + - Include CV-qualified variable width integer types. +- `A3-9-1` - `VariableWidthPlainCharTypeUsed.ql`: + - New query to support fine grained deviation support for the plain char type. \ No newline at end of file diff --git a/change_notes/2024-01-14-m5-3-3-exclude-binary.md b/change_notes/2024-01-14-m5-3-3-exclude-binary.md new file mode 100644 index 0000000000..f6bf94ef2c --- /dev/null +++ b/change_notes/2024-01-14-m5-3-3-exclude-binary.md @@ -0,0 +1,2 @@ +`M5-3-3` - `UnaryOperatorOverloaded.ql`: + - Exclude binary user defined `operator&` from this rule. \ No newline at end of file diff --git a/change_notes/2024-01-16-m5-2-10-arith-only.md b/change_notes/2024-01-16-m5-2-10-arith-only.md new file mode 100644 index 0000000000..4f92512b55 --- /dev/null +++ b/change_notes/2024-01-16-m5-2-10-arith-only.md @@ -0,0 +1,2 @@ + `M5-2-10` - `IncrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression.ql`: + - only report use of the increment and decrement operations in conjunction with arithmetic operators, as specified by the rule. Notably we no longer report the expressions of the form `*p++`, which combine increment and dereferencing operations. \ No newline at end of file diff --git a/change_notes/2024-01-17-a4-7-1-exclude-pointers.md b/change_notes/2024-01-17-a4-7-1-exclude-pointers.md new file mode 100644 index 0000000000..325149b219 --- /dev/null +++ b/change_notes/2024-01-17-a4-7-1-exclude-pointers.md @@ -0,0 +1 @@ + * `A4-7-1` - exclude pointer increment and decrement operators from this rule. \ No newline at end of file diff --git a/change_notes/2024-01-17-fix-reported-fp-for-a2-3-1.md b/change_notes/2024-01-17-fix-reported-fp-for-a2-3-1.md new file mode 100644 index 0000000000..0ac0580506 --- /dev/null +++ b/change_notes/2024-01-17-fix-reported-fp-for-a2-3-1.md @@ -0,0 +1,2 @@ +`A2-3-1`: ` cpp/autosar/invalid-character-in-string-literal` + - Fixes #311. Exclude wide string literals and utf8 string literal. \ No newline at end of file diff --git a/change_notes/2024-01-18-fix-reported-fp-for-rule-7-3.md b/change_notes/2024-01-18-fix-reported-fp-for-rule-7-3.md new file mode 100644 index 0000000000..dea57f1be4 --- /dev/null +++ b/change_notes/2024-01-18-fix-reported-fp-for-rule-7-3.md @@ -0,0 +1,2 @@ +`RULE-7-3`: `c/misra/lowercase-character-l-used-in-literal-suffix` + - Exclude non integer literals. This removes a false positive triggered when analyzing C++ code containing the `false` literal. \ No newline at end of file diff --git a/change_notes/2024-01-24-throwing-functions-exclude-noexcept.md b/change_notes/2024-01-24-throwing-functions-exclude-noexcept.md new file mode 100644 index 0000000000..4752123832 --- /dev/null +++ b/change_notes/2024-01-24-throwing-functions-exclude-noexcept.md @@ -0,0 +1,6 @@ + * Exceptions are no longer propagated from calls to `noexcept` functions, or calls functions with dynamic exception specifications where the exception is not permitted. This is consistent with the default behaviour specified in `[expect.spec]` which indicates that `std::terminate` is called. This has the following impact: + - `A15-4-2`, `ERR55-CPP` - reduce false positives for `noexcept` functions which call other `noexcept` function which may throw. + - `A15-2-2` - reduce false positives for constructors which call `noexcept` functions. + - `A15-4-5` - reduce false positives for checked exceptions that are thrown from `noexcept` functions called by the original function. + - `DCL57-CPP` - do not report exceptions thrown from `noexcept` functions called by deallocation functions or destructors. + - `A15-5-1`, `M15-3-1` - do not report exceptions thrown from `noexcept` functions called by special functions. \ No newline at end of file diff --git a/change_notes/2024-01-25-exclusion-m9-3-3.md b/change_notes/2024-01-25-exclusion-m9-3-3.md new file mode 100644 index 0000000000..cb16180172 --- /dev/null +++ b/change_notes/2024-01-25-exclusion-m9-3-3.md @@ -0,0 +1,2 @@ +`M9-3-3` - `MemberFunctionConstIfPossible.ql`, `MemberFunctionStaticIfPossible.ql`: + - Fixes #413. Exclude deleted member functions. \ No newline at end of file diff --git a/change_notes/2024-01-25-fix-reported-fp-for-a8-4-7.md b/change_notes/2024-01-25-fix-reported-fp-for-a8-4-7.md new file mode 100644 index 0000000000..34c4343d1b --- /dev/null +++ b/change_notes/2024-01-25-fix-reported-fp-for-a8-4-7.md @@ -0,0 +1,3 @@ +`A8-4-7` - `InParametersForCheapToCopyTypesNotPassedByValue.ql`, `InParametersForNotCheapToCopyTypesNotPassedByReference.ql`: + - Fixes #397. Exclude user defined operators and move constructors.` + - Exclude parameters for instantiated templates because the declaration location of the function does not contain enough information about the type used in the instantiation to make an actionable alert. \ No newline at end of file diff --git a/change_notes/2024-01-26-exclusion-a5-0-2.md b/change_notes/2024-01-26-exclusion-a5-0-2.md new file mode 100644 index 0000000000..33d8113774 --- /dev/null +++ b/change_notes/2024-01-26-exclusion-a5-0-2.md @@ -0,0 +1,2 @@ +`A5-0-2` - `NonBooleanIfStmt.qll`, `NonBooleanIterationStmt.qll`: + - Exclude compiler generated conditions. \ No newline at end of file diff --git a/change_notes/2024-01-30-exclusion-a13-3-1.md b/change_notes/2024-01-30-exclusion-a13-3-1.md new file mode 100644 index 0000000000..7033fb040e --- /dev/null +++ b/change_notes/2024-01-30-exclusion-a13-3-1.md @@ -0,0 +1,2 @@ +`A13-3-1` - `FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql`: + - Fixes #399. Exclude functions that have different number of parameters. \ No newline at end of file diff --git a/change_notes/2024-01-30-fix-fp-for-a4-7-1.md b/change_notes/2024-01-30-fix-fp-for-a4-7-1.md new file mode 100644 index 0000000000..2c4a3d7d19 --- /dev/null +++ b/change_notes/2024-01-30-fix-fp-for-a4-7-1.md @@ -0,0 +1,2 @@ +`A4-7-1`: `IntegerExpressionLeadToDataLoss.ql` + - Fix #368: Incorrectly reporting `/=` as a cause for data loss. diff --git a/change_notes/2024-01-30-fix-fp-for-a8-4-8.md b/change_notes/2024-01-30-fix-fp-for-a8-4-8.md new file mode 100644 index 0000000000..a71f810b24 --- /dev/null +++ b/change_notes/2024-01-30-fix-fp-for-a8-4-8.md @@ -0,0 +1,7 @@ +- `A8-4-8` - `OutParametersUsed.ql` + - Fixes #370 - Non-member user-defined assignment operator and stream insertion/extraction parameters that are required to be out parameters are excluded. + - Broadens the definition of out parameter by considering assignment and crement operators as modifications to an out parameter candidate. +- `FIO51-CPP` - `CloseFilesWhenTheyAreNoLongerNeeded.ql`: + - Broadened definition of `IStream` and `OStream` types may result in reduced false negatives. +- `A5-1-1` - `LiteralValueUsedOutsideTypeInit.ql`: + - Broadened definition of `IStream` types may result in reduced false positives because more file stream function calls may be detected as logging operations that will be excluded from the results. diff --git a/change_notes/2024-01-30-m0-3-2.md b/change_notes/2024-01-30-m0-3-2.md new file mode 100644 index 0000000000..b074f6b2b1 --- /dev/null +++ b/change_notes/2024-01-30-m0-3-2.md @@ -0,0 +1 @@ + * `M0-3-2` - the alert messages now include the name of the called function. \ No newline at end of file diff --git a/change_notes/2024-01-31-exclusion-a16-0-1.md b/change_notes/2024-01-31-exclusion-a16-0-1.md new file mode 100644 index 0000000000..8ff06ba32d --- /dev/null +++ b/change_notes/2024-01-31-exclusion-a16-0-1.md @@ -0,0 +1,2 @@ +`A16-0-1` - `PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.ql`: + - Exclude all preprocessor elses and also consider elifs separately (ie do not affect valid ifs) but not valid if not meeting the same criteria as an ifdef etc. \ No newline at end of file diff --git a/change_notes/2024-01-31-fix-fp-a4-5-1.md b/change_notes/2024-01-31-fix-fp-a4-5-1.md new file mode 100644 index 0000000000..89caded89c --- /dev/null +++ b/change_notes/2024-01-31-fix-fp-a4-5-1.md @@ -0,0 +1,4 @@ +`A4-5-1`: `EnumUsedInArithmeticContexts.ql`: + - Address incorrect exclusion of the binary operator `&`. + - Address incorrect inclusion of the unary operator `&`. + - Fix FP reported in #366. \ No newline at end of file diff --git a/change_notes/2024-01-31-fix-fp-a7-1-2.md b/change_notes/2024-01-31-fix-fp-a7-1-2.md new file mode 100644 index 0000000000..94a74d463f --- /dev/null +++ b/change_notes/2024-01-31-fix-fp-a7-1-2.md @@ -0,0 +1,2 @@ +`A7-1-2` - `VariableMissingConstexpr.ql`: + - Fix FP reported in #466. Addresses incorrect assumption that calls to `constexpr` functions are always compile-time evaluated. \ No newline at end of file diff --git a/change_notes/2024-01-31-fix-fp-m9-3-3.md b/change_notes/2024-01-31-fix-fp-m9-3-3.md new file mode 100644 index 0000000000..4294871638 --- /dev/null +++ b/change_notes/2024-01-31-fix-fp-m9-3-3.md @@ -0,0 +1,2 @@ +`M9-3-3`: `MemberFunctionConstIfPossible.ql`: + - Fix FP reported in 467. Excluding candidates in uninstantiated templates. \ No newline at end of file diff --git a/change_notes/2024-02-01-fix-fp-reported-for-a7-1-1.md b/change_notes/2024-02-01-fix-fp-reported-for-a7-1-1.md new file mode 100644 index 0000000000..346d7a0182 --- /dev/null +++ b/change_notes/2024-02-01-fix-fp-reported-for-a7-1-1.md @@ -0,0 +1,2 @@ +`A7-1-1` - `DeclarationUnmodifiedObjectMissingConstSpecifier.ql` + - Fix FP reported in #372. Exclude compiler generated variables. \ No newline at end of file diff --git a/change_notes/2024-02-12-exclusion-A2-10-4.md b/change_notes/2024-02-12-exclusion-A2-10-4.md new file mode 100644 index 0000000000..ccaf302dd1 --- /dev/null +++ b/change_notes/2024-02-12-exclusion-A2-10-4.md @@ -0,0 +1,2 @@ +- `A2-10-4` - `IdentifierNameOfStaticNonMemberObjectReusedInNamespace.ql`: + - Fix FP reported in #385. Addresses incorrect detection of partially specialized template variables as conflicting reuses. \ No newline at end of file diff --git a/change_notes/2024-02-12-improve-a18-0-1.md b/change_notes/2024-02-12-improve-a18-0-1.md new file mode 100644 index 0000000000..a4a9613a45 --- /dev/null +++ b/change_notes/2024-02-12-improve-a18-0-1.md @@ -0,0 +1,2 @@ +- `A18-0-1` - `CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql`: + - Fix issue #7 - improve query logic to only match on exact standard library names (e.g., now excludes sys/header.h type headers from the results as those are not C standard libraries). \ No newline at end of file diff --git a/change_notes/2024-02-13-fix-fn-M7-3-6.md b/change_notes/2024-02-13-fix-fn-M7-3-6.md new file mode 100644 index 0000000000..aa86ab6222 --- /dev/null +++ b/change_notes/2024-02-13-fix-fn-M7-3-6.md @@ -0,0 +1,2 @@ +- `M7-3-6` - `UsingDeclarationsUsedInHeaderFiles.ql`: + - Address FN reported in #400. Only using-declarations are exempted from class- and function-scope. \ No newline at end of file diff --git a/change_notes/2024-02-13-fix-fp-a15-4-4.md b/change_notes/2024-02-13-fix-fp-a15-4-4.md new file mode 100644 index 0000000000..1afb29fd6a --- /dev/null +++ b/change_notes/2024-02-13-fix-fp-a15-4-4.md @@ -0,0 +1,2 @@ +-`A15-4-4` - `MissingNoExcept.ql`: + - Fix FP reported in #424. Exclude functions calling `std::string::reserve` or `std::string::append` that may throw even if their signatures don't specify it. \ No newline at end of file diff --git a/change_notes/2024-02-14-fix-fp-m0-1-4.md b/change_notes/2024-02-14-fix-fp-m0-1-4.md new file mode 100644 index 0000000000..43aa9f5723 --- /dev/null +++ b/change_notes/2024-02-14-fix-fp-m0-1-4.md @@ -0,0 +1,4 @@ +- `M0-1-4` - `SingleUseMemberPODVariable.ql`: + - Address FP reported in #388. Include aggregrate initialization as a use of a member. + - Include indirect initialization of members. For example, casting a pointer to a buffer to a struct pointer. + - Reformat the alert message to adhere to the style-guide. \ No newline at end of file diff --git a/change_notes/2024-02-15-fix-fp-m0-1-3.md b/change_notes/2024-02-15-fix-fp-m0-1-3.md new file mode 100644 index 0000000000..e84f9fb6db --- /dev/null +++ b/change_notes/2024-02-15-fix-fp-m0-1-3.md @@ -0,0 +1,4 @@ +- `M0-1-3` - `UnusedMemberVariable.ql`, `UnusedGlobalOrNamespaceVariable.ql`: + - Address FP reported in #384. Exclude variables with compile time values that may have been used as a template argument. + - Exclude uninstantiated template members. + - Reformat the alert message to adhere to the style-guide. diff --git a/change_notes/2024-02-16-fix-fps-a5-1-1.md b/change_notes/2024-02-16-fix-fps-a5-1-1.md new file mode 100644 index 0000000000..11831f70ea --- /dev/null +++ b/change_notes/2024-02-16-fix-fps-a5-1-1.md @@ -0,0 +1,7 @@ +- `A5-1-1` - `LiteralValueUsedOutsideTypeInit.ql`: + - Address FP reported in #371. Exclude literals generated by uses of constexpr variables. + - Exclude literals used in class template instantiations. + - Update the alert message to adhere to the style-guide. + - Exclude boolean literals used as template arguments. + - Exclude `u` and `U` prefixed char literals. + - Exclude literals part of a class aggregate literal. diff --git a/change_notes/2024-02-21-fix-reported-fp-a4-7-1.md b/change_notes/2024-02-21-fix-reported-fp-a4-7-1.md new file mode 100644 index 0000000000..246d0481f2 --- /dev/null +++ b/change_notes/2024-02-21-fix-reported-fp-a4-7-1.md @@ -0,0 +1,4 @@ +- `A4-7-1` - `IntegerExpressionLeadToDataLoss.ql`: + - Address reported FP in #396. Exclude shift operations guarded to prevent undefined behavior that could lead to dataloss. +- `INT34-C` - `ExprShiftedbyNegativeOrGreaterPrecisionOperand.ql`: + - Format the alert message according to the style-guide. \ No newline at end of file diff --git a/change_notes/2024-02-22-fix-fp-a5-0-2-and-change-alert-m5-3-1.md b/change_notes/2024-02-22-fix-fp-a5-0-2-and-change-alert-m5-3-1.md new file mode 100644 index 0000000000..86d4ce46ba --- /dev/null +++ b/change_notes/2024-02-22-fix-fp-a5-0-2-and-change-alert-m5-3-1.md @@ -0,0 +1,4 @@ +- `A5-0-2` - `NonBooleanIterationCondition.ql`: + - Address FP reported in #10. Exclude conditions in uninstantiated templates. +- `M5-3-1` - `EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql`: + - Adjust the alert message to comply with the style guide. \ No newline at end of file diff --git a/change_notes/2024-02-26-exclusion-M5-14-1.md b/change_notes/2024-02-26-exclusion-M5-14-1.md new file mode 100644 index 0000000000..e60ccd0319 --- /dev/null +++ b/change_notes/2024-02-26-exclusion-M5-14-1.md @@ -0,0 +1,2 @@ +- `M5-14-1` - `RightHandOperandOfALogicalAndOperatorsContainSideEffects.ql`: + - Fix FP reported in #375. Addresses incorrect detection of side effects in unevaluated contexts. \ No newline at end of file diff --git a/change_notes/2024-02-27-identifier-hidden.md b/change_notes/2024-02-27-identifier-hidden.md new file mode 100644 index 0000000000..d43e1ad9e6 --- /dev/null +++ b/change_notes/2024-02-27-identifier-hidden.md @@ -0,0 +1,3 @@ +- `A2-10-1`, `RULE-5-3` - `IdentifierHiding.ql`, `IdentifierHidingC.ql`: + - Address FN reported in #118. Rule was missing detection of functions. Additionally omitted class template instantiations. + - Fix FP for identifiers in nested namespaces. \ No newline at end of file diff --git a/change_notes/2024-03-01-fix-fp-a12-4-1-and-a12-8-6.md b/change_notes/2024-03-01-fix-fp-a12-4-1-and-a12-8-6.md new file mode 100644 index 0000000000..7ba99b44f1 --- /dev/null +++ b/change_notes/2024-03-01-fix-fp-a12-4-1-and-a12-8-6.md @@ -0,0 +1,6 @@ +- `A12-4-1` - `DestructorOfABaseClassNotPublicVirtual.ql`: + - Fix FP reported in #392. Improve base class detection for template classes. + - Update the alert message to prevent duplicate alerts for base classes that are both derived and abstract. +- `A12-8-6` - `CopyAndMoveNotDeclaredProtected.ql`: + - Fix FP reported in #392. Improve base class detection for template classes. + - Update the alert message to prevent duplicate alerts for base classes that are both derived and abstract. diff --git a/change_notes/2024-03-04-fix-fp-a16-2-2.md b/change_notes/2024-03-04-fix-fp-a16-2-2.md new file mode 100644 index 0000000000..79ff54eea1 --- /dev/null +++ b/change_notes/2024-03-04-fix-fp-a16-2-2.md @@ -0,0 +1,2 @@ +- `A16-2-2` - `UnusedIncludeDirectives.ql`: + - Address FP reported in #453. Exclude reporting of redundant include directives indirectly included by included files. diff --git a/change_notes/2024-03-04-improve-a8-4-7.md b/change_notes/2024-03-04-improve-a8-4-7.md new file mode 100644 index 0000000000..4b0f15106b --- /dev/null +++ b/change_notes/2024-03-04-improve-a8-4-7.md @@ -0,0 +1,3 @@ +- `A8-4-7` - `InParametersForNotCheapToCopyTypesNotPassedByReference.ql`, `InParametersForCheapToCopyTypesNotPassedByValue.ql`: + - Improve coverage of the query by additionally alerting to non-trivially-copyable types being passed by value. + - Non-trivially-copyable types not passed by value will no longer be incorrectly reported. diff --git a/change_notes/2024-03-19-change-alert-a3-3-1.md b/change_notes/2024-03-19-change-alert-a3-3-1.md new file mode 100644 index 0000000000..f0cceccfed --- /dev/null +++ b/change_notes/2024-03-19-change-alert-a3-3-1.md @@ -0,0 +1,2 @@ +- `A3-3-1` - `ExternalLinkageNotDeclaredInHeaderFile.ql`: + - Adjust the alert message to comply with the style guide. \ No newline at end of file diff --git a/change_notes/2024-03-22-fix-fp-89-a8-4-7.md b/change_notes/2024-03-22-fix-fp-89-a8-4-7.md new file mode 100644 index 0000000000..755bdbd2b5 --- /dev/null +++ b/change_notes/2024-03-22-fix-fp-89-a8-4-7.md @@ -0,0 +1,2 @@ +- `A8-4-7` - `InParametersForCheapToCopyTypesNotPassedByValue.ql`, `InParametersForCheapToCopyTypesNotPassedByReference.ql`: + - Fixes #89. Accidental floor rounding was applying to type size calculations. \ No newline at end of file diff --git a/change_notes/2024-03-22-fix-fp-ctr55-cpp.md b/change_notes/2024-03-22-fix-fp-ctr55-cpp.md new file mode 100644 index 0000000000..98e3eb6339 --- /dev/null +++ b/change_notes/2024-03-22-fix-fp-ctr55-cpp.md @@ -0,0 +1,2 @@ +- `CTR55-CPP` - `DoNotUseAnAdditiveOperatorOnAnIterator.ql`: + - Address reported FP in #374. Improve logic on valid end checks and size checks on iterators. \ No newline at end of file diff --git a/change_notes/2024-04-12-fix-fp-m9-3-3.md b/change_notes/2024-04-12-fix-fp-m9-3-3.md new file mode 100644 index 0000000000..bbc9fb9ab0 --- /dev/null +++ b/change_notes/2024-04-12-fix-fp-m9-3-3.md @@ -0,0 +1,2 @@ +`M9-3-3`: `MemberFunctionConstIfPossible.ql`: + - Fix FP reported in 381. Omit member functions that return nonconst reference types. \ No newline at end of file diff --git a/change_notes/2024-04-22-improve-a13-2-2.md b/change_notes/2024-04-22-improve-a13-2-2.md new file mode 100644 index 0000000000..97670cc9fc --- /dev/null +++ b/change_notes/2024-04-22-improve-a13-2-2.md @@ -0,0 +1,2 @@ +- `A13-2-2` - `BinaryOperatorAndBitwiseOperatorReturnAPrvalue.ql`: + - Replaced the usage of getIdentityString() with toString() to avoid expensive computation to display the Operator names which were causing crashes on production code. diff --git a/change_notes/2024-04-23-fix-fp-193.md b/change_notes/2024-04-23-fix-fp-193.md new file mode 100644 index 0000000000..1a89edf190 --- /dev/null +++ b/change_notes/2024-04-23-fix-fp-193.md @@ -0,0 +1,2 @@ +- `A0-4-4`,`FLP32-C` - `UncheckedRangeDomainPoleErrors.ql`: + - Fixes #193. Adds missing cases for domain errors, an unspecified result case and pole error cases. \ No newline at end of file diff --git a/change_notes/2024-04-26-fix-fp-rule-6-1.md b/change_notes/2024-04-26-fix-fp-rule-6-1.md new file mode 100644 index 0000000000..856c15623f --- /dev/null +++ b/change_notes/2024-04-26-fix-fp-rule-6-1.md @@ -0,0 +1,2 @@ +- `RULE-6-1` - `BitFieldsShallOnlyBeDeclaredWithAnAppropriateType.ql`: + - Address FP reported in #318. Add support for implementation specific bitfield types for Clang and Gcc. \ No newline at end of file diff --git a/change_notes/2024-05-06-fix-fn-31-STR32C.md b/change_notes/2024-05-06-fix-fn-31-STR32C.md new file mode 100644 index 0000000000..5abb1f2137 --- /dev/null +++ b/change_notes/2024-05-06-fix-fn-31-STR32C.md @@ -0,0 +1,2 @@ +- `STR32-C` - `NonNullTerminatedToFunctionThatExpectsAString.ql`: + - Fixes #31. Realloc was not modelled previously. \ No newline at end of file diff --git a/change_notes/2024-05-21-identifier-hidden.md b/change_notes/2024-05-21-identifier-hidden.md new file mode 100644 index 0000000000..c09d05bd27 --- /dev/null +++ b/change_notes/2024-05-21-identifier-hidden.md @@ -0,0 +1,2 @@ +- `A2-10-1`, `RULE-5-3` - `IdentifierHiding.ql`, `IdentifierHidingC.ql`: + - Revert some changes previously made in PR #546 (addressing issue #118). Revert expansion to function identifiers. \ No newline at end of file diff --git a/change_notes/2024-05-22-fix-fp-rule-A18-5-8.md b/change_notes/2024-05-22-fix-fp-rule-A18-5-8.md new file mode 100644 index 0000000000..3091ce9ef3 --- /dev/null +++ b/change_notes/2024-05-22-fix-fp-rule-A18-5-8.md @@ -0,0 +1,2 @@ +- `A18-5-8` - `UnnecessaryUseOfDynamicStorage.ql`: + - Address FP reported in #20. Add model of flow from MakeSharedOrUnique to return expression to capture copy/move elision case NRVO. \ No newline at end of file diff --git a/change_notes/2024-06-03-a3-1-5-trivial-defs.md b/change_notes/2024-06-03-a3-1-5-trivial-defs.md new file mode 100644 index 0000000000..29a7f48eb5 --- /dev/null +++ b/change_notes/2024-06-03-a3-1-5-trivial-defs.md @@ -0,0 +1,4 @@ + - `A3-1-5` - `TrivialOrTemplateFunctionDefinedOutsideClassDefinition.ql`: + - Query deleted - rule was never intended to cover this case (see https://forum.misra.org.uk/archive/index.php?thread-1588.html). + - `A3-1-5` - `NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql`: + - Removed false positives caused by flagging member functions in template instantiations diff --git a/change_notes/2024-06-03-constexpr-variable.md b/change_notes/2024-06-03-constexpr-variable.md new file mode 100644 index 0000000000..1da02b5d9f --- /dev/null +++ b/change_notes/2024-06-03-constexpr-variable.md @@ -0,0 +1,2 @@ + - `A7-1-2` - `VariableMissingConstexpr.ql`: + - Fixes #607. Remove false positives for compiler generated variables and in uninstantiated templates \ No newline at end of file diff --git a/change_notes/2024-06-07-m0-1-3-uninstantiated-templates.md b/change_notes/2024-06-07-m0-1-3-uninstantiated-templates.md new file mode 100644 index 0000000000..0dcb7c1c1a --- /dev/null +++ b/change_notes/2024-06-07-m0-1-3-uninstantiated-templates.md @@ -0,0 +1,2 @@ + - `M0-1-3` - `UnusedGlobalOrNamespaceVariable.ql` + - Reduces false positives by excluding compiler generated variables, and variables in uninstantiated templates. \ No newline at end of file diff --git a/change_notes/2024-06-11-fix-fp-376-M-0-1-2.md b/change_notes/2024-06-11-fix-fp-376-M-0-1-2.md new file mode 100644 index 0000000000..49b53f5f37 --- /dev/null +++ b/change_notes/2024-06-11-fix-fp-376-M-0-1-2.md @@ -0,0 +1,2 @@ +- `M0-1-2` - `InfeasiblePath.ql`: + - Fixes #376. For template functions we now only report when a path is infeasible regardless of instantiations present. \ No newline at end of file diff --git a/change_notes/2024-06-18-fix-fp-614-A3-9-1.md b/change_notes/2024-06-18-fix-fp-614-A3-9-1.md new file mode 100644 index 0000000000..121c285b20 --- /dev/null +++ b/change_notes/2024-06-18-fix-fp-614-A3-9-1.md @@ -0,0 +1,2 @@ +- `A3-9-1` - `VariableWidthIntegerTypesUsed.ql`: + - Fixes #614. Excludes post increment and decrement operators. \ No newline at end of file diff --git a/change_notes/2024-06-18-fix-fp-616-M9-3-3.md b/change_notes/2024-06-18-fix-fp-616-M9-3-3.md new file mode 100644 index 0000000000..6c13807ed6 --- /dev/null +++ b/change_notes/2024-06-18-fix-fp-616-M9-3-3.md @@ -0,0 +1,2 @@ +- `M9-3-3` - `MemberFunctionStaticIfPossible.ql`: + - Fixes #616. Exclude uninstantiated templates. \ No newline at end of file diff --git a/change_notes/2024-06-21-misra-cpp-2023-support.md b/change_notes/2024-06-21-misra-cpp-2023-support.md new file mode 100644 index 0000000000..e314d447fa --- /dev/null +++ b/change_notes/2024-06-21-misra-cpp-2023-support.md @@ -0,0 +1,2 @@ +- `MISRA C++ 2023`: + - Extend the project structure and provide initial support for query writing. diff --git a/change_notes/2024-07-03-consider-anonymous-struct.md b/change_notes/2024-07-03-consider-anonymous-struct.md new file mode 100644 index 0000000000..4d7430bc22 --- /dev/null +++ b/change_notes/2024-07-03-consider-anonymous-struct.md @@ -0,0 +1,2 @@ +- `RULE-8-3` - `DeclarationsOfAnObjectSameNameAndType.ql`: + - Fixes #618. Consider anonymous structs. \ No newline at end of file diff --git a/change_notes/2024-07-03-fix-fp-611-A3-1-5.md b/change_notes/2024-07-03-fix-fp-611-A3-1-5.md new file mode 100644 index 0000000000..b4902a1ee3 --- /dev/null +++ b/change_notes/2024-07-03-fix-fp-611-A3-1-5.md @@ -0,0 +1,2 @@ +- `A3-1-5` - `NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql`, `TrivialOrTemplateFunctionDefinedOutsideClassDefinition.ql`: + - Fixes #611. Relax definition of trivial length of trivial member function to 10 LOC. \ No newline at end of file diff --git a/change_notes/2024-07-05-fix-fp-576-STR34-C.md b/change_notes/2024-07-05-fix-fp-576-STR34-C.md new file mode 100644 index 0000000000..340d8f4288 --- /dev/null +++ b/change_notes/2024-07-05-fix-fp-576-STR34-C.md @@ -0,0 +1,2 @@ +- `STR34-C` - `CastCharBeforeConvertingToLargerSizes.ql`: + - Fixes #576. Do not consider integer type aliases in templates. \ No newline at end of file diff --git a/change_notes/2024-07-05-fix-fp-621-A7-1-1.md b/change_notes/2024-07-05-fix-fp-621-A7-1-1.md new file mode 100644 index 0000000000..1295619cbf --- /dev/null +++ b/change_notes/2024-07-05-fix-fp-621-A7-1-1.md @@ -0,0 +1,2 @@ +- `A7-1-1` - `DeclarationUnmodifiedObjectMissingConstSpecifier.ql`: + - Fixes #621. Exclude template instantiations that come from constexpr templates. \ No newline at end of file diff --git a/change_notes/2024-07-05-fix-fp628-630.md b/change_notes/2024-07-05-fix-fp628-630.md new file mode 100644 index 0000000000..8f0517f708 --- /dev/null +++ b/change_notes/2024-07-05-fix-fp628-630.md @@ -0,0 +1,3 @@ +- `A7-1-7` - `IdentifierDeclarationAndInitializationNotOnSeparateLines.ql`: + - Fixes #628. Excludes Functions. + - Fixes #630. Excludes struct identifiers and variables on the same line when a typedef is used. \ No newline at end of file diff --git a/change_notes/2024-07-10-fix-fn-119-m0-2-1.md b/change_notes/2024-07-10-fix-fn-119-m0-2-1.md new file mode 100644 index 0000000000..08d139ddbe --- /dev/null +++ b/change_notes/2024-07-10-fix-fn-119-m0-2-1.md @@ -0,0 +1,2 @@ +- `M0-2-1` - `DoNotPassAliasedPointerToRestrictQualifiedParam.ql`: + - Fixes #119. Adds shared query to cover missing detection of overlapping arrays or pointers in specific list of functions that list undefined behaviour when their parameters overlap. \ No newline at end of file diff --git a/change_notes/2024-07-11-fix-fp-406.md b/change_notes/2024-07-11-fix-fp-406.md new file mode 100644 index 0000000000..78e607ecb6 --- /dev/null +++ b/change_notes/2024-07-11-fix-fp-406.md @@ -0,0 +1,2 @@ + - `A13-3-1` - `FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql`: + - Fixes #406. Exclude detection of overloaded implicit copy/move constructors. \ No newline at end of file diff --git a/change_notes/2024-07-12-support-doxygen-comment-groups.md b/change_notes/2024-07-12-support-doxygen-comment-groups.md new file mode 100644 index 0000000000..b0d7a148ba --- /dev/null +++ b/change_notes/2024-07-12-support-doxygen-comment-groups.md @@ -0,0 +1,2 @@ +- `A2-7-3` - `UndocumentedUserDefinedType.ql`: + - Fixes #391. Declarations for which a Doxygen comment group provides documentation will no longer produce results. \ No newline at end of file diff --git a/change_notes/2024-07-16-fix-fp-606-A2-7-3.md b/change_notes/2024-07-16-fix-fp-606-A2-7-3.md new file mode 100644 index 0000000000..a4fc343b76 --- /dev/null +++ b/change_notes/2024-07-16-fix-fp-606-A2-7-3.md @@ -0,0 +1,2 @@ +- `A2-7-3` - `UndocumentedUserDefinedType.ql`: + - Fixes #606. Fix false positive relating to friend functions in template classes. \ No newline at end of file diff --git a/change_notes/2024-07-23-fix-fp-646-M0-1-10.md b/change_notes/2024-07-23-fix-fp-646-M0-1-10.md new file mode 100644 index 0000000000..8854c7b59a --- /dev/null +++ b/change_notes/2024-07-23-fix-fp-646-M0-1-10.md @@ -0,0 +1,2 @@ +- `M0-1-10` - `EncapsulatingFunctions.qll`: + - Fixes #646. Consider typedef'd `int` return types for `main()` function as MainFunction. diff --git a/change_notes/2024-07-30-fix-fp-650-PRE32-C.md b/change_notes/2024-07-30-fix-fp-650-PRE32-C.md new file mode 100644 index 0000000000..e1ea391499 --- /dev/null +++ b/change_notes/2024-07-30-fix-fp-650-PRE32-C.md @@ -0,0 +1,2 @@ +- `PRE32-C` - `MacroOrFunctionArgsContainHashToken.ql`: + - Fixes #650. Correctly identifies presence of preprocessor directives in function calls. diff --git a/change_notes/2024-08-06-fix-fp-658-M0-1-3.md b/change_notes/2024-08-06-fix-fp-658-M0-1-3.md new file mode 100644 index 0000000000..47a26705ae --- /dev/null +++ b/change_notes/2024-08-06-fix-fp-658-M0-1-3.md @@ -0,0 +1,2 @@ +- `M0-1-3` - `UnusedLocalVariable.ql`: + - Fixes #658. Considers usage of const/constexpr variables in array size and function parameters that are used in arguments of template functions. diff --git a/change_notes/2024-09-11-rule-12-2-improvements.md b/change_notes/2024-09-11-rule-12-2-improvements.md new file mode 100644 index 0000000000..0e713a5088 --- /dev/null +++ b/change_notes/2024-09-11-rule-12-2-improvements.md @@ -0,0 +1,6 @@ +- `RULE-12-2` - `RightHandOperandOfAShiftRange.ql`: + - Reduce false positives related to ranges determined by `%=`. + - Reduce false positives for integer constants with explicit size suffix were incorrectly identified as smaller types. + - Improve explanation of results, providing additional information on types and size ranges. + - Combine results stemming from the expansion of a macro, where the result is not dependent on the context. + \ No newline at end of file diff --git a/change_notes/2024-09-16-rule-5-8-consider-linkage.md b/change_notes/2024-09-16-rule-5-8-consider-linkage.md new file mode 100644 index 0000000000..2877d53f50 --- /dev/null +++ b/change_notes/2024-09-16-rule-5-8-consider-linkage.md @@ -0,0 +1,2 @@ + - `RULE-5-8` - `IdentifiersWithExternalLinkageNotUnique.ql` + - Remove false positives where conflicting declarations do not appear in the same link target. \ No newline at end of file diff --git a/change_notes/2024-09-17-essential-types-unary.md b/change_notes/2024-09-17-essential-types-unary.md new file mode 100644 index 0000000000..401f59a9a6 --- /dev/null +++ b/change_notes/2024-09-17-essential-types-unary.md @@ -0,0 +1,4 @@ + - `RULE-10-1` - `OperandsOfAnInappropriateEssentialType.ql` + - Reduce false negatives by supporting operands to the `~` operator with the incorrect essential type. + - Reduce false positives by identifying the essential type of `!` as essentially boolean type. + - Improve clarity reporting by reporting the violating operand, instead of the operator, and addressing message typos. \ No newline at end of file diff --git a/change_notes/2024-09-17-fix-fp-678-m0-1-9.md b/change_notes/2024-09-17-fix-fp-678-m0-1-9.md new file mode 100644 index 0000000000..e068825f4c --- /dev/null +++ b/change_notes/2024-09-17-fix-fp-678-m0-1-9.md @@ -0,0 +1,2 @@ +- `M0-1-9` - `DeadCode.qll`: + - Fixes #678. Remove dead code false positive when integer constant expression is used to define the size of an array. diff --git a/change_notes/2024-09-17-rule-8-3-linker-aware.md b/change_notes/2024-09-17-rule-8-3-linker-aware.md new file mode 100644 index 0000000000..3e48bb1228 --- /dev/null +++ b/change_notes/2024-09-17-rule-8-3-linker-aware.md @@ -0,0 +1,2 @@ + - `RULE-8-3` - `DeclarationsOfAnObjectSameNameAndType.ql` + - Remove false positives where two conflicting declarations are never linked together. \ No newline at end of file diff --git a/change_notes/2024-09-18-handle-warning-suppresion-flags b/change_notes/2024-09-18-handle-warning-suppresion-flags new file mode 100644 index 0000000000..12bf30e937 --- /dev/null +++ b/change_notes/2024-09-18-handle-warning-suppresion-flags @@ -0,0 +1,2 @@ +- `A1-1-2` - `CompilerWarningLevelNotInCompliance.ql`: + - Fixes #689 false negatives where '-Wno-foo' was treated as enabling, rather than disabling warnings. \ No newline at end of file diff --git a/change_notes/2024-09-19-c-extensions.md b/change_notes/2024-09-19-c-extensions.md new file mode 100644 index 0000000000..2f78574679 --- /dev/null +++ b/change_notes/2024-09-19-c-extensions.md @@ -0,0 +1,4 @@ + - `RULE-1-2` - `LanguageExtensionsShouldNotBeUsed.ql`: + - Improve reporting by describing which language extensions are used. + - Improve reporting by aggregating results generated from a macro expansion at the generating macro location. + - Reduce false positives for the variable length array check by permitting those extensions which are included in the C99 standard. \ No newline at end of file diff --git a/change_notes/2024-09-19-fix-fp-665-M3-4-1.md b/change_notes/2024-09-19-fix-fp-665-M3-4-1.md new file mode 100644 index 0000000000..63c5f91b56 --- /dev/null +++ b/change_notes/2024-09-19-fix-fp-665-M3-4-1.md @@ -0,0 +1,2 @@ +- `M3-4-1` - `UnnecessaryExposedIdentifierDeclarationShared.qll`: + - Fixes #665. Exclude variables that are constexpr and coming from template instantiations. diff --git a/change_notes/2024-09-20-fix-7-2-fps.md b/change_notes/2024-09-20-fix-7-2-fps.md new file mode 100644 index 0000000000..897aebadb7 --- /dev/null +++ b/change_notes/2024-09-20-fix-7-2-fps.md @@ -0,0 +1,3 @@ + - `RULE-7-2` - `UOrUSuffixRepresentedInUnsignedType.ql` + - Remove false positives where integer constants are generated from macros. + - Remove false positives where a signed integer is implicitly converted to unsigned, which is permitted by the standard. \ No newline at end of file diff --git a/change_notes/2024-09-25-dead-code-improvements.md b/change_notes/2024-09-25-dead-code-improvements.md new file mode 100644 index 0000000000..9cd8d95ff5 --- /dev/null +++ b/change_notes/2024-09-25-dead-code-improvements.md @@ -0,0 +1,5 @@ + - `M0-1-9` - `DeadCode.ql` + - Remove false positives for statements where the enclosing function is compiled multiple times, either as part of different targets or a different template instantiations. Previously we would see false positives where a statement was dead in one instance of the code, but not other instances. We now only consider a statement dead if it is dead in all instances of that code. +- `RULE-2-2` - `DeadCode.ql`: + - Query has been rewritten to report only _operations_ that are considered dead, not statements. This should reduce false positives. + - Remove false positives for operations where the enclosing function is compiled multiple times, either as part of different targets or a different template instantiations. Previously we would see false positives where a operation was dead in one instance of the code, but not other instances. We now only consider a operation dead if it is dead in all instances of that code. \ No newline at end of file diff --git a/change_notes/2024-09-28-improved-noreturn-rules.md b/change_notes/2024-09-28-improved-noreturn-rules.md new file mode 100644 index 0000000000..99fb4a0f46 --- /dev/null +++ b/change_notes/2024-09-28-improved-noreturn-rules.md @@ -0,0 +1,3 @@ + - `A7-6-1`, `MSC53-CPP`, `RULE-9-6-4` - `FunctionNoReturnAttbrituteCondition.qll` + - Analysis expanded from functions with "noreturn" attribute, now includes the "noreturn" specifier as well to handle new c rules. No difference in C++ results expected. + - Exclude compiler generated functions from being reported. \ No newline at end of file diff --git a/change_notes/2024-10-02-c-perf-issues.md b/change_notes/2024-10-02-c-perf-issues.md new file mode 100644 index 0000000000..c9fcac1a05 --- /dev/null +++ b/change_notes/2024-10-02-c-perf-issues.md @@ -0,0 +1,4 @@ + - `RULE-10-7` - `ImplicitConversionOfCompositeExpression.ql`: + - Improved performance on larger codebases. + - `SIG31-C` - `DoNotAccessSharedObjectsInSignalHandlers.ql`: + - Improved performance on larger codebases. \ No newline at end of file diff --git a/change_notes/2024-10-02-fix-fp-711-M0-1-10.md b/change_notes/2024-10-02-fix-fp-711-M0-1-10.md new file mode 100644 index 0000000000..cff5d5ab43 --- /dev/null +++ b/change_notes/2024-10-02-fix-fp-711-M0-1-10.md @@ -0,0 +1,2 @@ +- `M0-1-10` - `UnusedFunction.ql`: + - Fixes #711. Excludes constexpr functions, considers functions from GoogleTest as an EntryPoint and does not consider special member functions. Another query called UnusedSplMemberFunction.ql is created that reports unused special member functions. This is done so as to enable deviations to be applied to this case. diff --git a/change_notes/2024-10-03-misra-c-query-suites.md b/change_notes/2024-10-03-misra-c-query-suites.md new file mode 100644 index 0000000000..c60aac8941 --- /dev/null +++ b/change_notes/2024-10-03-misra-c-query-suites.md @@ -0,0 +1,10 @@ + - The following query suites have been added or modified for MISRA C: + - A new query suite has been created `misra-c-default.qls` to avoid confusion with the MISRA C++ query suites. The `misra-default.qls` suite has been deprecated, and will be removed in a future releases, and is replaced by the `misra-c-default.qls` suite. + - The `misra-c-default.qls` suite has been specified as the default for the pack, and will include our most up-to-date coverage for MISRA C. + - A new query suite `misra-c-2012-third-edition-with-amendment-2.qls` has been created to represent our previous MISRA C coverage. Note: this query suite will run the rules that were present in MISRA C 2012, Third Edition, First Revision and Amendment 2. The interpretation of those rules may be updated to reflect changes in more recent MISRA standards. + - Three new query suites, `misra-c-mandatory.qls`, `misra-c-required.qls` and `misra-c-advisory.qls`, have been added to enable running mandatory, required or advisory queries. + - The following query suites have been added or modified for MISRA C++: + - A new query suite has been created `misra-cpp-default.qls` to avoid confusion with the MISRA C query suites. The `misra-default.qls` suite has been deprecated, and will be removed in a future releases, and is replaced by the `misra-cpp-default.qls` suite. + - The `misra-cpp-default.qls` suite has been specified as the default for the pack, and will include our most up-to-date coverage for MISRA C. + - A new query suite has been created `misra-cpp-single-translation-unit.qls` to avoid confusion with the MISRA C query suites. The `misra-single-translation-unit.qls` suite has been deprecated, and will be removed in a future releases, and is replaced by the `misra-cpp-single-translation-unit.qls` suite. + - Three new query suites, `misra-cpp-mandatory.qls`, `misra-c-required.qls` and `misra-c-advisory.qls`, have been added to enable running mandatory, required or advisory queries. \ No newline at end of file diff --git a/change_notes/2024-10-04-fix-constexpr-arr-size-fp-a0-1-1.md b/change_notes/2024-10-04-fix-constexpr-arr-size-fp-a0-1-1.md new file mode 100644 index 0000000000..184efa9462 --- /dev/null +++ b/change_notes/2024-10-04-fix-constexpr-arr-size-fp-a0-1-1.md @@ -0,0 +1,2 @@ +- `A0-1-1` - `UselessAssignments.qll`: + - Remove (dead code) useless assignment false positive when integer constant expression is used to define the size of an array. diff --git a/change_notes/2024-10-08-upgrade-to-2.16.6.md b/change_notes/2024-10-08-upgrade-to-2.16.6.md new file mode 100644 index 0000000000..9f1e11d3d3 --- /dev/null +++ b/change_notes/2024-10-08-upgrade-to-2.16.6.md @@ -0,0 +1,3 @@ +- Updated the CodeQL version to `2.16.6`. +- `M0-1-2` - `InfeasiblePath.ql`: + - This query may now report additional results within templates where a relational operation is performed which has a constant value given the specified arguments. \ No newline at end of file diff --git a/change_notes/2024-10-10-rule-18-8-vla-rule-changes-amendment4.md b/change_notes/2024-10-10-rule-18-8-vla-rule-changes-amendment4.md new file mode 100644 index 0000000000..f465836052 --- /dev/null +++ b/change_notes/2024-10-10-rule-18-8-vla-rule-changes-amendment4.md @@ -0,0 +1,4 @@ +- `RULE-18-8` - `VariableLengthArrayTypesUsed.ql`: + - Implement changes declared in MISRA C 2012 Amendment 4. This rule now only bans the use of VLA objects. Rules restricting the use of VLA types -- specifically, pointers to VLA types -- are now implemented in `RULE-18-10`. +- `EXP-35-C` - `DoNotModifyObjectsWithTemporaryLifetime.ql` + - Refactor component into a shared library, should not have any effect on rule results. \ No newline at end of file diff --git a/change_notes/2024-10-11-specifiers-rule-11-misra-c.md b/change_notes/2024-10-11-specifiers-rule-11-misra-c.md new file mode 100644 index 0000000000..5f74dc6b3f --- /dev/null +++ b/change_notes/2024-10-11-specifiers-rule-11-misra-c.md @@ -0,0 +1,4 @@ +- `RULE-11-3`, `RULE-11-4`, `RULE-11-5`, `RULE-11-7` - `CastBetweenObjectPointerAndDifferentObjectType.ql`, `ConversionBetweenPointerToObjectAndIntegerType.ql`, `ConversionFromPointerToVoidIntoPointerToObject.ql`, `CastBetweenPointerToObjectAndNonIntArithmeticType.ql`: + - Removed false positives where casts involved a specified void type pointer, e.g. `const void*`, which should not be considered as a pointer to object. +- `RULE-11-5` - `ConversionFromPointerToVoidIntoPointerToObject.ql`: + - Addressed false negatives where the pointer-to-void was specified. \ No newline at end of file diff --git a/change_notes/2024-10-15-a7-1-3-multi-refs.md b/change_notes/2024-10-15-a7-1-3-multi-refs.md new file mode 100644 index 0000000000..39e00495cb --- /dev/null +++ b/change_notes/2024-10-15-a7-1-3-multi-refs.md @@ -0,0 +1,2 @@ +- `A7-1-3` - `CvQualifiersNotPlacedOnTheRightHandSide.ql`: + - Removed false positives where a correctly CV-qualified typedef variable type was also referenced in the initializer. \ No newline at end of file diff --git a/change_notes/2024-10-15-fix-fp-739-a14-5-2.md b/change_notes/2024-10-15-fix-fp-739-a14-5-2.md new file mode 100644 index 0000000000..6e3f422718 --- /dev/null +++ b/change_notes/2024-10-15-fix-fp-739-a14-5-2.md @@ -0,0 +1,2 @@ +- `A14-5-2` - `NonTemplateMemberDefinedInTemplate.ql` + - Fixes #739. Correctly detect template parameters specified in using alias base types, e.g. `using T1 = some_type::Type;`. diff --git a/change_notes/2024-10-15-lits-and-constants-10-4.md b/change_notes/2024-10-15-lits-and-constants-10-4.md new file mode 100644 index 0000000000..cfcb309204 --- /dev/null +++ b/change_notes/2024-10-15-lits-and-constants-10-4.md @@ -0,0 +1,5 @@ + - `RULE-10-4` - `OperandswithMismatchedEssentialTypeCategory.ql`: + - Removed false positives where a specified or typedef'd enum type was compared to an enum constant type. + - `EssentialType` - for all queries related to essential types: + - `\n` and other control characters are now correctly deduced as essentially char type, instead of an essentially integer type. + - Enum constants for anonymous enums are now correctly deduced as an essentially signed integer type instead of essentially enum. \ No newline at end of file diff --git a/change_notes/2024-10-17-a5-2-6-no-ambiguity.md b/change_notes/2024-10-17-a5-2-6-no-ambiguity.md new file mode 100644 index 0000000000..6e00b3bbaf --- /dev/null +++ b/change_notes/2024-10-17-a5-2-6-no-ambiguity.md @@ -0,0 +1,3 @@ + - `A5-2-6` - `OperandsOfAlogicalAndOrNotParenthesized.ql`: + - Remove false positives where the operator is identical. + - Improve alert message to clarify which expression needs to be parenthesized. \ No newline at end of file diff --git a/change_notes/2024-10-17-suffixes.md b/change_notes/2024-10-17-suffixes.md new file mode 100644 index 0000000000..16d8ca4cda --- /dev/null +++ b/change_notes/2024-10-17-suffixes.md @@ -0,0 +1,4 @@ + - `5.13.4` - `UnsignedLiteralsNotAppropriatelySuffixed.ql`: + - Expand detection to binary literals. + - `M2-13-3` - `MissingUSuffix.ql`: + - Expand detection to binary literals. \ No newline at end of file diff --git a/change_notes/2024-10-18-init-base-class-deleted.md b/change_notes/2024-10-18-init-base-class-deleted.md new file mode 100644 index 0000000000..992e1e88a2 --- /dev/null +++ b/change_notes/2024-10-18-init-base-class-deleted.md @@ -0,0 +1,2 @@ +- `A12-1-1`, `RULE-15-1-2` - `InitializeAllVirtualBaseClasses.ql`, `ExplicitConstructorBaseClassInitialization.ql`: + - Remove false positives for deleted member functions. \ No newline at end of file diff --git a/change_notes/2024-10-20-8-13-fixes.md b/change_notes/2024-10-20-8-13-fixes.md new file mode 100644 index 0000000000..6ee8e3a32c --- /dev/null +++ b/change_notes/2024-10-20-8-13-fixes.md @@ -0,0 +1,7 @@ + - `RULE-8-13` - `PointerShouldPointToConstTypeWhenPossible.ql` + - Exclude false positives where a variable occurs in a file compiled multiple times, but where it may only be const in some of those scenarios. + - Exclude results for local scope variables in functions that use assembly code, as CodeQL cannot determine the impact of the assembly. + - Exclude false positives when an assignment is made to a struct field. + - Exclude false positives where the object pointed to by the variable is modified using `*p++ = ...`. + - Exclude false positives for functions without bodies. + - Rules that rely on the determination of side-effects of an expression may change as a result of considering `*p++ = ...` as having a side-effect on `p`. \ No newline at end of file diff --git a/change_notes/2024-10-21-rule-1-3-main.md b/change_notes/2024-10-21-rule-1-3-main.md new file mode 100644 index 0000000000..7bd8d4bd54 --- /dev/null +++ b/change_notes/2024-10-21-rule-1-3-main.md @@ -0,0 +1,3 @@ + - `RULE-1-3` - `OccurrenceOfUndefinedBehavior.ql`: + - Improve alert message to report the undefined behavior triggered. + - Address both false positives and false negatives in identifying standard compliant main methods. Previously, `void main()` was considered permitted and `int main(void)` banned. In addition, we now detect main methods as standard compliant if they use typedefs, and if arrays are used in the definition of `argv`. \ No newline at end of file diff --git a/change_notes/2024-10-21-rule-5-4-conditional.md b/change_notes/2024-10-21-rule-5-4-conditional.md new file mode 100644 index 0000000000..cfc22f3642 --- /dev/null +++ b/change_notes/2024-10-21-rule-5-4-conditional.md @@ -0,0 +1,3 @@ + - `RULE-5-4` - `MacroIdentifiersNotDistinct.ql`: + - Exclude false positives related to conditional compilation, where a macro may be defined twice, but not within the same compilation. + - Improve alert message in the case the 63 char limit is not relevant by using the form "Definition of macro `` is not distinct from alternative definition of `` in ``. \ No newline at end of file diff --git a/change_notes/2024-10-22-fix-fp-m6-5-3.md b/change_notes/2024-10-22-fix-fp-m6-5-3.md new file mode 100644 index 0000000000..0d8ca573d9 --- /dev/null +++ b/change_notes/2024-10-22-fix-fp-m6-5-3.md @@ -0,0 +1,2 @@ +- `M6-5-3` - `Loops.qll`: + - Fixes #755. Specifies that the access to the loop counter must be via non-const address. diff --git a/change_notes/2024-10-22-rule-2-5.md b/change_notes/2024-10-22-rule-2-5.md new file mode 100644 index 0000000000..6de3f0be11 --- /dev/null +++ b/change_notes/2024-10-22-rule-2-5.md @@ -0,0 +1,2 @@ + - `RULE-2-5` - `UnusedMacroDeclaration.ql`: + - Exclude false positives where a macro was used before definition, for example a header guard. \ No newline at end of file diff --git a/change_notes/2024-10-22-update-release-artifacts.md b/change_notes/2024-10-22-update-release-artifacts.md new file mode 100644 index 0000000000..46d0ed0c30 --- /dev/null +++ b/change_notes/2024-10-22-update-release-artifacts.md @@ -0,0 +1,4 @@ + - Modifications to the release artifacts: + - New CodeQL pack release artifacts have been created. These release artifacts can be downloaded from the release, and will be published to the GitHub registry under the `codeql` org for ease of deployment. + - The user manual has been updated to describe how to use the CodeQL packs. + - We no longer require a separate download of the CodeQL Standard Library for C++ - all queries have been pre-compiled and linked with the appropriate standard library. \ No newline at end of file diff --git a/change_notes/2024-10-23-cvalue-widening.md b/change_notes/2024-10-23-cvalue-widening.md new file mode 100644 index 0000000000..1d7a0f876a --- /dev/null +++ b/change_notes/2024-10-23-cvalue-widening.md @@ -0,0 +1,2 @@ + - `M5-0-3`, `M5-0-7`, `M5-0-8`, `M5-0-9` - `CvalueExpressionConvertedToDifferentUnderlyingType.ql`, `ExplicitFloatingIntegralConversionOfACValueExpr.ql`, `ExplicitWideningConversionOfACValueExpr.ql`, `ExplicitSignedness.ql`: + - Reduce false positives from misidentifying an explicitly casted expression used as a function argument or return value as a `cvalue`. diff --git a/change_notes/2024-10-24-compatible-types.md b/change_notes/2024-10-24-compatible-types.md new file mode 100644 index 0000000000..05afbd64d9 --- /dev/null +++ b/change_notes/2024-10-24-compatible-types.md @@ -0,0 +1,2 @@ + - `DCL40-C` - `IncompatibleFunctionDeclarations.ql`: + - Reduce false positives by identifying compatible integer arithmetic types (e.g. "signed int" and "int"). \ No newline at end of file diff --git a/change_notes/2024-10-28-essential-types-bitwise.md b/change_notes/2024-10-28-essential-types-bitwise.md new file mode 100644 index 0000000000..a382290351 --- /dev/null +++ b/change_notes/2024-10-28-essential-types-bitwise.md @@ -0,0 +1,2 @@ + - `RULE-10-1`, `RULE-10-3`, `RULE-10-4`, `RULE-10-5`, `RULE-10-6`, `RULE-10-7`, `RULE-10-8`, `RULE-12-2` - `OperandsOfAnInappropriateEssentialType.ql`, `AssignmentOfIncompatibleEssentialType.ql`, `OperandsWithMismatchedEssentialTypeCategory.ql`, `InappropriateEssentialTypeCast.ql`, `AssignmentToWiderEssentialType,ql`, `ImplicitConversionOfCompositeExpression.ql`, `InappropriateCastOfCompositeExpression.ql`: + - False positives and false negatives removed due to fixing incorrect essential type of the binary bitwise operations `^`, `|` and `&`. Previously the standard type was used, instead of applying the essential type rules which dictate that if both arguments have the same signedness, the essential type will have the same signedness and a rank equal to the larger of the two operands. \ No newline at end of file diff --git a/change_notes/2024-10-30-fix-issue-629.md b/change_notes/2024-10-30-fix-issue-629.md new file mode 100644 index 0000000000..1e7421f6f6 --- /dev/null +++ b/change_notes/2024-10-30-fix-issue-629.md @@ -0,0 +1,2 @@ +- `A7-1-7` - `IdentifierDeclarationAndInitializationNotOnSeparateLines.ql` + - Fixes #629. Adds brackets, excluding expressions statements in macros. diff --git a/change_notes/2024-11-11-fix-fp-789.md b/change_notes/2024-11-11-fix-fp-789.md new file mode 100644 index 0000000000..b06ebb9b11 --- /dev/null +++ b/change_notes/2024-11-11-fix-fp-789.md @@ -0,0 +1,3 @@ +- `A7-1-2` - `VariableMissingConstexpr.ql`: + - Do not report on member variables if the class has un-instantiated member function(s). + - Check a call's qualifier as well whether it can be compile time evaluated or not. diff --git a/change_notes/2024-11-13-fix-fp-796.md b/change_notes/2024-11-13-fix-fp-796.md new file mode 100644 index 0000000000..5fa32f57e8 --- /dev/null +++ b/change_notes/2024-11-13-fix-fp-796.md @@ -0,0 +1,2 @@ + - `A13-3-1` - `FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql`: + - Reduce false positives by explicitly checking that the locations of overloaded functions are different. diff --git a/change_notes/2024-11-27-c-object-refactor.md b/change_notes/2024-11-27-c-object-refactor.md new file mode 100644 index 0000000000..511ce1b7ce --- /dev/null +++ b/change_notes/2024-11-27-c-object-refactor.md @@ -0,0 +1,21 @@ +- `CON34-C` - `AppropriateThreadObjectStorageDurations.ql`: + - Improved analysis for detecting objects with automatic storage duration + - New reports will include `a.x`, `a[x]` for object `a` with automatic storage duration +- `DCL30-C` - `AppropriateStorageDurationsFunctionReturn.ql`: + - Improved analysis for detecting objects with automatic storage duration + - New reports will include `a.x`, `a[x]` for object `a` with automatic storage duration + - False positives related to returning copying pointer values +- `EXP35-C` - `DoNotModifyObjectsWithTemporaryLifetime.ql`: + - Improved analysis for detecting objects with temporary lifetime + - More non-lvalue expressions that produce temporary objects detected, for instance `(x = y).x`, previously only `f().x` discovered +- `MEM33-C` - `AllocStructsWithAFlexibleArrayMemberDynamically.ql`: + - Improved analysis for detecting objects with automatic storage duration + - New reports will include struct literals with a flexible array member +- `RULE-18-9` - `ModifiableLValueSubscriptedWithTemporaryLifetime.ql`: + - Problems will be reported at more obviously non-lvalue locations + - Implementation refactored to be shared with other libraries + - No other changes expected +- `RULE-18-9` - `ArrayToPointerConversionOfTemporaryLifetime.ql`: + - Problems will be reported at more obviously non-lvalue locations + - Implementation refactored to be shared with other libraries + - No other changes expected \ No newline at end of file diff --git a/change_notes/2024-11-27-raii-concurrency-analysis-perf.md b/change_notes/2024-11-27-raii-concurrency-analysis-perf.md new file mode 100644 index 0000000000..3a08427808 --- /dev/null +++ b/change_notes/2024-11-27-raii-concurrency-analysis-perf.md @@ -0,0 +1,2 @@ + - `Concurrency` - for all queries related to RAII-style mutexes + - These types of locks have been refactored to improve performance in some queries. No change in query results expected. \ No newline at end of file diff --git a/change_notes/2024-11-27-resource-leak-analysis-refactor.md b/change_notes/2024-11-27-resource-leak-analysis-refactor.md new file mode 100644 index 0000000000..8f2799b543 --- /dev/null +++ b/change_notes/2024-11-27-resource-leak-analysis-refactor.md @@ -0,0 +1,10 @@ +- `ERR57-CPP` - `DoNotLeakResourcesWhenHandlingExceptions.ql`: + - Resource leak detection code refactored for sharing across queries + - Control flow no longer uses "cut nodes." This could impact performance positively or negatively, however measurements have been taken that indicate no significant change + - Some false positives have been suppressed due to slightly different control flow approach + - Leaked mutex locks and open files are reported at slightly different location, reported at call site (e.g. `f.open(...)`, `m.lock()`) rather than on the variable itself (`f` and `m`). +- `A15-1-4` - `ValidResourcesStateBeforeThrow.ql`: + - Resource leak detection code refactored for sharing across queries + - Control flow no longer uses "cut nodes." This could impact performance positively or negatively, however measurements have been taken that indicate no significant change + - Some false positives have been suppressed due to slightly different control flow approach + - Leaked mutex locks and open files are reported at slightly different location, reported at call site (e.g. `f.open(...)`, `m.lock()`) rather than on the variable itself (`f` and `m`). \ No newline at end of file diff --git a/change_notes/2024-12-05-upgrade-to-2.18.4.md b/change_notes/2024-12-05-upgrade-to-2.18.4.md new file mode 100644 index 0000000000..6f3d4ba404 --- /dev/null +++ b/change_notes/2024-12-05-upgrade-to-2.18.4.md @@ -0,0 +1,3 @@ +- Updated the CodeQL version to `2.18.4`. +- `A12-8-6` - `CopyAndMoveNotDeclaredProtected.ql`: + - Implicitly created copy and move constructors will no longer be flagged in tenplate instantiations when they are unused, or trivial (tracked at https://github.com/github/codeql-coding-standards/issues/811). \ No newline at end of file diff --git a/change_notes/2024-12-08-identifier-hiding.md b/change_notes/2024-12-08-identifier-hiding.md new file mode 100644 index 0000000000..b769b16e57 --- /dev/null +++ b/change_notes/2024-12-08-identifier-hiding.md @@ -0,0 +1,7 @@ + - `A2-10-1` - `IdentifierHiding.ql`: + - Improved evaluation performance. + - Addressed false negatives where nested loops used the same variable name. + - Exclude cases where a variable declared in a lambda expression shadowed a global or namespace variable that did not appear in the same translation unit. + - `RULE-5-3` - `IdentifierHidingC.ql`: + - Improved evaluation performance. + - Addressed false negatives where nested loops used the same variable name. \ No newline at end of file diff --git a/change_notes/2024-12-10-a15-4-4-deviations.md b/change_notes/2024-12-10-a15-4-4-deviations.md new file mode 100644 index 0000000000..4a595e3e00 --- /dev/null +++ b/change_notes/2024-12-10-a15-4-4-deviations.md @@ -0,0 +1,2 @@ + - `A15-4-4` - `MissingNoExcept.ql`: + - Enable deviations on either declarations or definitions. \ No newline at end of file diff --git a/change_notes/2024-12-10-refactor-concurrency-library.md b/change_notes/2024-12-10-refactor-concurrency-library.md new file mode 100644 index 0000000000..ccefe85f19 --- /dev/null +++ b/change_notes/2024-12-10-refactor-concurrency-library.md @@ -0,0 +1,2 @@ + - `Concurrency.qll` - for all queries using this library + - This has been refactored into a set of smaller utility files. No impact on query results or performance expected. \ No newline at end of file diff --git a/change_notes/2024-12-10-udpate-a7-1-1.md b/change_notes/2024-12-10-udpate-a7-1-1.md new file mode 100644 index 0000000000..6efa1ae01f --- /dev/null +++ b/change_notes/2024-12-10-udpate-a7-1-1.md @@ -0,0 +1,2 @@ +- `A7-1-1` - `DeclarationUnmodifiedObjectMissingConstSpecifier.ql`: + - Exclude rvalue references. diff --git a/change_notes/2024-12-12-complex-floating-essential-types.md b/change_notes/2024-12-12-complex-floating-essential-types.md new file mode 100644 index 0000000000..5f5b6b519f --- /dev/null +++ b/change_notes/2024-12-12-complex-floating-essential-types.md @@ -0,0 +1,8 @@ + - `EssentialType` - for all queries related to essential types: + - Complex floating types are now considered a different essential type than real floating types. + - `RULE-10-1` `RULE-10-3`, `RULE-10-4`, `RULE-10-5`, `RULE-10-7`, `RULE-10-8` - `OperandsOfAnInappropriateEssentialType.ql`, `AssignmentOfIncompatibleEssentialType.ql`, `OperandsWithMismatchedEssentialTypeCategory.ql`, `InappropriateEssentialTypeCast.ql`, `ImplicitConversionOfCompositeExpression.ql`, `InappropriateCastOfCompositeExpression.ql`: + - Updates to rules handling complex floating types in MISRA-C 2012 Amendment 3 have been implemented. +- `RULE-14-1`, `LoopOverEssentiallyFloatType.ql`: + - Query updated to account for the existence of complex essentially floating point types. No change in query results or performance expected. + - `DIR-4-6` - `PlainNumericalTypeUsedOverExplicitTypedef.ql`: + - Updates from MISRA-C 2012 Amendment 3 specifying complex fixed width typedef support has been implemented. \ No newline at end of file diff --git a/change_notes/2024-12-12-lessen-emergent-language-feature-restrictions.md b/change_notes/2024-12-12-lessen-emergent-language-feature-restrictions.md new file mode 100644 index 0000000000..2893ba620b --- /dev/null +++ b/change_notes/2024-12-12-lessen-emergent-language-feature-restrictions.md @@ -0,0 +1,2 @@ + - `RULE-1-4` - `EmergentLanguageFeaturesUsed.ql`: + - Remove restrictions on `stdnoreturn.h`, `stdalign.h`. \ No newline at end of file diff --git a/change_notes/2024-12-13-implement-misra-c-amendment4-rule-amendments.md b/change_notes/2024-12-13-implement-misra-c-amendment4-rule-amendments.md new file mode 100644 index 0000000000..b168ccaf78 --- /dev/null +++ b/change_notes/2024-12-13-implement-misra-c-amendment4-rule-amendments.md @@ -0,0 +1,11 @@ + - `RULE-11-3` - `CastBetweenObjectPointerAndDifferentObjectType.ql` + - Constrain exception that pointer types to may be cast to char types, so that it does not apply to atomic pointer types, in compliance with MISRA-C 2012 Amendment 4. + - `RULE-11-8` - `CastRemovesConstOrVolatileQualification.ql` + - Query expanded to detect cases of removing `_Atomic` qualification, in compliance with MISRA-C 2012 Amendment 4. + - `EXP33-C`, `RULE-9-1`, `A8-5-0`, `EXP53-CPP` - `DoNotReadUninitializedMemory.ql`, `ObjectWithAutoStorageDurationReadBeforeInit.ql`, `MemoryNotInitializedBeforeItIsRead.ql`, `DoNotReadUninitializedMemory.ql` + - Atomic local variables excluded from query results, in compliance with MISRA-C 2012 Amendment 4, and to reduce false positives in the other standards. + - `RULE-13-2` - `UnsequencedAtomicReads.ql` + - New query to find expressions which read an atomic variable more than once between sequence points, to address new case from MISRA-C 2012 Amendment 4. + - `RULE-3-1` - `CharacterSequencesAndUsedWithinAComment.ql` + - Add exception allowing URLs inside of cpp-style `/* ... */` comments, in compliance with MISRA-C 2012 Amendment 4. + - No longer report cases of `//*some comment` in this rule. \ No newline at end of file diff --git a/change_notes/2024-12-17-fix-fp-824-a15-4-4 b/change_notes/2024-12-17-fix-fp-824-a15-4-4 new file mode 100644 index 0000000000..89ccf49815 --- /dev/null +++ b/change_notes/2024-12-17-fix-fp-824-a15-4-4 @@ -0,0 +1,2 @@ + - `A15-4-4` - `MissingNoExcept.ql`: + - Reduce false positives by not reporting on functions that have a noexcept specification with a complex expression or call other such functions. diff --git a/change_notes/2024-12-18-fix-fp-540-a3-9-1.md b/change_notes/2024-12-18-fix-fp-540-a3-9-1.md new file mode 100644 index 0000000000..fbd09ca840 --- /dev/null +++ b/change_notes/2024-12-18-fix-fp-540-a3-9-1.md @@ -0,0 +1,2 @@ + - `A3-9-1` - `VariableWidthIntegerTypesUsed.ql`: + - Reduce false positives by not considering variables from template instantiations. diff --git a/change_notes/2024-9-20-a1-1-2-improvements.md b/change_notes/2024-9-20-a1-1-2-improvements.md new file mode 100644 index 0000000000..25e393954b --- /dev/null +++ b/change_notes/2024-9-20-a1-1-2-improvements.md @@ -0,0 +1,2 @@ +- `A1-1-2` - `CompilerWarningLevelNotInCompliance.ql`: + - Report non-compliance for compilations that use the error-suppressing `-w` flag. diff --git a/change_notes/2025-01-09-return-reference.md b/change_notes/2025-01-09-return-reference.md new file mode 100644 index 0000000000..69480916c7 --- /dev/null +++ b/change_notes/2025-01-09-return-reference.md @@ -0,0 +1,2 @@ + - `M7-5-1`, `RULE-6-8-2` - `FunctionReturnAutomaticVarCondition.ql`, `ReturnReferenceOrPointerToAutomaticLocalVariable.ql`: + - Remove false positives for member and global variables reported under this rule. \ No newline at end of file diff --git a/change_notes/2025-01-21-a7-1-2-remove-function-constexpr.md b/change_notes/2025-01-21-a7-1-2-remove-function-constexpr.md new file mode 100644 index 0000000000..ac9964adc9 --- /dev/null +++ b/change_notes/2025-01-21-a7-1-2-remove-function-constexpr.md @@ -0,0 +1,2 @@ + - `A7-1-2` - `FunctionMissingConstexpr.ql` + - Address false positives by removing the query - the rule is not intended to cover functions. \ No newline at end of file diff --git a/change_notes/2025-01-29-implement-misra-clarifications-change-categories.md b/change_notes/2025-01-29-implement-misra-clarifications-change-categories.md new file mode 100644 index 0000000000..04ef636392 --- /dev/null +++ b/change_notes/2025-01-29-implement-misra-clarifications-change-categories.md @@ -0,0 +1,6 @@ + - `RULE-13-6` - `SizeofOperandWithSideEffect.ql`: + - Changed from Mandatory to Required in implementation of Technical Corrigenda 2. + - `RULE-17-5` - `ArrayFunctionArgumentNumberOfElements.ql`: + - Changed from Advisory to Required in implementation of Technical Corrigenda 2. + - `RULE-21-11` - `StandardHeaderFileTgmathhUsed.ql`: + - Changed from Required to Advisory in implementation of Amendment 3. \ No newline at end of file diff --git a/change_notes/2025-02-06-a3-1-5-audit.md b/change_notes/2025-02-06-a3-1-5-audit.md new file mode 100644 index 0000000000..1f56a25236 --- /dev/null +++ b/change_notes/2025-02-06-a3-1-5-audit.md @@ -0,0 +1,2 @@ + - `A3-1-5` - `NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql`: + - Mark this as an `audit` query. As a consequence, it will no longer be run as part of the default query suite for AUTOSAR. It can still be run as part of the `autosar-audit.qls` query suite. The query has been downgraded because the rule allows for functions to be declared in the class body if they were "intended" to be inlined, and that developer intention cannot be determined automatically from the code. \ No newline at end of file diff --git a/change_notes/2025-02-06-m5-3-1-exclude-unknown-type.md b/change_notes/2025-02-06-m5-3-1-exclude-unknown-type.md new file mode 100644 index 0000000000..ba7f50af45 --- /dev/null +++ b/change_notes/2025-02-06-m5-3-1-exclude-unknown-type.md @@ -0,0 +1,2 @@ + - `M5-3-1` - `EachOperandOfTheOperatorOfTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql`: + - Consistently exclude results in unevaluated contexts associated with uninstantiated templates, for example `noexcept` specifiers and `static_assert`s. \ No newline at end of file diff --git a/change_notes/2025-02-10-improve-perf-a5-1-9.md b/change_notes/2025-02-10-improve-perf-a5-1-9.md new file mode 100644 index 0000000000..5d355bab49 --- /dev/null +++ b/change_notes/2025-02-10-improve-perf-a5-1-9.md @@ -0,0 +1,3 @@ + - `A5-1-9` - `IdenticalLambdaExpressions.ql`: + - Performance has been improved. + - False positives due to repeated invocation of macros containing lambdas have been excluded. \ No newline at end of file diff --git a/change_notes/2025-02-13-deviations.md b/change_notes/2025-02-13-deviations.md new file mode 100644 index 0000000000..fb01cdf596 --- /dev/null +++ b/change_notes/2025-02-13-deviations.md @@ -0,0 +1,13 @@ + - A new in code deviation format has been introduced, using the C/C++ attribute syntax: + ``` + [[codeql::_deviation("")]] + ``` + This can be applied to functions, statements and variables to apply a deviation from the Coding Standards configuration file. The user manual has been updated to describe the new format. + - For those codebases that cannot use standard attributes, we have also introduced a comment based syntax + ``` + // codeql::_deviation() + // codeql::_deviation_next_line() + // codeql::_deviation_begin() + // codeql::_deviation_end() + ``` + Further information is available in the user manual. \ No newline at end of file diff --git a/change_notes/2025-02-13-fix-issue-718.md b/change_notes/2025-02-13-fix-issue-718.md new file mode 100644 index 0000000000..39e499d583 --- /dev/null +++ b/change_notes/2025-02-13-fix-issue-718.md @@ -0,0 +1,2 @@ +- `A2-7-3` - `UndocumentedUserDefinedType.ql` + - Fixes #718. Include trailing characters after group comment endings with ///@{ ... ///@}. diff --git a/change_notes/2025-02-17-iofstream-performance.md b/change_notes/2025-02-17-iofstream-performance.md new file mode 100644 index 0000000000..8e566d3778 --- /dev/null +++ b/change_notes/2025-02-17-iofstream-performance.md @@ -0,0 +1,2 @@ + - `A27-0-3`, `FIO309-C`, `FIO50-CPP`, `RULE-30-0-2` - `InterleavedInputOutputWithoutFlush.ql`, `DoNotAlternatelyIOFromStreamWithoutPositioning.ql`, `InterleavedInputOutputWithoutPosition.ql`, `ReadsAndWritesOnStreamNotSeparatedByPositioning.ql`: + - Reduce evaluation time on complex codebases. \ No newline at end of file diff --git a/change_notes/2025-02-20-rule-22-16-update-aliasing-for-performance.md b/change_notes/2025-02-20-rule-22-16-update-aliasing-for-performance.md new file mode 100644 index 0000000000..80ff92748f --- /dev/null +++ b/change_notes/2025-02-20-rule-22-16-update-aliasing-for-performance.md @@ -0,0 +1,3 @@ + - `RULE-22-16`, `ERR57-CPP`, `A15-1-4` - `MutexObjectsNotAlwaysUnlocked.ql`, `DoNotLeakResourcesWhenHandlingExceptions.ql`, `ValidResourcesStateBeforeThrow.ql`: + - Shared module `ResourceLeakAnalysis.qll` changed to not get aliases recursively for simplicity and improved performance. The recent update to these queries had logic intending to handle the case where an allocation node is an alias of a parent node, and the free operation releases that parent node. However, the behavior was incorrectly defined and not working, and in the presence of performance issues this behavior has been removed. + - (`RULE-22-16` only) The alias behavior has been updated to compare expressions with `HashCons` instead of `GlobalValueNumbering` for higher performance. GVN is more expensive generally, seemed to introduce low performance joins secondarily, and is stricter than `HashCons` in a contravening position, meaning a stricter analysis introduces a higher likelihood of false positives. \ No newline at end of file diff --git a/change_notes/2025-02-25-move-type-related-libraries.md b/change_notes/2025-02-25-move-type-related-libraries.md new file mode 100644 index 0000000000..9f4fbd0bf2 --- /dev/null +++ b/change_notes/2025-02-25-move-type-related-libraries.md @@ -0,0 +1,2 @@ + - All rules using `Type.qll`, `TypeUses.qll`, `Pointers.qll`, `TrivialType.qll`, `VariablyModifiedTypes.qll`: + - Files moved into `cpp/common/types` directory. No external changes in behavior expected. \ No newline at end of file diff --git a/change_notes/2025-02-25-update-macro-deduplication-library.md b/change_notes/2025-02-25-update-macro-deduplication-library.md new file mode 100644 index 0000000000..90b3ef51af --- /dev/null +++ b/change_notes/2025-02-25-update-macro-deduplication-library.md @@ -0,0 +1,4 @@ +- `RULE-2-8` - `UnusedObjectDefinition.ql`, `UnusedObjectDefinitionStrict.ql`: + - Refactor to allow additional parameters in non-macro results for library `DeduplicateMacroResults.qll`. + - Refactor to replace `Location` with `Locatable` in API of library `DeduplicationMacroResults.qll`. + - No observable difference in behavior expected. diff --git a/change_notes/2025-03-04-essential-types-with-explicit-conversions.md b/change_notes/2025-03-04-essential-types-with-explicit-conversions.md new file mode 100644 index 0000000000..aa32044087 --- /dev/null +++ b/change_notes/2025-03-04-essential-types-with-explicit-conversions.md @@ -0,0 +1,2 @@ + - `EssentialType` - for all queries related to essential types: + - Updated the way essential types of expressions with "conversions" (including explicit casts, parenthesis, and implicit conversions such as array-to-pointer conversions) are handled, to get proper essential types when parenthesis, casts, and generics interact. \ No newline at end of file diff --git a/change_notes/2025-03-04-more-accurate-type-comparisons.md b/change_notes/2025-03-04-more-accurate-type-comparisons.md new file mode 100644 index 0000000000..942d76f7af --- /dev/null +++ b/change_notes/2025-03-04-more-accurate-type-comparisons.md @@ -0,0 +1,6 @@ + - `RULE-8-3` - `DeclarationsOfAFunctionSameNameAndType.ql`, `DeclarationsOfAnObjectSameNameAndType.ql`: + - New shared module used to fix false positives for compound types referring to the same basic integer types under a different name, e.g., query will not report for `signed[4]` used in place of `int[4]` as per MISRA spec. + - Now query will report incompatibilities for two functions of the same name with a different number of parameters. + - Query result string updated to not use the word "Compatible," which is confusing, as it may falsely appear that the query is testing for compatibility as defined by C17. + - `RULE-8-4`, `DCL-40C` - `CompatibleDeclarationFunctionDefined.ql`, `CompatibleDeclarationObjectDefined.ql`, `IncomptatibleFunctionDeclarations.ql`: + - New shared module used to fix false positives by updating "compatible" type checks to more closely match the C17 standard. For instance, `int[3]` and `int[]` are compatible declarations (while `int[3]` and `int[4]` are not), and typedefs are now resolved as well. Some false positives may still occur regarding structs from different compilation units. \ No newline at end of file diff --git a/change_notes/2025-03-09-rule-8-7.md b/change_notes/2025-03-09-rule-8-7.md new file mode 100644 index 0000000000..3c3678ca6d --- /dev/null +++ b/change_notes/2025-03-09-rule-8-7.md @@ -0,0 +1,4 @@ + - `RULE-8-7` - `ShouldNotBeDefinedWithExternalLinkage.ql`: + - Remove false positives where the declaration is not defined in the database. + - Remove false positives where the definition and reference are in different translation units. + - Remove false positives where the reference occurs in a header file. \ No newline at end of file diff --git a/change_notes/2025-03-11-various-misra-amendments.md b/change_notes/2025-03-11-various-misra-amendments.md new file mode 100644 index 0000000000..19783fe803 --- /dev/null +++ b/change_notes/2025-03-11-various-misra-amendments.md @@ -0,0 +1,9 @@ + - `DIR-4-9` - `FunctionOverFunctionLikeMacro.ql`: + - Macros with `_Generic` now no longer reported. + - `RULE-1-4` - `EmergentLanguageFeaturesUsed.ql`: + - Ban on usage of `_Generics` removed. + - `RULE-18-6` - `ThreadLocalObjectAddressCopiedToGlobalObject.ql`: + - New query added to detect thread local objects assigned to static storage duration objects. + - `RULE-21-12` - `ExceptionHandlingFeaturesOfFenvhUsed.ql`: + - Added reports for `#include`ing "fenv.h", and for using `fesetenv`, `feupdatenv`, and `fesetround`. + - Report message altered to handle new cases. \ No newline at end of file diff --git a/change_notes/2025-03-26-deviations-suppression.md b/change_notes/2025-03-26-deviations-suppression.md new file mode 100644 index 0000000000..5dcb5e8dba --- /dev/null +++ b/change_notes/2025-03-26-deviations-suppression.md @@ -0,0 +1 @@ + - The `DeviationsSuppression.ql` query has been restored after being incorrectly deleted in a previous release. \ No newline at end of file diff --git a/change_notes/2025-03-27-detect-precision-limit-in-trig-functions.md b/change_notes/2025-03-27-detect-precision-limit-in-trig-functions.md new file mode 100644 index 0000000000..914b25a2a2 --- /dev/null +++ b/change_notes/2025-03-27-detect-precision-limit-in-trig-functions.md @@ -0,0 +1,2 @@ + - `DIR-4-11` - `LowPrecisionPeriodicTrigonometricFunctionCall.ql`: + - New query within rule added to detect calls to periodic trigonometric functions with values outside of pi*k for k that depends on implementation and application precision goals, assuming k=1 for 32 bit floating types and k=10 for 64 bit floating types. diff --git a/change_notes/2025-03-31-allow-atomics-threads-and-threadlocals-in-misra-c.md b/change_notes/2025-03-31-allow-atomics-threads-and-threadlocals-in-misra-c.md new file mode 100644 index 0000000000..b59e04610f --- /dev/null +++ b/change_notes/2025-03-31-allow-atomics-threads-and-threadlocals-in-misra-c.md @@ -0,0 +1,2 @@ + - `RULE-1-4` - `EmergentLanguageFeaturesUsed.ql`: + - Allow usage of atomics, `thread.h`, and `_Thread_local` as per Misra C 2012 Amendment 4. \ No newline at end of file diff --git a/change_notes/2025-04-08-address-cross-compiler-compatibility-in-misra-2023.md b/change_notes/2025-04-08-address-cross-compiler-compatibility-in-misra-2023.md new file mode 100644 index 0000000000..abe4c2bba8 --- /dev/null +++ b/change_notes/2025-04-08-address-cross-compiler-compatibility-in-misra-2023.md @@ -0,0 +1,12 @@ + - `RULE-21-22`, `RULE-21-23` - `TgMathArgumentWithInvalidEssentialType.ql`, `TgMathArgumentsWithDifferingStandardType.ql` + - Change type-generic macro analysis for finding macro parameters to be compatible with gcc, by ignoring early arguments inserted by gcc. + - Change explicit conversion logic to ignore the explicit casts inserted in macro bodies by clang, which previously overruled the argument essential type. + - `RULE-13-2` - `UnsequencedAtomicReads.ql`: + - Handle statement expression implementation of atomic operations in gcc. + - `RULE-21-25` - `InvalidMemoryOrderArgument.ql`: + - Handle case of where the enum `memory_order` is declared via a typedef as an anonymous enum. + - Rewrite how atomically sequenced operations are found; no longer look for builtins or internal functions, instead look for macros with the exact expected name and analyze the macro bodies for the memory sequence parameter. + - `RULE-9-7` - `UninitializedAtomicArgument.ql`: + - Handle gcc case where `atomic_init` is defined is a call to `atomic_store`, and take a more flexible approach to finding the initialized atomic variable. + - `DIR-4-15` - `PossibleMisuseOfUndetectedInfinity.ql`, `PossibleMisuseOfUndetectedNaN.ql`: + - Fix issue when analyzing clang/gcc implementations of floating point classification macros, where analysis incorrectly determined that `x` in `isinf(x)` was guaranteed to be infinite at the call site itself, affecting later analysis involving `x`. \ No newline at end of file diff --git a/change_notes/2025-04-09-new-cert-c-recommendation-query-suite.md b/change_notes/2025-04-09-new-cert-c-recommendation-query-suite.md new file mode 100644 index 0000000000..910433e351 --- /dev/null +++ b/change_notes/2025-04-09-new-cert-c-recommendation-query-suite.md @@ -0,0 +1,9 @@ + - The following query suites have been added or modified for CERT C: + - A new query suite has been created `cert-c-default.qls` to avoid confusion with the CERT C++ query suites. The `cert-default.qls` suite has been deprecated, and will be removed in a future releases, and is replaced by the `cert-c-default.qls` suite. + - The `cert-c-default.qls` suite has been specified as the default for the pack, and will include our most up-to-date coverage for CERT C. + - One new query suite, `cert-c-recommended.qls` has been added to enable running CERT recommendations (as opposed to rules) that will be added in the future. + - The default query suite, `cert-c-default.qls` has been set to exclude CERT recommendations (as opposed to rules) that will be added in the future. + - The following query suites have been added or modified for CERT C++: + - A new query suite has been created `cert-cpp-default.qls` to avoid confusion with the CERT C query suites. The `cert-default.qls` suite has been deprecated, and will be removed in a future releases, and is replaced by the `cert-cpp-default.qls` suite. + - The `cert-cpp-default.qls` suite has been specified as the default for the pack, and will include our most up-to-date coverage for CERT C. + - A new query suite has been created `cert-cpp-single-translation-unit.qls` to avoid confusion with the CERT C query suites. The `cert-single-translation-unit.qls` suite has been deprecated, and will be removed in a future releases, and is replaced by the `cert-cpp-single-translation-unit.qls` suite. \ No newline at end of file diff --git a/change_notes/2025-04-14-update-infinity-nan-detection.md b/change_notes/2025-04-14-update-infinity-nan-detection.md new file mode 100644 index 0000000000..fe484a37fe --- /dev/null +++ b/change_notes/2025-04-14-update-infinity-nan-detection.md @@ -0,0 +1,4 @@ + - `DIR-4-15` - `PossibleMisuseOfUndetectedInfinity.ql`, `PossibleMisuseOfUndetectedNaN.ql`: + - Add logic to suppress NaNs from the CodeQL extractor in the new restricted range analysis, which can have unexpected downstream effects. + - Alter the behavior of floating point class guards (such as `isinf`, `isfinite`, `isnan`) to more correctly reflect the branches that have been guarded. + - Query files have been moved/refactored to share logic across MISRA-C and MISRA-C++; no observable change in behavior from this is expected. \ No newline at end of file diff --git a/change_notes/2025-04-25-improve-type-comparison-performance.md b/change_notes/2025-04-25-improve-type-comparison-performance.md new file mode 100644 index 0000000000..39c462fbf2 --- /dev/null +++ b/change_notes/2025-04-25-improve-type-comparison-performance.md @@ -0,0 +1,6 @@ + - `RULE-8-3`, `RULE-8-4`, `DCL40-C`, `RULE-23-5`: `DeclarationsOfAFunctionSameNameAndType.ql`, `DeclarationsOfAnObjectSameNameAndType.ql`, `CompatibleDeclarationOfFunctionDefined.ql`, `CompatibleDeclarationObjectDefined.ql`, `IncompatibleFunctionDeclarations.ql`, `DangerousDefaultSelectionForPointerInGeneric.ql`: + - Added pragmas to alter join order on function parameter equivalence (names and types). + - Refactored expression which the optimizer was confused by, and compiled into a cartesian product. + - Altered the module `Compatible.qll` to compute equality in two stages. Firstly, all pairs of possible type comparisons (including recursive comparisons) are found, then those pairwise comparisons are evaluated in a second stage. This greatly reduces the number of comparisons and greatly improves performance. + - `RULE-23-5`: `DangerousDefaultSelectionForPointerInGeneric.ql`: + - Altered the module `SimpleAssignment.qll` in accordance with the changes to `Compatible.qll`. \ No newline at end of file diff --git a/change_notes/2025-05-01-cert-extra-props.md b/change_notes/2025-05-01-cert-extra-props.md new file mode 100644 index 0000000000..3244360703 --- /dev/null +++ b/change_notes/2025-05-01-cert-extra-props.md @@ -0,0 +1,2 @@ + - All CERT rules now include additional tags to represent the [Risk Assessment](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RiskAssessment) properties specified on CERT rules. + - In addition, new query suites are included which allow the selection of queries that represent CERT Rules (not Recommendations) for each of the Levels (1-3). These are called `cert--.qls` and can be used either directly in the CodeQL CLI, or via the CodeQL Action. \ No newline at end of file diff --git a/change_notes/2025-05-15-misra-c-2023.md b/change_notes/2025-05-15-misra-c-2023.md new file mode 100644 index 0000000000..defd2ff823 --- /dev/null +++ b/change_notes/2025-05-15-misra-c-2023.md @@ -0,0 +1,4 @@ + - Support for MISRA C 2023 is now completed. + - The default query suites for MISRA C now target MISRA C 2023. + - The user manual has been updated to list MISRA C 2023 as completed. + - The `misra-c-2012-third-edition-with-amendment-2.qls` query suite can be used to run the queries present in MISRA C 2012 (3rd Edition) and Amendment 2. \ No newline at end of file diff --git a/change_notes/2025-06-06-same-source-performance.md b/change_notes/2025-06-06-same-source-performance.md new file mode 100644 index 0000000000..e0dfc36dea --- /dev/null +++ b/change_notes/2025-06-06-same-source-performance.md @@ -0,0 +1,2 @@ + - `FIO39-C`, `FIO50-CPP`, `A27-0-3`, `RULE-30-0-2`: `IOFstreamMissingPositioning.ql`, `InterleavedInputOutputWithoutPosition.ql`, `InterleavedInputOutputWithoutFlush.ql`, `ReadsAndWritesOnStreamNotSeparatedByPositioning.ql`. + - Improved performance for codebases with large numbers of stream or file accesses. \ No newline at end of file diff --git a/change_notes/2025-07-11-typo-in-alert-message..md b/change_notes/2025-07-11-typo-in-alert-message..md new file mode 100644 index 0000000000..077f2efb66 --- /dev/null +++ b/change_notes/2025-07-11-typo-in-alert-message..md @@ -0,0 +1,2 @@ +- `SIG30-C`: `CallOnlyAsyncSafeFunctionsWithinSignalHandlers.ql` + - Fixed a misspelling of "asynchronous" in the alert message. diff --git a/change_notes/2025-08-15-typo-in-alert-message.md b/change_notes/2025-08-15-typo-in-alert-message.md new file mode 100644 index 0000000000..a953f3e86d --- /dev/null +++ b/change_notes/2025-08-15-typo-in-alert-message.md @@ -0,0 +1,2 @@ +- `ENV34-C`, `RULE-21-20`, `RULE-25-5-3`: `DoNotStorePointersReturnedByEnvFunctions.ql`, `CallToSetlocaleInvalidatesOldPointers.ql`, `CallToSetlocaleInvalidatesOldPointersMisra.ql` + - Fixed a misspelling of "subsequent" in the alert message. diff --git a/change_notes/2025-1-04-misra-c-technical-corrigenda-2.md b/change_notes/2025-1-04-misra-c-technical-corrigenda-2.md new file mode 100644 index 0000000000..6849951810 --- /dev/null +++ b/change_notes/2025-1-04-misra-c-technical-corrigenda-2.md @@ -0,0 +1,12 @@ + - `RULE-8-3` - `DeclarationsOfAFunctionSameNameAndType.ql`: + - Implement new exception, unnamed parameters are not covered by this rule. + - `RULE-10-2` - `AdditionSubtractionOnEssentiallyCharType.ql`: + - Disallow `+` and `-` operations with an essentially char type and other types larger than int type. + - Note, this change affects the essential type of such expressions, which may affect other essential types rules. + - `RULE-18-1`, `M5-0-16` - `PointerAndDerivedPointerMustAddressSameArray.ql`, `PointerAndDerivedPointerAccessDifferentArray.ql`: + - Treat casts to byte pointers as pointers to arrays of the size of the pointed-to type. + - Fix typo in report message, "passed" replaced with "past." + - Suppress results where range analysis appears potentially unreliable. + - `RULE-21-10`, `RULE-25-5-3`, `ENV34-C` - `CallToSetlocaleInvalidatesOldPointers.ql`, `CallToSetlocaleInvalidatesOldPointersMisra.ql`, `DoNotStorePointersReturnedByEnvFunctions.ql`: + - Report usage of returned pointers from `asctime`, `ctime`, during a call to either of the former. + - Report usage of returned pointers from `gmtime`, `localtime`, during a call to either of the former. \ No newline at end of file diff --git a/change_notes/2025-7-15-fix-performance-issues-in-2.20.7.md b/change_notes/2025-7-15-fix-performance-issues-in-2.20.7.md new file mode 100644 index 0000000000..a936579a97 --- /dev/null +++ b/change_notes/2025-7-15-fix-performance-issues-in-2.20.7.md @@ -0,0 +1,4 @@ + - `DCL40-C`, `RULE-8-4`: `IncompatibleFunctionDeclarations.ql`, `CompatibleDeclarationFunctionDefined.ql`. + - Fixed performance issues introduced when upgrading to CodeQL `2.20.7` by removing unnecessary check that matching function declarations have matching names. + - `RULE-7-5`: `IncorrectlySizedIntegerConstantMacroArgument.ql`. + - Added a `bindingset` to improve performance when checking if a literal matches the size of an integer constant macro. \ No newline at end of file diff --git a/codeql_modules/codeql b/codeql_modules/codeql deleted file mode 160000 index 28fe7a7660..0000000000 --- a/codeql_modules/codeql +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 28fe7a76603ab7ef884ca35115b63104ecb699a7 diff --git a/cpp/.codeqlmanifest.json b/cpp/.codeqlmanifest.json deleted file mode 100644 index 384848fdd1..0000000000 --- a/cpp/.codeqlmanifest.json +++ /dev/null @@ -1,3 +0,0 @@ -{ "provide": [ - "*/src/qlpack.yml", - "*/test/qlpack.yml" ] } diff --git a/cpp/autosar/src/codeql-pack.lock.yml b/cpp/autosar/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/autosar/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/cpp/autosar/src/codeql-suites/autosar-advisory.qls b/cpp/autosar/src/codeql-suites/autosar-advisory.qls index 163de00856..0de273308e 100644 --- a/cpp/autosar/src/codeql-suites/autosar-advisory.qls +++ b/cpp/autosar/src/codeql-suites/autosar-advisory.qls @@ -1,5 +1,5 @@ -- description: AUTOSAR C++14 Guidelines 20-11 (Advisory) -- qlpack: autosar-cpp-coding-standards +- description: AUTOSAR C++14 Guidelines R22-11, R21-11, R20-11, R19-11 and R19-03 (Advisory) +- qlpack: codeql/autosar-cpp-coding-standards - include: kind: - problem @@ -8,4 +8,4 @@ - external/autosar/obligation/advisory - exclude: tags contain: - - external/autosar/audit \ No newline at end of file + - external/autosar/audit diff --git a/cpp/autosar/src/codeql-suites/autosar-audit.qls b/cpp/autosar/src/codeql-suites/autosar-audit.qls index 6e77e82e77..7ee6d12207 100644 --- a/cpp/autosar/src/codeql-suites/autosar-audit.qls +++ b/cpp/autosar/src/codeql-suites/autosar-audit.qls @@ -1,8 +1,8 @@ -- description: AUTOSAR C++14 Guidelines 20-11 (Audit) -- qlpack: autosar-cpp-coding-standards +- description: AUTOSAR C++14 Guidelines R22-11, R21-11, R20-11, R19-11 and R19-03 (Audit) +- qlpack: codeql/autosar-cpp-coding-standards - include: kind: - problem - path-problem tags contain: - - external/autosar/audit \ No newline at end of file + - external/autosar/audit diff --git a/cpp/autosar/src/codeql-suites/autosar-default.qls b/cpp/autosar/src/codeql-suites/autosar-default.qls index 0296cc54f8..7cd2054bbf 100644 --- a/cpp/autosar/src/codeql-suites/autosar-default.qls +++ b/cpp/autosar/src/codeql-suites/autosar-default.qls @@ -1,5 +1,5 @@ -- description: AUTOSAR C++14 Guidelines 20-11 (Default) -- qlpack: autosar-cpp-coding-standards +- description: AUTOSAR C++14 Guidelines R22-11, R21-11, R20-11, R19-11 and R19-03 (Default) +- qlpack: codeql/autosar-cpp-coding-standards - include: kind: - problem @@ -7,4 +7,4 @@ - exclude: tags contain: - external/autosar/audit - - external/autosar/default-disabled \ No newline at end of file + - external/autosar/default-disabled diff --git a/cpp/autosar/src/codeql-suites/autosar-required.qls b/cpp/autosar/src/codeql-suites/autosar-required.qls index b79562c66c..b7a6a8b872 100644 --- a/cpp/autosar/src/codeql-suites/autosar-required.qls +++ b/cpp/autosar/src/codeql-suites/autosar-required.qls @@ -1,5 +1,5 @@ -- description: AUTOSAR C++14 Guidelines 20-11 (Required) -- qlpack: autosar-cpp-coding-standards +- description: AUTOSAR C++14 Guidelines R22-11, R21-11, R20-11, R19-11 and R19-03 (Required) +- qlpack: codeql/autosar-cpp-coding-standards - include: kind: - problem @@ -8,4 +8,4 @@ - external/autosar/obligation/required - exclude: tags contain: - - external/autosar/audit \ No newline at end of file + - external/autosar/audit diff --git a/cpp/autosar/src/codeql-suites/autosar-single-translation-unit.qls b/cpp/autosar/src/codeql-suites/autosar-single-translation-unit.qls index 6ba3032fa8..2ba8424b27 100644 --- a/cpp/autosar/src/codeql-suites/autosar-single-translation-unit.qls +++ b/cpp/autosar/src/codeql-suites/autosar-single-translation-unit.qls @@ -1,5 +1,5 @@ -- description: AUTOSAR C++14 Guidelines 20-11 (Single Translation Unit) -- qlpack: autosar-cpp-coding-standards +- description: AUTOSAR C++14 Guidelines R22-11, R21-11, R20-11, R19-11 and R19-03 (Single Translation Unit) +- qlpack: codeql/autosar-cpp-coding-standards - include: kind: - problem @@ -9,4 +9,4 @@ - exclude: tags contain: - external/autosar/audit - - external/autosar/default-disabled \ No newline at end of file + - external/autosar/default-disabled diff --git a/cpp/autosar/src/codingstandards/cpp/HardwareOrProtocolInterface.qll b/cpp/autosar/src/codingstandards/cpp/HardwareOrProtocolInterface.qll index 673d7045ed..b0b20b82d9 100644 --- a/cpp/autosar/src/codingstandards/cpp/HardwareOrProtocolInterface.qll +++ b/cpp/autosar/src/codingstandards/cpp/HardwareOrProtocolInterface.qll @@ -3,14 +3,16 @@ import codingstandards.cpp.CommonTypes as CommonTypes abstract class HardwareOrProtocolInterfaceClass extends Class { } +class HardwareOrProtocolInterfaceComment extends Comment { + HardwareOrProtocolInterfaceComment() { + exists(getContents().regexpFind("(?m)^\\s*(//|\\*)\\s*@HardwareOrProtocolInterface\\s*$", _, _)) + } +} + class AnnotatedHardwareOrProtocolInterfaceClass extends HardwareOrProtocolInterfaceClass { AnnotatedHardwareOrProtocolInterfaceClass() { - exists(Comment c, string contents | - c.getCommentedElement() = this.getADeclarationEntry() and - contents = - c.getContents() - .splitAt("\n") - .regexpFind("^\\s*(//|\\*)\\s*@HardwareOrProtocolInterface\\s*$", _, _) + exists(HardwareOrProtocolInterfaceComment c | + c.getCommentedElement() = this.getADeclarationEntry() ) } } @@ -39,7 +41,7 @@ class DefinedSizeType extends Type { class DefinedSizeClass extends Class { DefinedSizeClass() { - this.isPOD() and + this.isPod() and forall(Field f | f = this.getAField() | f.getType() instanceof DefinedSizeType) } } diff --git a/cpp/autosar/src/qlpack.yml b/cpp/autosar/src/qlpack.yml index dfdf8f5ea9..6c763ef633 100644 --- a/cpp/autosar/src/qlpack.yml +++ b/cpp/autosar/src/qlpack.yml @@ -1,4 +1,8 @@ -name: autosar-cpp-coding-standards -version: 2.9.0 +name: codeql/autosar-cpp-coding-standards +version: 2.49.0-dev +description: AUTOSAR C++14 Guidelines R22-11, R21-11, R20-11, R19-11 and R19-03 suites: codeql-suites -libraryPathDependencies: common-cpp-coding-standards +license: MIT +dependencies: + codeql/common-cpp-coding-standards: '*' + codeql/cpp-all: 4.0.3 diff --git a/cpp/autosar/src/rules/A0-1-1/UselessAssignment.ql b/cpp/autosar/src/rules/A0-1-1/UselessAssignment.ql index f6219abe4b..a1c6fb1fa8 100644 --- a/cpp/autosar/src/rules/A0-1-1/UselessAssignment.ql +++ b/cpp/autosar/src/rules/A0-1-1/UselessAssignment.ql @@ -20,6 +20,6 @@ import codingstandards.cpp.deadcode.UselessAssignments from SsaDefinition ultimateDef, InterestingStackVariable v where - not isExcluded(v, DeadCodePackage::uselessAssignmentQuery()) and + not isExcluded(ultimateDef, DeadCodePackage::uselessAssignmentQuery()) and isUselessSsaDefinition(ultimateDef, v) select ultimateDef, "Definition of $@ is unused.", v, v.getName() diff --git a/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql b/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql index 8e8a91ae5a..891a44ed2a 100644 --- a/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql +++ b/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql @@ -16,7 +16,8 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.dataflow.DataFlow +import codingstandards.cpp.Operator +import cpp /* * This query performs a simple syntactic check to ensure that the return value of the function is @@ -39,8 +40,11 @@ where // so the rule does not require the use of the return value not f instanceof Operator and // Exclude cases where the function call is generated within a macro, as the user of the macro is - // not necessarily able to address thoes results + // not necessarily able to address those results not fc.isAffectedByMacro() and - // Rule allows disabling this rule where a static_cast is applied - not fc.getExplicitlyConverted().(StaticCast).getActualType() instanceof VoidType + // Rule allows disabling this rule where a static_cast or a C-style cast to void is applied + not exists(Cast cast | cast instanceof StaticCast or cast instanceof CStyleCast | + fc.getExplicitlyConverted() = cast and + cast.getActualType() instanceof VoidType + ) select fc, "Return value from call to $@ is unused.", f, f.getName() diff --git a/cpp/autosar/src/rules/A0-1-3/UnusedLocalFunction.ql b/cpp/autosar/src/rules/A0-1-3/UnusedLocalFunction.ql index 013047256a..3d5869123a 100644 --- a/cpp/autosar/src/rules/A0-1-3/UnusedLocalFunction.ql +++ b/cpp/autosar/src/rules/A0-1-3/UnusedLocalFunction.ql @@ -19,6 +19,19 @@ import codingstandards.cpp.autosar import codingstandards.cpp.DynamicCallGraph import codingstandards.cpp.deadcode.UnusedFunctions +/** + * Checks if an overloaded function of + * the function passed in the arguments, is called. + */ +predicate overloadedFunctionIsCalled(Function unusedFunction) { + exists(Function f | f = unusedFunction.getAnOverload() and f = getTarget(_)) +} + +/** Checks if a Function's address was taken. */ +predicate addressBeenTaken(Function unusedFunction) { + exists(FunctionAccess fa | fa.getTarget() = unusedFunction) +} + /** A `Function` nested in an anonymous namespace. */ class AnonymousNamespaceFunction extends Function { AnonymousNamespaceFunction() { getNamespace().getParentNamespace*().isAnonymous() } @@ -76,6 +89,16 @@ where functionFromInstantiatedTemplate.isConstructedFrom(functionFromUninstantiatedTemplate) and functionFromInstantiatedTemplate = getTarget(_) ) and + // A function is defined as "used" if any one of the following holds true: + // - It's an explicitly deleted functions e.g. =delete + // - It's annotated as "[[maybe_unused]]" + // - It's part of an overloaded set and any one of the overloaded instance + // is called. + // - It's an operand of an expression in an unevaluated context. + not unusedLocalFunction.isDeleted() and + not unusedLocalFunction.getAnAttribute().getName() = "maybe_unused" and + not overloadedFunctionIsCalled(unusedLocalFunction) and + not addressBeenTaken(unusedLocalFunction) and // Get a printable name ( if exists(unusedLocalFunction.getQualifiedName()) diff --git a/cpp/autosar/src/rules/A0-1-4/UnusedParameter.ql b/cpp/autosar/src/rules/A0-1-4/UnusedParameter.ql index dc446318d5..d7359cc795 100644 --- a/cpp/autosar/src/rules/A0-1-4/UnusedParameter.ql +++ b/cpp/autosar/src/rules/A0-1-4/UnusedParameter.ql @@ -16,11 +16,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.deadcode.UnusedParameters +import codingstandards.cpp.rules.unusedparameter.UnusedParameter -from Function f, UnusedParameter p -where - not isExcluded(p, DeadCodePackage::unusedParameterQuery()) and - f = p.getFunction() and - // Virtual functions are covered by a different rule - not f.isVirtual() -select p, "Unused parameter '" + p.getName() + "' for function $@.", f, f.getQualifiedName() +class UnusedParameterQuery extends UnusedParameterSharedQuery { + UnusedParameterQuery() { this = DeadCodePackage::unusedParameterQuery() } +} diff --git a/cpp/autosar/src/rules/A0-1-6/UnusedTypeDeclarations.ql b/cpp/autosar/src/rules/A0-1-6/UnusedTypeDeclarations.ql index 577decb72f..0026e14959 100644 --- a/cpp/autosar/src/rules/A0-1-6/UnusedTypeDeclarations.ql +++ b/cpp/autosar/src/rules/A0-1-6/UnusedTypeDeclarations.ql @@ -16,13 +16,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TypeUses +import codingstandards.cpp.rules.unusedtypedeclarations.UnusedTypeDeclarations -from UserType ut -where - not isExcluded(ut, DeadCodePackage::unusedTypeDeclarationsQuery()) and - not ut instanceof TemplateParameter and - not ut instanceof ProxyClass and - not exists(getATypeUse(ut)) and - not ut.isFromUninstantiatedTemplate(_) -select ut, "Type declaration " + ut.getName() + " is not used." +class UnusedTypeDeclarationsQuery extends UnusedTypeDeclarationsSharedQuery { + UnusedTypeDeclarationsQuery() { this = DeadCodePackage::unusedTypeDeclarationsQuery() } +} diff --git a/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql b/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql index 3e869efb96..0a59b423d0 100644 --- a/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql +++ b/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql @@ -18,10 +18,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TypeUses +import codingstandards.cpp.types.Uses class NumericLimits extends Class { - NumericLimits() { this.hasQualifiedName("std", "numeric_limits") } + NumericLimits() { this.hasQualifiedName("std", ["numeric_limits", "__libcpp_numeric_limits"]) } /** * Gets the template argument specified for this type. diff --git a/cpp/autosar/src/rules/A0-4-3/CompilerImplementationShallComplyWithCPP14Standard.ql b/cpp/autosar/src/rules/A0-4-3/CompilerImplementationShallComplyWithCPP14Standard.ql index da4b6314e2..98e95ed96f 100644 --- a/cpp/autosar/src/rules/A0-4-3/CompilerImplementationShallComplyWithCPP14Standard.ql +++ b/cpp/autosar/src/rules/A0-4-3/CompilerImplementationShallComplyWithCPP14Standard.ql @@ -20,8 +20,14 @@ from File f, string flag where not isExcluded(f, ToolchainPackage::compilerImplementationShallComplyWithCPP14StandardQuery()) and exists(Compilation c | f = c.getAFileCompiled() | - c.getAnArgument() = flag and flag.regexpMatch("-std=(?!c\\+\\+14)[\\w+]+") - ) + flag = + max(string std, int index | + c.getArgument(index) = std and std.matches("-std=%") + | + std order by index + ) + ) and + flag != "-std=c++14" select f, "File '" + f.getBaseName() + "' compiled with flag '" + flag + "' which does not strictly comply with ISO C++14." diff --git a/cpp/autosar/src/rules/A0-4-4/UncheckedRangeDomainPoleErrors.ql b/cpp/autosar/src/rules/A0-4-4/UncheckedRangeDomainPoleErrors.ql index ed86d17903..76f5406b31 100644 --- a/cpp/autosar/src/rules/A0-4-4/UncheckedRangeDomainPoleErrors.ql +++ b/cpp/autosar/src/rules/A0-4-4/UncheckedRangeDomainPoleErrors.ql @@ -15,69 +15,10 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import codingstandards.cpp.rules.uncheckedrangedomainpoleerrors.UncheckedRangeDomainPoleErrors -bindingset[name] -Function getMathVariants(string name) { result.hasGlobalOrStdName([name, name + "f", name + "l"]) } - -predicate hasDomainError(FunctionCall fc, string description) { - exists(Function functionWithDomainError | fc.getTarget() = functionWithDomainError | - functionWithDomainError = [getMathVariants(["acos", "asin", "atanh"])] and - not ( - upperBound(fc.getArgument(0)) <= 1.0 and - lowerBound(fc.getArgument(0)) >= -1.0 - ) and - description = - "the argument has a range " + lowerBound(fc.getArgument(0)) + "..." + - upperBound(fc.getArgument(0)) + " which is outside the domain of this function (-1.0...1.0)" - or - functionWithDomainError = getMathVariants(["atan2", "pow"]) and - ( - fc.getArgument(0).getValue().toFloat() = 0 and - fc.getArgument(1).getValue().toFloat() = 0 and - description = "both arguments are equal to zero" - ) - or - functionWithDomainError = getMathVariants("pow") and - ( - upperBound(fc.getArgument(0)) < 0.0 and - upperBound(fc.getArgument(1)) < 0.0 and - description = "both arguments are less than zero" - ) - or - functionWithDomainError = getMathVariants("acosh") and - upperBound(fc.getArgument(0)) < 1.0 and - description = "argument is less than 1" - or - functionWithDomainError = getMathVariants(["ilogb", "logb", "tgamma"]) and - fc.getArgument(0).getValue().toFloat() = 0 and - description = "argument is equal to zero" - or - functionWithDomainError = getMathVariants(["log", "log10", "log2", "sqrt"]) and - upperBound(fc.getArgument(0)) < 0.0 and - description = "argument is negative" - or - functionWithDomainError = getMathVariants("log1p") and - upperBound(fc.getArgument(0)) < -1.0 and - description = "argument is less than 1" - ) +class UncheckedRangeDomainPoleErrorsQuery extends UncheckedRangeDomainPoleErrorsSharedQuery { + UncheckedRangeDomainPoleErrorsQuery() { + this = TypeRangesPackage::uncheckedRangeDomainPoleErrorsQuery() + } } - -/* - * Domain cases not covered by this query: - * - pow - x is finite and negative and y is finite and not an integer value. - * - tgamma - negative integer can't be covered. - * - lrint/llrint/lround/llround - no domain errors checked - * - fmod - no domain errors checked. - * - remainder - no domain errors checked. - * - remquo - no domain errors checked. - * - * Implementations may also define their own domain errors (as per the C99 standard), which are not - * covered by this query. - */ - -from FunctionCall fc, string description -where - not isExcluded(fc, TypeRangesPackage::uncheckedRangeDomainPoleErrorsQuery()) and - hasDomainError(fc, description) -select fc, "Domain error in call to " + fc.getTarget().getName() + ": " + description + "." diff --git a/cpp/autosar/src/rules/A1-1-1/StrstreamTypesAreDeprecated.ql b/cpp/autosar/src/rules/A1-1-1/StrstreamTypesAreDeprecated.ql index ee55364f67..081083c576 100644 --- a/cpp/autosar/src/rules/A1-1-1/StrstreamTypesAreDeprecated.ql +++ b/cpp/autosar/src/rules/A1-1-1/StrstreamTypesAreDeprecated.ql @@ -14,11 +14,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TypeUses -from Class c, Locatable l +from TypeMention l, Class c where not isExcluded(l, ToolchainPackage::strstreamTypesAreDeprecatedQuery()) and c.hasQualifiedName("std", ["strstreambuf", "ostrstream", "istrstream"]) and - exists(Type t | t = c | l = getATypeUse(t)) -select l, "Use of class '" + c.getQualifiedName() + "' is deprecated." + l.getMentionedType() = c +select l, "Use of class '" + c.getName() + "' is deprecated." diff --git a/cpp/autosar/src/rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql b/cpp/autosar/src/rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql index bd98ad9162..1499191236 100644 --- a/cpp/autosar/src/rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql +++ b/cpp/autosar/src/rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql @@ -20,13 +20,57 @@ import codingstandards.cpp.autosar predicate hasResponseFileArgument(Compilation c) { c.getAnArgument().matches("@%") } -predicate hasWarningOption(Compilation c) { c.getAnArgument().regexpMatch("-W[\\w=-]+") } +class CompilationWithNoWarnings extends Compilation { + CompilationWithNoWarnings() { + getAnArgument() = "-w" + or + not exists(EnableWarningFlag enableFlag | + this.getAnArgument() = enableFlag and + not exists(DisableWarningFlag disableFlag | + this.getAnArgument() = disableFlag and + enableFlag.getWarningType() = disableFlag.getWarningType() + ) + ) + } +} + +class CompilationArgument extends string { + Compilation compilation; + + CompilationArgument() { this = compilation.getAnArgument() } +} + +/** + * Compiler flags of type -Wfoo or -Wfoo=bar, which enables the `foo` warning. + */ +class EnableWarningFlag extends CompilationArgument { + string warningType; + + EnableWarningFlag() { + warningType = regexpCapture("^-W([\\w-]+)(=.*)?$", 1) and + not this instanceof DisableWarningFlag + } + + string getWarningType() { result = warningType } +} + +/** + * Compiler flags of type -Wno-foo or -Wfoo=0, which disables the `foo` warning + * and overrules -Wfoo. + */ +class DisableWarningFlag extends CompilationArgument { + string warningType; + + DisableWarningFlag() { + warningType = regexpCapture("^-Wno-([\\w-]+)", 1) or + warningType = regexpCapture("^-W([\\w-]+)=0", 1) + } + + string getWarningType() { result = warningType } +} from File f where not isExcluded(f, ToolchainPackage::compilerWarningLevelNotInComplianceQuery()) and - exists(Compilation c | f = c.getAFileCompiled() | - not hasResponseFileArgument(c) and - not hasWarningOption(c) - ) + exists(CompilationWithNoWarnings c | f = c.getAFileCompiled() | not hasResponseFileArgument(c)) select f, "No warning-level options were used in the compilation of '" + f.getBaseName() + "'." diff --git a/cpp/autosar/src/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.ql b/cpp/autosar/src/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.ql index 1e9c20713c..37807e0f4c 100644 --- a/cpp/autosar/src/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.ql +++ b/cpp/autosar/src/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.ql @@ -26,7 +26,7 @@ where c.getAnArgument() = flag and flag = [ - "-Ofast", "-ffast-math", "-fgnu-keywords", "-fno-signed-zeroes", "-menable-unsafe-fp-math", + "-Ofast", "-ffast-math", "-fgnu-keywords", "-fno-signed-zeros", "-menable-unsafe-fp-math", "-menable-no-nans", "-menable-no-infs", "-menable-unsafe-fp-math", "-ffinite-math-only", "-ffloat-store" ] diff --git a/cpp/autosar/src/rules/A11-0-1/NonPodTypeShouldBeDefinedAsClass.ql b/cpp/autosar/src/rules/A11-0-1/NonPodTypeShouldBeDefinedAsClass.ql index 7867af2fdc..41611c5536 100644 --- a/cpp/autosar/src/rules/A11-0-1/NonPodTypeShouldBeDefinedAsClass.ql +++ b/cpp/autosar/src/rules/A11-0-1/NonPodTypeShouldBeDefinedAsClass.ql @@ -22,5 +22,5 @@ import codingstandards.cpp.Typehelpers from Struct s where not isExcluded(s, ClassesPackage::nonPodTypeShouldBeDefinedAsClassQuery()) and - not s.isPOD() + not s.isPod() select s, "Non-POD type defined as struct instead of class." diff --git a/cpp/autosar/src/rules/A12-0-2/OperationsAssumingMemoryLayoutPerformedOnObjects.ql b/cpp/autosar/src/rules/A12-0-2/OperationsAssumingMemoryLayoutPerformedOnObjects.ql index 865c7189ad..4248b223b0 100644 --- a/cpp/autosar/src/rules/A12-0-2/OperationsAssumingMemoryLayoutPerformedOnObjects.ql +++ b/cpp/autosar/src/rules/A12-0-2/OperationsAssumingMemoryLayoutPerformedOnObjects.ql @@ -19,7 +19,7 @@ import cpp import codingstandards.cpp.autosar class Object extends Class { - Object() { not this.(Struct).isPOD() } + Object() { not this.(Struct).isPod() } } predicate isPointerToObject(Expr e) { diff --git a/cpp/autosar/src/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.ql b/cpp/autosar/src/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.ql index 33906535d3..66fe0345dc 100644 --- a/cpp/autosar/src/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.ql +++ b/cpp/autosar/src/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.ql @@ -16,29 +16,11 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.Constructor +import codingstandards.cpp.rules.initializeallvirtualbaseclasses.InitializeAllVirtualBaseClasses -from Constructor c, Class declaringType, Class baseClass, string type -where - not isExcluded(c, InitializationPackage::explicitConstructorBaseClassInitializationQuery()) and - declaringType = c.getDeclaringType() and - ( - declaringType.getABaseClass() = baseClass and type = "" - or - baseClass.(VirtualBaseClass).getAVirtuallyDerivedClass().getADerivedClass+() = declaringType and - type = " virtual" - ) and - // There is not an initializer on the constructor for this particular base class - not exists(ConstructorBaseClassInit init | - c.getAnInitializer() = init and - init.getInitializedClass() = baseClass and - not init.isCompilerGenerated() - ) and - // Must be a defined constructor - c.isDefined() and - // Not a compiler-generated constructor - not c.isCompilerGenerated() and - // Not a defaulted constructor - not c.isDefaulted() -select c, "Constructor for $@ does not explicitly call constructor for" + type + " base class $@.", - declaringType, declaringType.getSimpleName(), baseClass, baseClass.getSimpleName() +class ExplicitConstructorBaseClassInitializationQuery extends InitializeAllVirtualBaseClassesSharedQuery +{ + ExplicitConstructorBaseClassInitializationQuery() { + this = InitializationPackage::explicitConstructorBaseClassInitializationQuery() + } +} diff --git a/cpp/autosar/src/rules/A12-1-5/InitializerHashCons.qll b/cpp/autosar/src/rules/A12-1-5/InitializerHashCons.qll index b1e1bc03f4..bc63c253fa 100644 --- a/cpp/autosar/src/rules/A12-1-5/InitializerHashCons.qll +++ b/cpp/autosar/src/rules/A12-1-5/InitializerHashCons.qll @@ -891,7 +891,7 @@ private predicate mk_FieldCons( analyzableClassAggregateLiteral(cal) and cal.getUnspecifiedType() = c and exists(Expr e | - e = cal.getFieldExpr(f).getFullyConverted() and + e = cal.getAFieldExpr(f).getFullyConverted() and f.getInitializationOrder() = i and ( hc = hashCons(e) and @@ -907,9 +907,9 @@ private predicate mk_FieldCons( private predicate analyzableClassAggregateLiteral(ClassAggregateLiteral cal) { forall(int i | exists(cal.getChild(i)) | strictcount(cal.getChild(i).getFullyConverted()) = 1 and - strictcount(Field f | cal.getChild(i) = cal.getFieldExpr(f)) = 1 and + strictcount(Field f | cal.getChild(i) = cal.getAFieldExpr(f)) = 1 and strictcount(Field f, int j | - cal.getFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder() + cal.getAFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder() ) = 1 ) } diff --git a/cpp/autosar/src/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.ql b/cpp/autosar/src/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.ql index 4743e0b529..c534f9e591 100644 --- a/cpp/autosar/src/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.ql +++ b/cpp/autosar/src/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.ql @@ -29,9 +29,9 @@ predicate isProtectedNonVirtual(Destructor d) { d.isProtected() and not d.isVirt from Destructor d where not isExcluded(d, VirtualFunctionsPackage::destructorOfABaseClassNotPublicVirtualQuery()) and - isPossibleBaseClass(d.getDeclaringType(), _) and + d.getDeclaringType() instanceof BaseClass and (not isPublicOverride(d) and not isProtectedNonVirtual(d) and not isPublicVirtual(d)) // Report the declaration entry in the class body, as that is where the access specifier should be set select getDeclarationEntryInClassDeclaration(d), - "Destructor of base class " + d.getDeclaringType() + - " is not declared as public virtual, public override, or protected non-virtual." + "Destructor of base class '" + d.getDeclaringType().getQualifiedName() + + "' is not declared as public virtual, public override, or protected non-virtual." diff --git a/cpp/autosar/src/rules/A12-7-1/RedundantMemberFunctionsShouldBeDefaultedOrLeftUndefined.ql b/cpp/autosar/src/rules/A12-7-1/RedundantMemberFunctionsShouldBeDefaultedOrLeftUndefined.ql index 120eb62a31..b066db8b3d 100644 --- a/cpp/autosar/src/rules/A12-7-1/RedundantMemberFunctionsShouldBeDefaultedOrLeftUndefined.ql +++ b/cpp/autosar/src/rules/A12-7-1/RedundantMemberFunctionsShouldBeDefaultedOrLeftUndefined.ql @@ -34,10 +34,12 @@ class NonDefaultedNoExceptUserDefinedSpecialMemberFunction extends UserDefinedSp abstract class RedundantSpecialMemberFunction extends UserDefinedSpecialMemberFunction { } abstract class RedundantNonDefaultNoExceptSpecialMemberFunction extends RedundantSpecialMemberFunction, - NonDefaultedNoExceptUserDefinedSpecialMemberFunction { } + NonDefaultedNoExceptUserDefinedSpecialMemberFunction +{ } class RedundantCopySpecialMemberFunction extends RedundantNonDefaultNoExceptSpecialMemberFunction, - CopyConstructor { + CopyConstructor +{ RedundantCopySpecialMemberFunction() { // a copy function is redundant if // 1) it has a vacuous body @@ -68,7 +70,8 @@ class RedundantCopySpecialMemberFunction extends RedundantNonDefaultNoExceptSpec } class RedundantMoveSpecialMemberFunction extends RedundantNonDefaultNoExceptSpecialMemberFunction, - MoveConstructor { + MoveConstructor +{ RedundantMoveSpecialMemberFunction() { // a move function is redundant if // 1) it has a vacuous body @@ -97,7 +100,8 @@ class RedundantMoveSpecialMemberFunction extends RedundantNonDefaultNoExceptSpec } class RedundantConstructorSpecialMemberFunction extends RedundantNonDefaultNoExceptSpecialMemberFunction, - Constructor { + Constructor +{ RedundantConstructorSpecialMemberFunction() { // 1) The constructor should be not one of the other special types of // constructors @@ -131,7 +135,8 @@ class RedundantDestructorSpecialMemberFunction extends RedundantSpecialMemberFun } class RedundantCopyAssignmentOperatorSpecialMemberFunction extends RedundantNonDefaultNoExceptSpecialMemberFunction, - CopyAssignmentOperator { + CopyAssignmentOperator +{ RedundantCopyAssignmentOperatorSpecialMemberFunction() { // The copy (and move) assignment operator is especially hard to tell if it // is the same behavior as the default constructor. For our purposes we use @@ -167,7 +172,8 @@ class RedundantCopyAssignmentOperatorSpecialMemberFunction extends RedundantNonD } class RedundantMoveAssignmentOperatorSpecialMemberFunction extends RedundantNonDefaultNoExceptSpecialMemberFunction, - MoveAssignmentOperator { + MoveAssignmentOperator +{ RedundantMoveAssignmentOperatorSpecialMemberFunction() { // The move (and copy) assignment operator is especially hard to tell if it // is the same behavior as the default constructor. For our purposes we use diff --git a/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql b/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql index 4996afd34e..a71d49d844 100644 --- a/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql +++ b/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql @@ -16,7 +16,7 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType /** * A literal with no values. diff --git a/cpp/autosar/src/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.ql b/cpp/autosar/src/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.ql index a2ce643784..9697176711 100644 --- a/cpp/autosar/src/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.ql +++ b/cpp/autosar/src/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.ql @@ -17,43 +17,11 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.Operator +import codingstandards.cpp.rules.copyandmoveassignmentsshallhandleselfassignment.CopyAndMoveAssignmentsShallHandleSelfAssignment -predicate isUserCopyOrUserMove(Operator o) { - o instanceof UserCopyOperator or - o instanceof UserMoveOperator +class CopyAssignmentAndAMoveHandleSelfAssignmentQuery extends CopyAndMoveAssignmentsShallHandleSelfAssignmentSharedQuery +{ + CopyAssignmentAndAMoveHandleSelfAssignmentQuery() { + this = OperatorInvariantsPackage::copyAssignmentAndAMoveHandleSelfAssignmentQuery() + } } - -predicate callsStdSwap(Function f) { - exists(FunctionCall fc | - fc.getTarget().hasGlobalOrStdName("swap") and - fc.getEnclosingFunction() = f - ) -} - -predicate callsNoExceptSwap(Operator o) { - exists(Function f, FunctionCall fc | - callsStdSwap(f) and - fc.getEnclosingFunction() = o and - fc.getTarget() = f - ) -} - -predicate checksForSelfAssignment(Operator o) { - exists(IfStmt i, ComparisonOperation c | - i.getEnclosingFunction() = o and - i.getCondition() = c and - ( - c.getLeftOperand().toString() = "this" or - c.getRightOperand().toString() = "this" - ) - ) -} - -from Operator o -where - not isExcluded(o, OperatorInvariantsPackage::copyAssignmentAndAMoveHandleSelfAssignmentQuery()) and - isUserCopyOrUserMove(o) and - not callsNoExceptSwap(o) and - not checksForSelfAssignment(o) -select o, "User defined copy or user defined move does not handle self-assignment correctly." diff --git a/cpp/autosar/src/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.ql b/cpp/autosar/src/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.ql index 0098c7e43f..7507eb1d7c 100644 --- a/cpp/autosar/src/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.ql +++ b/cpp/autosar/src/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.ql @@ -16,35 +16,57 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.Class -predicate isInvalidConstructor(Constructor f, string constructorType) { +predicate isInvalidConstructor(Constructor f, string constructorType, string missingAction) { not f.isDeleted() and not f.isProtected() and ( - f instanceof MoveConstructor and constructorType = "Move constructor" + f instanceof MoveConstructor and + if f.isCompilerGenerated() + then constructorType = "Implicit move constructor" and missingAction = "deleted" + else ( + constructorType = "Move constructor" and missingAction = "protected" + ) or - f instanceof CopyConstructor and constructorType = "Copy constructor" + f instanceof CopyConstructor and + if f.isCompilerGenerated() + then constructorType = "Implicit copy constructor" and missingAction = "deleted" + else ( + constructorType = "Copy constructor" and missingAction = "protected" + ) ) } -predicate isInvalidAssignment(Operator f, string operatorType) { +predicate isInvalidAssignment(Operator f, string operatorType, string missingAction) { not f.isDeleted() and ( - f instanceof CopyAssignmentOperator and operatorType = "Copy assignment operator" + f instanceof MoveAssignmentOperator and + if f.isCompilerGenerated() + then operatorType = "Implicit move assignment operator" and missingAction = "deleted" + else ( + operatorType = "Move assignment operator" and missingAction = "protected" + ) or - f instanceof MoveAssignmentOperator and operatorType = "Move assignment operator" + f instanceof CopyAssignmentOperator and + if f.isCompilerGenerated() + then operatorType = "Implicit copy assignment operator" and missingAction = "deleted" + else ( + operatorType = "Copy assignment operator" and missingAction = "protected" + ) ) and not f.hasSpecifier("protected") } -from MemberFunction mf, string type, string baseReason +from BaseClass baseClass, MemberFunction mf, string type, string missingAction where not isExcluded(mf, OperatorsPackage::copyAndMoveNotDeclaredProtectedQuery()) and ( - isInvalidConstructor(mf, type) + isInvalidConstructor(mf, type, missingAction) or - isInvalidAssignment(mf, type) + isInvalidAssignment(mf, type, missingAction) ) and - isPossibleBaseClass(mf.getDeclaringType(), baseReason) + baseClass = mf.getDeclaringType() +// To avoid duplicate alerts due to inaccurate location information in the database we don't use the location of the base class. +// This for example happens if multiple copies of the same header file are present in the database. select getDeclarationEntryInClassDeclaration(mf), - type + " for base class " + mf.getDeclaringType().getQualifiedName() + " (" + baseReason + - ") is not declared protected or deleted." + type + " for base class '" + baseClass.getQualifiedName() + "' is not declared " + missingAction + + "." diff --git a/cpp/autosar/src/rules/A13-1-2/UserDefinedLiteralOperatorSuffixViolation.ql b/cpp/autosar/src/rules/A13-1-2/UserDefinedLiteralOperatorSuffixViolation.ql index 7fe8bcdbe7..c739035596 100644 --- a/cpp/autosar/src/rules/A13-1-2/UserDefinedLiteralOperatorSuffixViolation.ql +++ b/cpp/autosar/src/rules/A13-1-2/UserDefinedLiteralOperatorSuffixViolation.ql @@ -15,9 +15,9 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.UserDefinedLiteral +import codingstandards.cpp.UserDefinedLiteral as udl -from UserDefinedLiteral udl +from udl::UserDefinedLiteral udl where not isExcluded(udl, NamingPackage::userDefinedLiteralOperatorSuffixViolationQuery()) and not udl.hasCompliantSuffix() diff --git a/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallNotHaveSideEffects.ql b/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallNotHaveSideEffects.ql index 0cbb9f101e..b41a57f900 100644 --- a/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallNotHaveSideEffects.ql +++ b/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallNotHaveSideEffects.ql @@ -14,11 +14,11 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.UserDefinedLiteral +import codingstandards.cpp.UserDefinedLiteral as udl import codingstandards.cpp.SideEffect import codingstandards.cpp.sideeffect.DefaultEffects -from UserDefinedLiteral udl, SideEffect e +from udl::UserDefinedLiteral udl, SideEffect e where not isExcluded(udl, SideEffects2Package::userDefinedLiteralsOperatorsShallNotHaveSideEffectsQuery()) and diff --git a/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql b/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql index 8d8a3a79c7..4593065e01 100644 --- a/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql +++ b/cpp/autosar/src/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql @@ -16,10 +16,10 @@ import cpp import semmle.code.cpp.dataflow.TaintTracking import codingstandards.cpp.autosar -import codingstandards.cpp.UserDefinedLiteral +import codingstandards.cpp.UserDefinedLiteral as udl import codingstandards.cpp.SideEffect -from UserDefinedLiteral udl, Expr retExpr +from udl::UserDefinedLiteral udl, Expr retExpr where not isExcluded(udl, SideEffects2Package::userDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParametersQuery()) and diff --git a/cpp/autosar/src/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.ql b/cpp/autosar/src/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.ql index 8651118b49..c6c2e54378 100644 --- a/cpp/autosar/src/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.ql +++ b/cpp/autosar/src/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.ql @@ -17,6 +17,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.Operator +import semmle.code.cpp.Print from Operator o where @@ -30,5 +31,4 @@ where o.getType() instanceof ReferenceType ) select o, - "User-defined bitwise or arithmetic operator " + o.getFullSignature() + - " does not return a prvalue." + "User-defined bitwise or arithmetic operator " + o.toString() + " does not return a prvalue." diff --git a/cpp/autosar/src/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql b/cpp/autosar/src/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql index 7562082656..7b31ae5d9e 100644 --- a/cpp/autosar/src/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql +++ b/cpp/autosar/src/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.ql @@ -14,17 +14,35 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.FunctionEquivalence class Candidate extends TemplateFunction { Candidate() { - this.getAParameter().getType().(RValueReferenceType).getBaseType() instanceof TemplateParameter + this.getAParameter().getType().(RValueReferenceType).getBaseType() instanceof + TypeTemplateParameter } } -from Candidate c, Function f +from + Candidate c, Function f, Function overload, Function overloaded, string msg, + string firstMsgSegment where not isExcluded(f, OperatorsPackage::functionThatContainsForwardingReferenceAsItsArgumentOverloadedQuery()) and not f.isDeleted() and - f = c.getAnOverload() -select f, "Function overloads a $@ with a forwarding reference parameter.", c, "function" + f = c.getAnOverload() and + // Ensure the functions are not equivalent to each other (refer #796). + not f = getAnEquivalentFunction(c) and + // allow for overloading with different number of parameters, because there is no + // confusion on what function will be called. + f.getNumberOfParameters() = c.getNumberOfParameters() and + //ignore implicit copy and move constructor overloads + not ( + f.isCompilerGenerated() and + (f instanceof CopyConstructor or f instanceof MoveConstructor) + ) and + msg = "function with a forwarding reference parameter" and + firstMsgSegment = " " and + overloaded = c and + overload = f +select overload, "Function" + firstMsgSegment + "overloads a $@.", overloaded, msg diff --git a/cpp/autosar/src/rules/A13-5-2/UserDefinedConversionOperatorsNotDefinedExplicit.ql b/cpp/autosar/src/rules/A13-5-2/UserDefinedConversionOperatorsNotDefinedExplicit.ql index 5e83d02baa..ced94c5bdd 100644 --- a/cpp/autosar/src/rules/A13-5-2/UserDefinedConversionOperatorsNotDefinedExplicit.ql +++ b/cpp/autosar/src/rules/A13-5-2/UserDefinedConversionOperatorsNotDefinedExplicit.ql @@ -27,5 +27,6 @@ class ExplicitConversionOperator extends ConversionOperator { from ConversionOperator op where not isExcluded(op, OperatorsPackage::userDefinedConversionOperatorsNotDefinedExplicitQuery()) and - not op instanceof ExplicitConversionOperator + not op instanceof ExplicitConversionOperator and + not op.isCompilerGenerated() select op, "User-defined conversion operator is not explicit." diff --git a/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql b/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql index 4a81e32b0f..a8fb1ace66 100644 --- a/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql +++ b/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql @@ -15,10 +15,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TypeUses +import codingstandards.cpp.types.Uses import codingstandards.cpp.Operator -predicate templateDefinitionMentionsTypeParameter(Declaration d, TemplateParameter tp) { +predicate templateDefinitionMentionsTypeParameter(Declaration d, TypeTemplateParameter tp) { exists(Type t | ( // direct reference, e.g., fields. @@ -50,36 +50,36 @@ predicate templateDefinitionMentionsTypeParameter(Declaration d, TemplateParamet } /** - * The set of `TemplateParameter` references within an `Enum`. + * The set of `TypeTemplateParameter` references within an `Enum`. */ -TemplateParameter enumTemplateReferences(Enum e) { +TypeTemplateParameter enumTemplateReferences(Enum e) { templateDefinitionMentionsTypeParameter(e.getADeclaration(), result) or result = e.getExplicitUnderlyingType() } /** - * The set of `TemplateParameter` references within an `Class`. + * The set of `TypeTemplateParameter` references within an `Class`. */ -TemplateParameter classTemplateReferences(Class c) { +TypeTemplateParameter classTemplateReferences(Class c) { templateDefinitionMentionsTypeParameter(c.getAMember(), result) or c.getADerivation().getBaseType() = result } /** - * The set of all of the `TemplateParameter`s referenced by a `EnumConstant`. + * The set of all of the `TypeTemplateParameter`s referenced by a `EnumConstant`. */ -TemplateParameter enumConstantTemplateReferences(EnumConstant ec) { +TypeTemplateParameter enumConstantTemplateReferences(EnumConstant ec) { templateDefinitionMentionsTypeParameter(ec.getDeclaringType(), result) } /** - * The set of all `TemplateParameter`s referenced by a `Function`. + * The set of all `TypeTemplateParameter`s referenced by a `Function`. */ -TemplateParameter functionTemplateReferences(Function mf) { +TypeTemplateParameter functionTemplateReferences(Function mf) { // the type of the function - exists(TemplateParameter tp | + exists(TypeTemplateParameter tp | result = tp and ( mf.getType().refersTo(result) @@ -115,10 +115,10 @@ TemplateParameter functionTemplateReferences(Function mf) { } /** - * The set of all `TemplateParameters` available as arguments to the declaring + * The set of all `TypeTemplateParameters` available as arguments to the declaring * element of some `Declarations`. */ -TemplateParameter templateParametersOfDeclaringTemplateClass(Declaration d) { +TypeTemplateParameter templateParametersOfDeclaringTemplateClass(Declaration d) { result = d.getDeclaringType().getATemplateArgument() } @@ -149,7 +149,7 @@ where not d instanceof UserNegationOperator and // for each declaration within a template class get the // template parameters of the declaring class - not exists(TemplateParameter t | + not exists(TypeTemplateParameter t | t = templateParametersOfDeclaringTemplateClass(d) and // and require that the declaration depends on at least // one of those template parameters. @@ -167,7 +167,10 @@ where mf = c.getAMemberFunction() and not mf.isCompilerGenerated() and not exists(mf.getBlock()) ) ) - ) + ) and + // Omit using alias (cf. https://github.com/github/codeql-coding-standards/issues/739) + // Exclude Using alias which refer directly to a TypeParameter + not d.(UsingAliasTypedefType).getBaseType() instanceof TypeTemplateParameter select d, "Member " + d.getName() + " template class does not use any of template arguments of its $@.", d.getDeclaringType(), "declaring type" diff --git a/cpp/autosar/src/rules/A14-5-3/NonMemberGenericOperatorCondition.ql b/cpp/autosar/src/rules/A14-5-3/NonMemberGenericOperatorCondition.ql index a2211368ed..c2d28d3ef9 100644 --- a/cpp/autosar/src/rules/A14-5-3/NonMemberGenericOperatorCondition.ql +++ b/cpp/autosar/src/rules/A14-5-3/NonMemberGenericOperatorCondition.ql @@ -18,7 +18,7 @@ import codingstandards.cpp.autosar class NonMemberGenericOperator extends TemplateFunction { NonMemberGenericOperator() { this instanceof Operator and - exists(TemplateParameter tp, Type pType | + exists(TypeTemplateParameter tp, Type pType | pType = getAParameter().getType().getUnspecifiedType() //Parameter Type | pType = tp or diff --git a/cpp/autosar/src/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.ql b/cpp/autosar/src/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.ql index b583c9cc14..94214e8992 100644 --- a/cpp/autosar/src/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.ql +++ b/cpp/autosar/src/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.ql @@ -58,4 +58,4 @@ where not spec.getFile() = spec.getPrimary().getFile() and not extraExclude(spec) select spec, "Specialization found in file $@ where primary template is outside that file.", - spec.getFile(), spec.getFile().getRelativePath() + spec.getFile(), spec.getFile().getBaseName() diff --git a/cpp/autosar/src/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.ql b/cpp/autosar/src/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.ql index e53b532493..86218a47d6 100644 --- a/cpp/autosar/src/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.ql +++ b/cpp/autosar/src/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.ql @@ -16,8 +16,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.functiontemplatesexplicitlyspecialized.FunctionTemplatesExplicitlySpecialized -from FunctionTemplateSpecialization f -where not isExcluded(f, TemplatesPackage::explicitSpecializationsOfFunctionTemplatesUsedQuery()) -select f, "Specialization of function template from primary template located in $@.", - f.getPrimaryTemplate(), f.getPrimaryTemplate().getFile().getBaseName() +class ExplicitSpecializationsOfFunctionTemplatesUsedQuery extends FunctionTemplatesExplicitlySpecializedSharedQuery +{ + ExplicitSpecializationsOfFunctionTemplatesUsedQuery() { + this = TemplatesPackage::explicitSpecializationsOfFunctionTemplatesUsedQuery() + } +} diff --git a/cpp/autosar/src/rules/A15-1-2/PointerExceptionObject.ql b/cpp/autosar/src/rules/A15-1-2/PointerExceptionObject.ql index 348e02609c..b2f101082f 100644 --- a/cpp/autosar/src/rules/A15-1-2/PointerExceptionObject.ql +++ b/cpp/autosar/src/rules/A15-1-2/PointerExceptionObject.ql @@ -15,10 +15,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.exceptionobjecthavepointertype.ExceptionObjectHavePointerType -from Expr thrownExpr -where - not isExcluded(thrownExpr, Exceptions1Package::pointerExceptionObjectQuery()) and - thrownExpr = any(ThrowExpr te).getExpr() and - thrownExpr.getType().getUnspecifiedType() instanceof PointerType -select thrownExpr, "Exception object with pointer type " + thrownExpr.getType() + " is thrown here." +class PointerExceptionObjectQuery extends ExceptionObjectHavePointerTypeSharedQuery { + PointerExceptionObjectQuery() { this = Exceptions1Package::pointerExceptionObjectQuery() } +} diff --git a/cpp/autosar/src/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.ql b/cpp/autosar/src/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.ql index 5fbd09d522..1b3a3cfed2 100644 --- a/cpp/autosar/src/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.ql +++ b/cpp/autosar/src/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.ql @@ -53,10 +53,13 @@ class NewWrapperFunction extends Function { /** An expression on which `delete` is called, directly or indirectly. */ class DeletedExpr extends Expr { + pragma[noinline, nomagic] DeletedExpr() { - this = any(DeleteExpr deleteExpr).getExpr() or + this = any(DeleteExpr deleteExpr).getExpr() + or exists(DeleteWrapperFunction dwf, FunctionCall call | - this = call.getArgument(dwf.getADeleteParameter().getIndex()) + this = call.getArgument(dwf.getADeleteParameter().getIndex()) and + call.getTarget() = dwf ) } } @@ -75,29 +78,40 @@ class DeleteWrapperFunction extends Function { Parameter getADeleteParameter() { result = p } } +class ExceptionThrowingConstructor extends ExceptionThrowingFunction, Constructor { + ExceptionThrowingConstructor() { + exists(getAFunctionThrownType(this, _)) and + // The constructor is within the users source code + exists(getFile().getRelativePath()) + } +} + class ExceptionThrownInConstructor extends ExceptionThrowingExpr { Constructor c; - ExceptionThrownInConstructor() { exists(getAFunctionThrownType(c, this)) } + ExceptionThrownInConstructor() { + exists(getAFunctionThrownType(c, this)) and + // The constructor is within the users source code + exists(c.getFile().getRelativePath()) + } Constructor getConstructor() { result = c } } -query predicate nodes(ExceptionFlowNode node) { any() } - from - Constructor c, ExceptionThrownInConstructor throwingExpr, NewAllocationExpr newExpr, - ExceptionFlowNode exceptionSource, ExceptionFlowNode functionNode + ExceptionThrowingConstructor c, ExceptionThrownInConstructor throwingExpr, + NewAllocationExpr newExpr, ExceptionFlowNode exceptionSource, + ExceptionFlowNode throwingExprFlowNode, ExceptionFlowNode reportingNode where not isExcluded(c, Exceptions2Package::constructorErrorLeavesObjectInInvalidStateQuery()) and not isNoExceptTrue(c) and // Constructor must exit with an exception c = throwingExpr.getConstructor() and - throwingExpr.hasExceptionFlowReflexive(exceptionSource, functionNode, _) and + throwingExpr.hasExceptionFlowReflexive(exceptionSource, throwingExprFlowNode, _) and exists(ExceptionFlowNode mid | edges*(exceptionSource, mid) and newExpr.getASuccessor+() = mid.asThrowingExpr() and - edges*(mid, functionNode) and + edges*(mid, throwingExprFlowNode) and not exists(ExceptionFlowNode prior | edges(prior, mid) | prior.asCatchBlock().getEnclosingFunction() = c ) @@ -116,7 +130,16 @@ where DataFlow::localFlow(DataFlow::exprNode(newExpr), DataFlow::exprNode(deletedExpr)) and newExpr.getASuccessor+() = deletedExpr and deletedExpr.getASuccessor+() = throwingExpr - ) -select c, exceptionSource, functionNode, "Constructor throws $@ and allocates memory at $@", + ) and + // In CodeQL CLI 2.12.7 there is a bug which causes an infinite loop during results interpretation + // when a result includes more than maxPaths paths and also includes a path with no edges i.e. + // where the source and sink node are the same. + // To avoid this edge case, if we report a path where the source and sink are the same (i.e the + // throwingExpr directly throws an exception), we adjust the sink node to report the constructor, + // which creates a one step path from the throwingExprFlowNode to the constructor node. + if throwingExprFlowNode = exceptionSource + then reportingNode.asFunction() = c and edges(throwingExprFlowNode, reportingNode) + else reportingNode = throwingExprFlowNode +select c, exceptionSource, reportingNode, "Constructor throws $@ and allocates memory at $@", throwingExpr, throwingExpr.(ThrowingExpr).getAnExceptionType().getExceptionName(), newExpr, "alloc" diff --git a/cpp/autosar/src/rules/A15-3-3/MissingCatchHandlerInMain.ql b/cpp/autosar/src/rules/A15-3-3/MissingCatchHandlerInMain.ql index e6bf2b99e2..ce3a10f31d 100644 --- a/cpp/autosar/src/rules/A15-3-3/MissingCatchHandlerInMain.ql +++ b/cpp/autosar/src/rules/A15-3-3/MissingCatchHandlerInMain.ql @@ -22,24 +22,6 @@ import codingstandards.cpp.exceptions.ThirdPartyExceptions import codingstandards.cpp.standardlibrary.Exceptions import codingstandards.cpp.EncapsulatingFunctions -/** A `TryStmt` which covers the full body of a function. */ -class FullFunctionBodyTryStmt extends TryStmt { - FullFunctionBodyTryStmt() { - this instanceof FunctionTryStmt - or - exists(Function f, BlockStmt functionBlock | - functionBlock = f.getBlock() and - this = functionBlock.getStmt(0) and - ( - functionBlock.getNumStmt() = 1 - or - functionBlock.getNumStmt() = 2 and - functionBlock.getStmt(1) instanceof ReturnStmt - ) - ) - } -} - /* * The strategy for this query is to find a Stmt in the root BlockStmt which can throw one of the * ExceptionTypes that should be handled. diff --git a/cpp/autosar/src/rules/A15-3-5/ClassTypeExceptionNotCaughtByReference.ql b/cpp/autosar/src/rules/A15-3-5/ClassTypeExceptionNotCaughtByReference.ql index 4bb8aba62b..c0f4dbb2fe 100644 --- a/cpp/autosar/src/rules/A15-3-5/ClassTypeExceptionNotCaughtByReference.ql +++ b/cpp/autosar/src/rules/A15-3-5/ClassTypeExceptionNotCaughtByReference.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.catchexceptionsbylvaluereference.CatchExceptionsByLvalueReference -class ClassTypeExceptionNotCaughtByReference extends CatchExceptionsByLValueReferenceSharedQuery { +class ClassTypeExceptionNotCaughtByReference extends CatchExceptionsByLvalueReferenceSharedQuery { ClassTypeExceptionNotCaughtByReference() { this = Exceptions2Package::classTypeExceptionNotCaughtByReferenceQuery() } diff --git a/cpp/autosar/src/rules/A15-4-2/NoExceptFunctionThrows.ql b/cpp/autosar/src/rules/A15-4-2/NoExceptFunctionThrows.ql index 0c5bbb6011..169b5fc8f3 100644 --- a/cpp/autosar/src/rules/A15-4-2/NoExceptFunctionThrows.ql +++ b/cpp/autosar/src/rules/A15-4-2/NoExceptFunctionThrows.ql @@ -15,25 +15,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.exceptions.ExceptionFlow -import ExceptionPathGraph -import codingstandards.cpp.exceptions.ExceptionSpecifications +import codingstandards.cpp.rules.noexceptfunctionshouldnotpropagatetothecaller.NoexceptFunctionShouldNotPropagateToTheCaller -class NoExceptThrowingFunction extends ExceptionThrowingFunction { - NoExceptThrowingFunction() { - // Can exit with an exception - exists(getAFunctionThrownType(_, _)) and - // But is marked noexcept(true) or equivalent - isNoExceptTrue(this) - } +class NoExceptFunctionThrowsQuery extends NoexceptFunctionShouldNotPropagateToTheCallerSharedQuery { + NoExceptFunctionThrowsQuery() { this = Exceptions1Package::noExceptFunctionThrowsQuery() } } - -from - NoExceptThrowingFunction f, ExceptionFlowNode exceptionSource, ExceptionFlowNode functionNode, - ExceptionType exceptionType -where - not isExcluded(f, Exceptions1Package::noExceptFunctionThrowsQuery()) and - f.hasExceptionFlow(exceptionSource, functionNode, exceptionType) -select f, exceptionSource, functionNode, - "Function " + f.getName() + " is declared noexcept(true) but can throw exceptions of type " + - exceptionType.getExceptionName() + "." diff --git a/cpp/autosar/src/rules/A15-4-4/MissingNoExcept.ql b/cpp/autosar/src/rules/A15-4-4/MissingNoExcept.ql index 1857003826..058a77175f 100644 --- a/cpp/autosar/src/rules/A15-4-4/MissingNoExcept.ql +++ b/cpp/autosar/src/rules/A15-4-4/MissingNoExcept.ql @@ -19,17 +19,57 @@ import codingstandards.cpp.autosar import codingstandards.cpp.exceptions.ExceptionSpecifications import codingstandards.cpp.exceptions.ExceptionFlow +// These functions have a noexcept specification that could not be resolved +// to noexcept(true). So either, they are noexcept(false) functions which +// means, they can throw an exception OR they have an expression which +// could not be resolved to "true" or "false". Even in this case, lets +// be more conservative and assume they may thrown an exception. +class FunctionWithUnknownNoExcept extends Function { + FunctionWithUnknownNoExcept() { + // Exists a noexcept specification but not noexcept(true) + exists(this.getADeclarationEntry().getNoExceptExpr()) and + not isNoExceptTrue(this) + } +} + +// This predicate checks if a function can call to other functions +// that may have a noexcept specification which cannot be resolved to +// noexcept(true). +predicate mayCallThrowingFunctions(Function f) { + // Exists a call in this function + exists(Call fc | + fc.getEnclosingFunction() = f and + ( + // Either this call is to a function with an unknown noexcept OR + fc.getTarget() instanceof FunctionWithUnknownNoExcept + or + // That function can further have calls to unknown noexcept functions. + mayCallThrowingFunctions(fc.getTarget()) + ) + ) +} + from Function f where - not isExcluded(f, Exceptions1Package::missingNoExceptQuery()) and + not isExcluded(f.getADeclarationEntry(), Exceptions1Package::missingNoExceptQuery()) and // No thrown exceptions not exists(getAFunctionThrownType(f, _)) and // But not marked noexcept(true) not isNoExceptTrue(f) and // Not explicitly marked noexcept(false) not isNoExceptExplicitlyFalse(f) and + // Not having a noexcept specification that + // could not be computed as true or false above. + not exists(f.getADeclarationEntry().getNoExceptExpr()) and + // Not calling function(s) which have a noexcept specification that + // could not be computed as true. + not mayCallThrowingFunctions(f) and // Not compiler generated not f.isCompilerGenerated() and // The function is defined in this database - f.hasDefinition() -select f, "Function " + f.getName() + " could be declared noexcept(true)." + f.hasDefinition() and + // This function is not an overriden call operator of a lambda expression + not exists(LambdaExpression lambda | lambda.getLambdaFunction() = f) and + // Exclude results from uinstantiated templates + not f.isFromUninstantiatedTemplate(_) +select f, "Function " + f.getQualifiedName() + " could be declared noexcept(true)." diff --git a/cpp/autosar/src/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.ql b/cpp/autosar/src/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.ql index ae61049b6f..77fdd88670 100644 --- a/cpp/autosar/src/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.ql +++ b/cpp/autosar/src/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.ql @@ -22,15 +22,25 @@ import codingstandards.cpp.exceptions.ExceptionSpecifications from SpecialFunction f, string message where not isExcluded(f, Exceptions2Package::specialFunctionMissingNoExceptSpecificationQuery()) and - not isNoExceptTrue(f) and + not isFDENoExceptTrue(f.getDefinition()) and not f.isCompilerGenerated() and not f.isDeleted() and not f.isDefaulted() and ( isNoExceptExplicitlyFalse(f) and - message = f.getQualifiedName() + " should not be noexcept(false)." + message = + "Special function " + f.getQualifiedName() + + " has a noexcept(false) specification that permits exceptions." or + isNoExceptTrue(f) and + message = + f.getQualifiedName() + + " has an implicit noexcept(true) specification but should make that explicit." + or + not isNoExceptTrue(f) and not isNoExceptExplicitlyFalse(f) and - message = f.getQualifiedName() + " is implicitly noexcept(false) and might throw." + message = + "Special function " + f.getQualifiedName() + + " has an implicit noexcept(false) specification that permits exceptions." ) select f, message diff --git a/cpp/autosar/src/rules/A15-5-3/ConditionVariablePostConditionFailedAutosar.ql b/cpp/autosar/src/rules/A15-5-3/ConditionVariablePostConditionFailedAutosar.ql index 3a06dedc19..7d80bd9903 100644 --- a/cpp/autosar/src/rules/A15-5-3/ConditionVariablePostConditionFailedAutosar.ql +++ b/cpp/autosar/src/rules/A15-5-3/ConditionVariablePostConditionFailedAutosar.ql @@ -18,7 +18,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.conditionvariablepostconditionfailed.ConditionVariablePostConditionFailed -class ConditionVariablePostConditionFailedAutosarQuery extends ConditionVariablePostConditionFailedSharedQuery { +class ConditionVariablePostConditionFailedAutosarQuery extends ConditionVariablePostConditionFailedSharedQuery +{ ConditionVariablePostConditionFailedAutosarQuery() { this = Exceptions1Package::conditionVariablePostConditionFailedAutosarQuery() } diff --git a/cpp/autosar/src/rules/A15-5-3/JoinableThreadCopiedOrDestroyedAutosar.ql b/cpp/autosar/src/rules/A15-5-3/JoinableThreadCopiedOrDestroyedAutosar.ql index 77403c05ee..476f4f15ff 100644 --- a/cpp/autosar/src/rules/A15-5-3/JoinableThreadCopiedOrDestroyedAutosar.ql +++ b/cpp/autosar/src/rules/A15-5-3/JoinableThreadCopiedOrDestroyedAutosar.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.joinablethreadcopiedordestroyed.JoinableThreadCopiedOrDestroyed -class JoinableThreadCopiedOrDestroyedAutosarQuery extends JoinableThreadCopiedOrDestroyedSharedQuery { +class JoinableThreadCopiedOrDestroyedAutosarQuery extends JoinableThreadCopiedOrDestroyedSharedQuery +{ JoinableThreadCopiedOrDestroyedAutosarQuery() { this = Exceptions1Package::joinableThreadCopiedOrDestroyedAutosarQuery() } diff --git a/cpp/autosar/src/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.ql b/cpp/autosar/src/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.ql index 75c65e6bcd..6a4182d538 100644 --- a/cpp/autosar/src/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.ql +++ b/cpp/autosar/src/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.ql @@ -21,13 +21,26 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.FunctionLikeMacro +class PermittedInnerDirectiveType extends PreprocessorDirective { + PermittedInnerDirectiveType() { + //permissive listing for directives that can be used in a valid wrapper + this instanceof MacroWrapper or + this instanceof PreprocessorEndif or + this instanceof Include or + this instanceof PermittedMacro or + this instanceof PreprocessorElif or + this instanceof PreprocessorElse + } +} + class PermittedDirectiveType extends PreprocessorDirective { PermittedDirectiveType() { //permissive listing in case directive types modelled in ql ever expands (example non valid directives) this instanceof MacroWrapper or this instanceof PreprocessorEndif or this instanceof Include or - this instanceof PermittedMacro + this instanceof PermittedMacro or + this instanceof PreprocessorElse } } @@ -40,9 +53,9 @@ pragma[noinline] predicate isPreprocConditionalRange( PreprocessorBranch pb, string filepath, int startLine, int endLine ) { - exists(PreprocessorEndif end | pb.getEndIf() = end | - isPreprocFileAndLine(pb, filepath, startLine) and - isPreprocFileAndLine(end, filepath, endLine) + isPreprocFileAndLine(pb, filepath, startLine) and + exists(PreprocessorDirective end | + pb.getNext() = end and isPreprocFileAndLine(end, filepath, endLine) ) } @@ -73,18 +86,18 @@ class MacroWrapper extends PreprocessorIfndef { class AcceptableWrapper extends PreprocessorBranch { AcceptableWrapper() { forall(Element inner | not inner instanceof Comment and this = getAGuard(inner) | - inner instanceof PermittedDirectiveType + inner instanceof PermittedInnerDirectiveType ) } } from PreprocessorDirective directive, string message where - ( - not directive instanceof PermittedDirectiveType and - not directive instanceof AcceptableWrapper and - message = "Preprocessor directive used for conditional compilation." - ) and + //special exception case - pragmas already reported by A16-7-1 + not directive instanceof PreprocessorPragma and + not directive instanceof PermittedDirectiveType and + not directive instanceof AcceptableWrapper and + message = "Preprocessor directive used for conditional compilation." and not isExcluded(directive, MacrosPackage::preProcessorShallOnlyBeUsedForCertainDirectivesPatternsQuery()) select directive, message diff --git a/cpp/autosar/src/rules/A16-2-1/CharactersOccurInHeaderFileNameOrInIncludeDirective.ql b/cpp/autosar/src/rules/A16-2-1/CharactersOccurInHeaderFileNameOrInIncludeDirective.ql index 9892f49b00..18a373e935 100644 --- a/cpp/autosar/src/rules/A16-2-1/CharactersOccurInHeaderFileNameOrInIncludeDirective.ql +++ b/cpp/autosar/src/rules/A16-2-1/CharactersOccurInHeaderFileNameOrInIncludeDirective.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.preprocessorincludesforbiddenheadernames.PreprocessorIncludesForbiddenHeaderNames -class CharactersOccurInHeaderFileNameOrInIncludeDirectiveQuery extends PreprocessorIncludesForbiddenHeaderNamesQuery { +class CharactersOccurInHeaderFileNameOrInIncludeDirectiveQuery extends PreprocessorIncludesForbiddenHeaderNamesSharedQuery +{ CharactersOccurInHeaderFileNameOrInIncludeDirectiveQuery() { this = MacrosPackage::charactersOccurInHeaderFileNameOrInIncludeDirectiveQuery() } diff --git a/cpp/autosar/src/rules/A16-2-2/PreprocBlock.qll b/cpp/autosar/src/rules/A16-2-2/PreprocBlock.qll index f684b5d954..d83e33147c 100644 --- a/cpp/autosar/src/rules/A16-2-2/PreprocBlock.qll +++ b/cpp/autosar/src/rules/A16-2-2/PreprocBlock.qll @@ -43,7 +43,7 @@ class PreprocessorBlock extends @element { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [LGTM locations](https://lgtm.com/help/ql/locations). + * [CodeQL locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/autosar/src/rules/A16-2-2/UnusedIncludeDirectives.ql b/cpp/autosar/src/rules/A16-2-2/UnusedIncludeDirectives.ql index 9b536b78b3..ce51602fd2 100644 --- a/cpp/autosar/src/rules/A16-2-2/UnusedIncludeDirectives.ql +++ b/cpp/autosar/src/rules/A16-2-2/UnusedIncludeDirectives.ql @@ -223,10 +223,19 @@ private predicate firstReliableProvide(File f, File g, int line) { cached predicate mayProvideFirst(IncludeDepends i, File g) { - // i may provide g and does not come after a reliable include of g. + // i may provide g i.provides(g) and - not exists(int line | firstReliableProvide(i.getFile(), g, line) | - line < i.getLocation().getStartLine() + ( + // and does not come after a reliable include of g. + not exists(int line | firstReliableProvide(i.getFile(), g, line) | + line < i.getLocation().getStartLine() + ) + or + // or it comes after a reliable include of g, and although redundant, + // is not necessarily an issue e.g. in the case of libraries with + // public header forwards to an internal header. + // therefore, hold for transitive includes as well to exclude those results. + not i.getIncludedFile() = g ) } diff --git a/cpp/autosar/src/rules/A17-1-1/CStandardLibraryFunctionCalls.ql b/cpp/autosar/src/rules/A17-1-1/CStandardLibraryFunctionCalls.ql index 77d065f4e5..ce374fa8b1 100644 --- a/cpp/autosar/src/rules/A17-1-1/CStandardLibraryFunctionCalls.ql +++ b/cpp/autosar/src/rules/A17-1-1/CStandardLibraryFunctionCalls.ql @@ -17,6 +17,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.Naming +import codingstandards.cpp.StdNamespace FunctionCall nonCompliantCStdlibCalls(File f) { result = @@ -55,7 +56,7 @@ FunctionCall nonCompliantCStdlibCalls(File f) { nq = fc.getNameQualifier() and ( nq.getQualifyingElement() instanceof GlobalNamespace or - nq.getQualifyingElement() instanceof StdNamespace + nq.getQualifyingElement() instanceof StdNS ) ) ) diff --git a/cpp/autosar/src/rules/A17-6-1/NonStandardEntitiesInStandardNamespaces.ql b/cpp/autosar/src/rules/A17-6-1/NonStandardEntitiesInStandardNamespaces.ql index a9c08ec594..81ee00878e 100644 --- a/cpp/autosar/src/rules/A17-6-1/NonStandardEntitiesInStandardNamespaces.ql +++ b/cpp/autosar/src/rules/A17-6-1/NonStandardEntitiesInStandardNamespaces.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.nonstandardentitiesinstandardnamespaces.NonStandardEntitiesInStandardNamespaces -class NonStandardEntitiesInStandardNamespacesQuery extends NonStandardEntitiesInStandardNamespacesSharedQuery { +class NonStandardEntitiesInStandardNamespacesQuery extends NonStandardEntitiesInStandardNamespacesSharedQuery +{ NonStandardEntitiesInStandardNamespacesQuery() { this = ScopePackage::nonStandardEntitiesInStandardNamespacesQuery() } diff --git a/cpp/autosar/src/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql b/cpp/autosar/src/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql index ada60f305d..5c4d9d580f 100644 --- a/cpp/autosar/src/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql +++ b/cpp/autosar/src/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql @@ -28,7 +28,7 @@ where * not use any of 'signal.h's facilities, for example. */ - filename = i.getIncludedFile().getBaseName() and + filename = i.getIncludeText().substring(1, i.getIncludeText().length() - 1) and filename in [ "assert.h", "ctype.h", "errno.h", "fenv.h", "float.h", "inttypes.h", "limits.h", "locale.h", "math.h", "setjmp.h", "signal.h", "stdarg.h", "stddef.h", "stdint.h", "stdio.h", "stdlib.h", diff --git a/cpp/autosar/src/rules/A18-0-2/StringNumberConversionMissingErrorCheck.ql b/cpp/autosar/src/rules/A18-0-2/StringNumberConversionMissingErrorCheck.ql index 02b35f8764..99d5393171 100644 --- a/cpp/autosar/src/rules/A18-0-2/StringNumberConversionMissingErrorCheck.ql +++ b/cpp/autosar/src/rules/A18-0-2/StringNumberConversionMissingErrorCheck.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.stringnumberconversionmissingerrorcheck.StringNumberConversionMissingErrorCheck -class StringNumberConversionMissingErrorCheckQuery extends StringNumberConversionMissingErrorCheckSharedQuery { +class StringNumberConversionMissingErrorCheckQuery extends StringNumberConversionMissingErrorCheckSharedQuery +{ StringNumberConversionMissingErrorCheckQuery() { this = TypeRangesPackage::stringNumberConversionMissingErrorCheckQuery() } diff --git a/cpp/autosar/src/rules/A18-1-1/CStyleArraysUsed.ql b/cpp/autosar/src/rules/A18-1-1/CStyleArraysUsed.ql index 83d0220c5a..0494e86607 100644 --- a/cpp/autosar/src/rules/A18-1-1/CStyleArraysUsed.ql +++ b/cpp/autosar/src/rules/A18-1-1/CStyleArraysUsed.ql @@ -30,5 +30,7 @@ class StaticConstExprArrayDataMember extends MemberVariable { from Variable v where not isExcluded(v, BannedSyntaxPackage::cStyleArraysUsedQuery()) and - exists(ArrayType a | v.getType() = a | not v instanceof StaticConstExprArrayDataMember) + exists(ArrayType a | v.getType() = a | not v instanceof StaticConstExprArrayDataMember) and + // Exclude the compiler generated __func__ as it is the only way to access the function name information + not v.getName() = "__func__" select v, "Variable " + v.getName() + " declares a c-style array." diff --git a/cpp/autosar/src/rules/A18-1-2/VectorboolSpecializationUsed.ql b/cpp/autosar/src/rules/A18-1-2/VectorboolSpecializationUsed.ql index 9d3e315f08..5bbe181927 100644 --- a/cpp/autosar/src/rules/A18-1-2/VectorboolSpecializationUsed.ql +++ b/cpp/autosar/src/rules/A18-1-2/VectorboolSpecializationUsed.ql @@ -17,22 +17,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.vectorshouldnotbespecializedwithbool.VectorShouldNotBeSpecializedWithBool -predicate isVectorBool(ClassTemplateInstantiation c) { - c.getNamespace() instanceof StdNamespace and - c.getTemplateArgument(0) instanceof BoolType and - c.getSimpleName() = "vector" +class VectorboolSpecializationUsedQuery extends VectorShouldNotBeSpecializedWithBoolSharedQuery { + VectorboolSpecializationUsedQuery() { + this = BannedTypesPackage::vectorboolSpecializationUsedQuery() + } } - -predicate isUsingVectorBool(ClassTemplateInstantiation c) { - isVectorBool(c) or - isUsingVectorBool(c.getTemplateArgument(_)) -} - -from Variable v, ClassTemplateInstantiation c -where - v.getUnderlyingType() = c and - not v.isFromTemplateInstantiation(_) and - isUsingVectorBool(c) and - not isExcluded(v, BannedTypesPackage::vectorboolSpecializationUsedQuery()) -select v, "Use of std::vector specialization." diff --git a/cpp/autosar/src/rules/A18-1-3/AutoPtrTypeUsed.ql b/cpp/autosar/src/rules/A18-1-3/AutoPtrTypeUsed.ql index beb93c739a..4b9d187dc1 100644 --- a/cpp/autosar/src/rules/A18-1-3/AutoPtrTypeUsed.ql +++ b/cpp/autosar/src/rules/A18-1-3/AutoPtrTypeUsed.ql @@ -15,9 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.StdNamespace predicate isAutoPtr(ClassTemplateInstantiation c) { - c.getNamespace() instanceof StdNamespace and + c.getNamespace() instanceof StdNS and c.getSimpleName() = "auto_ptr" } diff --git a/cpp/autosar/src/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.ql b/cpp/autosar/src/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.ql index 969373d436..353c985137 100644 --- a/cpp/autosar/src/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.ql +++ b/cpp/autosar/src/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.ql @@ -17,18 +17,14 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.SmartPointers import semmle.code.cpp.dataflow.TaintTracking -import DataFlow::PathGraph +import SingleObjectSmartPointerArrayConstructionFlow::PathGraph class AutosarSmartPointerArraySpecialisation extends AutosarSmartPointer { AutosarSmartPointerArraySpecialisation() { this.getOwnedObjectType() instanceof ArrayType } } -class SingleObjectSmartPointerArrayConstructionConfig extends TaintTracking::Configuration { - SingleObjectSmartPointerArrayConstructionConfig() { - this = "SingleObjectSmartPointerArrayConstructionConfig" - } - - override predicate isSource(DataFlow::Node source) { +module SingleObjectSmartPointerArrayConstructionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NewArrayExpr or source.asExpr() = any(FunctionCall fc, MemberFunction mf | @@ -40,31 +36,45 @@ class SingleObjectSmartPointerArrayConstructionConfig extends TaintTracking::Con ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(AutosarSmartPointer sp | not sp instanceof AutosarSmartPointerArraySpecialisation and ( sp.getAConstructorCallWithExternalObjectConstruction().getAnArgument() = sink.asExpr() or - sink.asExpr() = - any(FunctionCall fc, MemberFunction mf | - mf = fc.getTarget() and - mf.getDeclaringType() = sp and - mf.getName() = "reset" - | - fc.getArgument(0) - ) + sink.asExpr() = sp.getAResetCall().getArgument(0) ) ) } + + predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node sink) { + exists(AutosarUniquePointer sp, FunctionCall fc | + fc = sp.getAReleaseCall() and + source.asExpr() = fc.getQualifier() and + sink.asExpr() = fc + ) + } + + predicate isBarrierIn(DataFlow::Node node) { + // Exclude flow into header files outside the source archive which are summarized by the + // additional taint steps above. + exists(AutosarUniquePointer sp | + sp.getAReleaseCall().getTarget() = node.asExpr().(ThisExpr).getEnclosingFunction() + | + not exists(node.getLocation().getFile().getRelativePath()) + ) + } } +module SingleObjectSmartPointerArrayConstructionFlow = + TaintTracking::Global; + from - SingleObjectSmartPointerArrayConstructionConfig config, DataFlow::PathNode source, - DataFlow::PathNode sink + SingleObjectSmartPointerArrayConstructionFlow::PathNode source, + SingleObjectSmartPointerArrayConstructionFlow::PathNode sink where not isExcluded(sink.getNode().asExpr(), PointersPackage::pointerToAnElementOfAnArrayPassedToASmartPointerQuery()) and - config.hasFlowPath(source, sink) + SingleObjectSmartPointerArrayConstructionFlow::flowPath(source, sink) select sink.getNode(), source, sink, "A pointer to an element of an array of objects flows to a smart pointer of a single object type." diff --git a/cpp/autosar/src/rules/A18-5-10/PlacementNewInsufficientStorageAutosar.ql b/cpp/autosar/src/rules/A18-5-10/PlacementNewInsufficientStorageAutosar.ql index 9ecf3f415f..2854f30e0d 100644 --- a/cpp/autosar/src/rules/A18-5-10/PlacementNewInsufficientStorageAutosar.ql +++ b/cpp/autosar/src/rules/A18-5-10/PlacementNewInsufficientStorageAutosar.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.placementnewinsufficientstorage.PlacementNewInsufficientStorage -class PlacementNewInsufficientStorageAutosarQuery extends PlacementNewInsufficientStorageSharedQuery { +class PlacementNewInsufficientStorageAutosarQuery extends PlacementNewInsufficientStorageSharedQuery +{ PlacementNewInsufficientStorageAutosarQuery() { this = AllocationsPackage::placementNewInsufficientStorageAutosarQuery() } diff --git a/cpp/autosar/src/rules/A18-5-11/OperatorNewAndOperatorDeleteNotDefinedLocally.ql b/cpp/autosar/src/rules/A18-5-11/OperatorNewAndOperatorDeleteNotDefinedLocally.ql index 485837073a..5d4cd71c79 100644 --- a/cpp/autosar/src/rules/A18-5-11/OperatorNewAndOperatorDeleteNotDefinedLocally.ql +++ b/cpp/autosar/src/rules/A18-5-11/OperatorNewAndOperatorDeleteNotDefinedLocally.ql @@ -19,7 +19,8 @@ import codingstandards.cpp.autosar from MemberFunction operator_new, Class c where - not isExcluded(operator_new) and + not isExcluded(operator_new, + DeclarationsPackage::operatorNewAndOperatorDeleteNotDefinedLocallyQuery()) and not isExcluded(c, DeclarationsPackage::operatorNewAndOperatorDeleteNotDefinedLocallyQuery()) and operator_new.hasName("operator new") and operator_new.getDeclaringType() = c and diff --git a/cpp/autosar/src/rules/A18-5-2/DoNotUseNonPlacementNew.ql b/cpp/autosar/src/rules/A18-5-2/DoNotUseNonPlacementNew.ql index 1320d6e486..3cfccbf11e 100644 --- a/cpp/autosar/src/rules/A18-5-2/DoNotUseNonPlacementNew.ql +++ b/cpp/autosar/src/rules/A18-5-2/DoNotUseNonPlacementNew.ql @@ -15,7 +15,6 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.dataflow.DataFlow from NewOrNewArrayExpr na where diff --git a/cpp/autosar/src/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.ql b/cpp/autosar/src/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.ql index 7819cfad4d..274b18301c 100644 --- a/cpp/autosar/src/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.ql +++ b/cpp/autosar/src/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.ql @@ -15,14 +15,11 @@ import cpp import codingstandards.cpp.autosar -import OperatorDelete +import codingstandards.cpp.rules.globalsizedoperatordeletenotdefined.GlobalSizedOperatorDeleteNotDefined -from OperatorDelete unsized_delete -where - not isExcluded(unsized_delete, DeclarationsPackage::globalSizedOperatorDeleteNotDefinedQuery()) and - not unsized_delete.isSizeDelete() and - not exists(OperatorDelete od | unsized_delete.isNoThrowDelete() = od.isNoThrowDelete() | - od.isSizeDelete() - ) -select unsized_delete, - "Unsized function '" + unsized_delete.getName() + "' defined globally without sized version." +class GlobalSizedOperatorDeleteNotDefinedQuery extends GlobalSizedOperatorDeleteNotDefinedSharedQuery +{ + GlobalSizedOperatorDeleteNotDefinedQuery() { + this = DeclarationsPackage::globalSizedOperatorDeleteNotDefinedQuery() + } +} diff --git a/cpp/autosar/src/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.ql b/cpp/autosar/src/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.ql index 2c96660704..2bd0ada800 100644 --- a/cpp/autosar/src/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.ql +++ b/cpp/autosar/src/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.ql @@ -15,14 +15,11 @@ import cpp import codingstandards.cpp.autosar -import OperatorDelete +import codingstandards.cpp.rules.globalunsizedoperatordeletenotdefined.GlobalUnsizedOperatorDeleteNotDefined -from OperatorDelete sized_delete -where - not isExcluded(sized_delete, DeclarationsPackage::globalUnsizedOperatorDeleteNotDefinedQuery()) and - sized_delete.isSizeDelete() and - not exists(OperatorDelete od | sized_delete.isNoThrowDelete() = od.isNoThrowDelete() | - not od.isSizeDelete() - ) -select sized_delete, - "Sized function '" + sized_delete.getName() + "' defined globally without unsized version." +class GlobalUnsizedOperatorDeleteNotDefinedQuery extends GlobalUnsizedOperatorDeleteNotDefinedSharedQuery +{ + GlobalUnsizedOperatorDeleteNotDefinedQuery() { + this = DeclarationsPackage::globalUnsizedOperatorDeleteNotDefinedQuery() + } +} diff --git a/cpp/autosar/src/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.ql b/cpp/autosar/src/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.ql index bf37fbd8e7..cf83f055bd 100644 --- a/cpp/autosar/src/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.ql +++ b/cpp/autosar/src/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.ql @@ -53,6 +53,9 @@ class MakeSharedOrUnique extends FunctionCall, CandidateFunctionLocalHeapAllocat // This includes the case where a result of `make_shared` or `make_unique` is return by a function // because the compiler will call the appropriate constructor. not exists(FunctionCall fc | DataFlow::localExprFlow(this, fc.getAnArgument())) and + // The flow to a return statement is explicitly modelled for the case where + // the copy/move constructor is elided and therefore there is no actual function call in the database + not exists(ReturnStmt ret | DataFlow::localExprFlow(this, ret.getExpr())) and // Not assigned to a field not exists(Field f | DataFlow::localExprFlow(this, f.getAnAssignedValue())) } @@ -64,13 +67,14 @@ class MakeSharedOrUnique extends FunctionCall, CandidateFunctionLocalHeapAllocat * An `AllocationExpr` that allocates heap memory, where the memory is freed on at least one path * through the enclosing function. */ -class AllocationExprFunctionLocal extends AllocationExpr, CandidateFunctionLocalHeapAllocationExpr { +class AllocationExprFunctionLocal extends CandidateFunctionLocalHeapAllocationExpr instanceof AllocationExpr +{ AllocationExprFunctionLocal() { this.getSizeBytes() < 1024 and TaintTracking::localExprTaint(this, any(DeallocationExpr de).getFreedExpr()) } - override int getHeapSizeBytes() { result = this.getSizeBytes() } + override int getHeapSizeBytes() { result = super.getSizeBytes() } DeallocationExpr getADeallocation() { TaintTracking::localExprTaint(this, result.getFreedExpr()) } diff --git a/cpp/autosar/src/rules/A18-5-9/ThrowingNoThrowOperatorNewDeleteAutosar.ql b/cpp/autosar/src/rules/A18-5-9/ThrowingNoThrowOperatorNewDeleteAutosar.ql index fd613aa3bf..f2cf835a2b 100644 --- a/cpp/autosar/src/rules/A18-5-9/ThrowingNoThrowOperatorNewDeleteAutosar.ql +++ b/cpp/autosar/src/rules/A18-5-9/ThrowingNoThrowOperatorNewDeleteAutosar.ql @@ -18,7 +18,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.throwingnothrowoperatornewdelete.ThrowingNoThrowOperatorNewDelete -class ThrowingNoThrowOperatorNewDeleteAutosarQuery extends ThrowingNoThrowOperatorNewDeleteSharedQuery { +class ThrowingNoThrowOperatorNewDeleteAutosarQuery extends ThrowingNoThrowOperatorNewDeleteSharedQuery +{ ThrowingNoThrowOperatorNewDeleteAutosarQuery() { this = AllocationsPackage::throwingNoThrowOperatorNewDeleteAutosarQuery() } diff --git a/cpp/autosar/src/rules/A18-5-9/ThrowingOperatorNewThrowsInvalidExceptionAutosar.ql b/cpp/autosar/src/rules/A18-5-9/ThrowingOperatorNewThrowsInvalidExceptionAutosar.ql index c6c464b44a..c85eba435d 100644 --- a/cpp/autosar/src/rules/A18-5-9/ThrowingOperatorNewThrowsInvalidExceptionAutosar.ql +++ b/cpp/autosar/src/rules/A18-5-9/ThrowingOperatorNewThrowsInvalidExceptionAutosar.ql @@ -18,7 +18,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.throwingoperatornewthrowsinvalidexception.ThrowingOperatorNewThrowsInvalidException -class ThrowingOperatorNewThrowsInvalidExceptionAutosarQuery extends ThrowingOperatorNewThrowsInvalidExceptionSharedQuery { +class ThrowingOperatorNewThrowsInvalidExceptionAutosarQuery extends ThrowingOperatorNewThrowsInvalidExceptionSharedQuery +{ ThrowingOperatorNewThrowsInvalidExceptionAutosarQuery() { this = AllocationsPackage::throwingOperatorNewThrowsInvalidExceptionAutosarQuery() } diff --git a/cpp/autosar/src/rules/A18-9-1/BindUsed.ql b/cpp/autosar/src/rules/A18-9-1/BindUsed.ql index d53d6ecf76..9f594f1ed3 100644 --- a/cpp/autosar/src/rules/A18-9-1/BindUsed.ql +++ b/cpp/autosar/src/rules/A18-9-1/BindUsed.ql @@ -15,13 +15,15 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.StdNamespace predicate isBind(FunctionCall fc) { - fc.getTarget().getQualifiedName() in ["std::bind", "std::bind1st", "std::bind2nd"] + fc.getTarget().getNamespace() instanceof StdNS and + fc.getTarget().getName() in ["bind", "bind1st", "bind2nd"] } from FunctionCall fc where isBind(fc) and not isExcluded(fc, BannedFunctionsPackage::bindUsedQuery()) -select fc, "Prefer lambdas to using " + fc.getTarget().getQualifiedName() + "." +select fc, "Prefer lambdas to using `" + fc.getTarget().getName() + "`." diff --git a/cpp/autosar/src/rules/A18-9-2/ForwardingValuesToOtherFunctions.ql b/cpp/autosar/src/rules/A18-9-2/ForwardingValuesToOtherFunctions.ql index b0dd714209..72de362ebc 100644 --- a/cpp/autosar/src/rules/A18-9-2/ForwardingValuesToOtherFunctions.ql +++ b/cpp/autosar/src/rules/A18-9-2/ForwardingValuesToOtherFunctions.ql @@ -14,20 +14,12 @@ */ import cpp -import codingstandards.cpp.standardlibrary.Utility import codingstandards.cpp.autosar +import codingstandards.cpp.rules.forwardingreferencesandforwardnotusedtogether.ForwardingReferencesAndForwardNotUsedTogether -from FunctionCall c, Parameter a, string message -where - not isExcluded(c, MoveForwardPackage::forwardingValuesToOtherFunctionsQuery()) and - a.getAnAccess() = c.getAnArgument() and - ( - c instanceof StdMoveCall and - a instanceof ForwardParameter and - message = "Function `std::forward` should be used for forwarding the forward reference $@." - or - c instanceof StdForwardCall and - a instanceof ConsumeParameter and - message = "Function `std::move` should be used for forwarding rvalue reference $@." - ) -select c, message, a, a.getName() +class ForwardingValuesToOtherFunctionsQuery extends ForwardingReferencesAndForwardNotUsedTogetherSharedQuery +{ + ForwardingValuesToOtherFunctionsQuery() { + this = MoveForwardPackage::forwardingValuesToOtherFunctionsQuery() + } +} diff --git a/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.ql b/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.ql index 8e61436d57..7a8a67c64e 100644 --- a/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.ql +++ b/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.ql @@ -17,7 +17,7 @@ import codingstandards.cpp.autosar class CandidateFunction extends Function { CandidateFunction() { - isDefined() and + hasDefinition() and isStatic() and not isMember() and not ( diff --git a/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.ql b/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.ql index ba24ada376..79e17305fb 100644 --- a/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.ql +++ b/cpp/autosar/src/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.ql @@ -20,7 +20,9 @@ class CandidateVariable extends Variable { CandidateVariable() { hasDefinition() and isStatic() and - not this instanceof MemberVariable + not this instanceof MemberVariable and + //exclude partially specialized template variables + not this.isSpecialization() } } diff --git a/cpp/autosar/src/rules/A2-13-1/EscapeSequenceOutsideISO.ql b/cpp/autosar/src/rules/A2-13-1/EscapeSequenceOutsideISO.ql index d8382f51c8..0f1d9a3271 100644 --- a/cpp/autosar/src/rules/A2-13-1/EscapeSequenceOutsideISO.ql +++ b/cpp/autosar/src/rules/A2-13-1/EscapeSequenceOutsideISO.ql @@ -16,11 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.backslashcharactermisuse.BackslashCharacterMisuse -from StringLiteral l, string es -where - not isExcluded(l, LiteralsPackage::escapeSequenceOutsideISOQuery()) and - es = l.getANonStandardEscapeSequence(_, _) and - // Exclude universal-character-names, which begin with \u or \U - not es.toLowerCase().matches("\\u") -select l, "This literal contains the non-standard escape sequence " + es + "." +class EscapeSequenceOutsideISOQuery extends BackslashCharacterMisuseSharedQuery { + EscapeSequenceOutsideISOQuery() { this = LiteralsPackage::escapeSequenceOutsideISOQuery() } +} diff --git a/cpp/autosar/src/rules/A2-13-3/TypeWcharTUsed.ql b/cpp/autosar/src/rules/A2-13-3/TypeWcharTUsed.ql index af077da0eb..b4f4ec4f02 100644 --- a/cpp/autosar/src/rules/A2-13-3/TypeWcharTUsed.ql +++ b/cpp/autosar/src/rules/A2-13-3/TypeWcharTUsed.ql @@ -26,12 +26,14 @@ predicate isUsingWideCharType(ClassTemplateInstantiation c) { from Variable v where - v.getUnderlyingType() instanceof WideCharType and - not v.isFromTemplateInstantiation(_) - or - exists(ClassTemplateInstantiation c | - c = v.getType() and - isUsingWideCharType(c) - ) and - not isExcluded(v, BannedTypesPackage::typeWcharTUsedQuery()) + not isExcluded(v, BannedTypesPackage::typeWcharTUsedQuery()) and + ( + v.getUnderlyingType() instanceof WideCharType and + not v.isFromTemplateInstantiation(_) + or + exists(ClassTemplateInstantiation c | + c = v.getType() and + isUsingWideCharType(c) + ) + ) select v, "Use of wchar_t type." diff --git a/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInComment.ql b/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInComment.ql index a3090003d3..fdcc74b115 100644 --- a/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInComment.ql +++ b/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInComment.ql @@ -18,21 +18,23 @@ import cpp import codingstandards.cpp.autosar -bindingset[s] -string getCharOutsideBasicSourceCharSet(string s) { - result = s.regexpFind("[\\u0000-\\u007f]", _, _) and - not result.regexpMatch("[\\p{Alnum}\\p{Space}_{}\\[\\]#()<>%:;.?*+-/^&|~!=,\\\\\"'@]") - or - result = s.regexpFind("[\\u00c0-\\u00df][\\u0080-\\u00bf]", _, _) - or - result = s.regexpFind("[\\u00e0-\\u00ef][\\u0080-\\u00bf]{2}", _, _) - or - result = s.regexpFind("[\\u00f0-\\u00f7][\\u0080-\\u00bf]{3}", _, _) +string getCharOutsideBasicSourceCharSet(Comment c) { + exists(string s | s = c.getContents() | + result = + s.regexpFind("(?![\\p{Alnum}\\p{Space}_{}\\[\\]#()<>%:;.?*+-/^&|~!=,\\\\\"'@])[\\u0000-\\u007f]", + _, _) + or + result = s.regexpFind("[\\u00c0-\\u00df][\\u0080-\\u00bf]", _, _) + or + result = s.regexpFind("[\\u00e0-\\u00ef][\\u0080-\\u00bf]{2}", _, _) + or + result = s.regexpFind("[\\u00f0-\\u00f7][\\u0080-\\u00bf]{3}", _, _) + ) } from Comment c, string ch where not isExcluded(c, NamingPackage::invalidCharacterInCommentQuery()) and - ch = getCharOutsideBasicSourceCharSet(c.getContents()) + ch = getCharOutsideBasicSourceCharSet(c) select c, "Comment uses the character '" + ch + "' that is outside the language basic character set." diff --git a/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInStringLiteral.ql b/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInStringLiteral.ql index 93109bcd30..4f215d7d9c 100644 --- a/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInStringLiteral.ql +++ b/cpp/autosar/src/rules/A2-3-1/InvalidCharacterInStringLiteral.ql @@ -17,6 +17,7 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.Literals bindingset[s] string getCharOutsideBasicSourceCharSet(string s) { @@ -27,6 +28,9 @@ string getCharOutsideBasicSourceCharSet(string s) { from StringLiteral s, string ch where not isExcluded(s, NamingPackage::invalidCharacterInStringLiteralQuery()) and - ch = getCharOutsideBasicSourceCharSet(s.getValueText()) + ch = getCharOutsideBasicSourceCharSet(s.getValueText()) and + // wide string and utf8 string literals are exempted. + not s instanceof WideStringLiteral and + not s instanceof Utf8StringLiteral select s, "String literal uses the character '" + ch + "' that is outside the language basic character set." diff --git a/cpp/autosar/src/rules/A2-7-1/SingleLineCommentEndsWithSlash.ql b/cpp/autosar/src/rules/A2-7-1/SingleLineCommentEndsWithSlash.ql index adbb1dccea..cd7d7c42cd 100644 --- a/cpp/autosar/src/rules/A2-7-1/SingleLineCommentEndsWithSlash.ql +++ b/cpp/autosar/src/rules/A2-7-1/SingleLineCommentEndsWithSlash.ql @@ -17,9 +17,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.linesplicingusedincomments.LineSplicingUsedInComments -from CppStyleComment c -where - not isExcluded(c, CommentsPackage::singleLineCommentEndsWithSlashQuery()) and - exists(c.getContents().regexpFind("\\\n", _, _)) -select c, "C++ comment includes \\ as the last character of a line" +class SingleLineCommentEndsWithSlashQuery extends LineSplicingUsedInCommentsSharedQuery { + SingleLineCommentEndsWithSlashQuery() { + this = CommentsPackage::singleLineCommentEndsWithSlashQuery() + } +} diff --git a/cpp/autosar/src/rules/A2-7-2/SectionsOfCodeCommentedOut.ql b/cpp/autosar/src/rules/A2-7-2/SectionsOfCodeCommentedOut.ql index f47085b54d..5dbb6ee3c0 100644 --- a/cpp/autosar/src/rules/A2-7-2/SectionsOfCodeCommentedOut.ql +++ b/cpp/autosar/src/rules/A2-7-2/SectionsOfCodeCommentedOut.ql @@ -19,7 +19,5 @@ import codingstandards.cpp.autosar import codingstandards.cpp.rules.sectionsofcodeshallnotbecommentedout.SectionsOfCodeShallNotBeCommentedOut class SectionsOfCodeCommentedOutQuery extends SectionsOfCodeShallNotBeCommentedOutSharedQuery { - SectionsOfCodeCommentedOutQuery() { - this = CommentsPackage::sectionsOfCodeCommentedOutQuery() - } + SectionsOfCodeCommentedOutQuery() { this = CommentsPackage::sectionsOfCodeCommentedOutQuery() } } diff --git a/cpp/autosar/src/rules/A2-7-3/UndocumentedUserDefinedType.ql b/cpp/autosar/src/rules/A2-7-3/UndocumentedUserDefinedType.ql index 54925718f2..020d1d4ee1 100644 --- a/cpp/autosar/src/rules/A2-7-3/UndocumentedUserDefinedType.ql +++ b/cpp/autosar/src/rules/A2-7-3/UndocumentedUserDefinedType.ql @@ -17,6 +17,47 @@ import cpp import codingstandards.cpp.autosar +private predicate isInFunctionScope(Declaration d) { + // Type declared in function + exists(d.(UserType).getEnclosingFunction()) + or + // Member declared in type which is in function scope + isInFunctionScope(d.getDeclaringType()) +} + +private string doxygenCommentGroupStrings(boolean opening) { + opening = true and result = ["///@{", "/**@{*/"] + or + opening = false and result = ["///@}%", "/**@}*/"] +} + +pragma[inline] +private predicate isBetweenDoxygenCommentGroup( + Location loc, Comment opening, Comment body, Comment closing +) { + // All in the same file + loc.getFile() = opening.getLocation().getFile() and + loc.getFile() = closing.getLocation().getFile() and + loc.getFile() = body.getLocation().getFile() and + // The comments are doxygen comments + opening.getContents().matches(doxygenCommentGroupStrings(true)) and + closing.getContents().matches(doxygenCommentGroupStrings(false)) and + // The closing comment is after the opening comment + opening.getLocation().getStartLine() < closing.getLocation().getStartLine() and + // The `body` comment directly precedes the opening comment + body.getLocation().getEndLine() = opening.getLocation().getStartLine() - 1 and + // There are no other opening/closing comment pairs between the opening and closing comments + not exists(Comment c | + c.getContents().matches(doxygenCommentGroupStrings(_)) and + c.getLocation().getStartLine() > opening.getLocation().getStartLine() and + c.getLocation().getStartLine() < closing.getLocation().getStartLine() + ) and + // `loc` is between the opening and closing comments and after the body comment + loc.getStartLine() > opening.getLocation().getStartLine() and + loc.getStartLine() < closing.getLocation().getStartLine() and + loc.getStartLine() > body.getLocation().getEndLine() +} + /** * A declaration which is required to be preceded by documentation by AUTOSAR A2-7-3. */ @@ -24,30 +65,46 @@ class DocumentableDeclaration extends Declaration { string declarationType; DocumentableDeclaration() { - this instanceof UserType and - declarationType = "user-defined type" and - // Exclude template parameter types. - not this.(UserType).involvesTemplateParameter() - or - this instanceof Function and - declarationType = "function" and - // Exclude compiler generated functions, which cannot reasonably be documented. - not this.(Function).isCompilerGenerated() and - // Exclude instantiated template functions, which cannot reasonably be documented. - not this.(Function).isFromTemplateInstantiation(_) and - // Exclude anonymous lambda functions. - not exists(LambdaExpression lc | - lc.getLambdaFunction() = this and not lc.getEnclosingElement() instanceof Initializer + // Within the users codebase, not a system header + exists(this.getFile().getRelativePath()) and + // Not required to be documented, as used within same scope + not isInFunctionScope(this) and + ( + this instanceof UserType and + declarationType = "user-defined type" and + // Exclude template parameter types. + not this.(UserType).involvesTemplateParameter() + or + this instanceof Function and + declarationType = "function" and + // Exclude compiler generated functions, which cannot reasonably be documented. + not this.(Function).isCompilerGenerated() and + // Exclude instantiated template functions, which cannot reasonably be documented. + not this.(Function).isFromTemplateInstantiation(_) and + // Exclude anonymous lambda functions. + not exists(LambdaExpression lc | lc.getLambdaFunction() = this) and + //Exclude friend functions (because they have 2 entries in the database), and only one shows documented truly + not exists(FriendDecl d | + d.getFriend().(Function).getDefinition() = this.getADeclarationEntry() + ) + or + this instanceof MemberVariable and + declarationType = "member variable" and + // Exclude memeber variables in instantiated templates, which cannot reasonably be documented. + not this.(MemberVariable).isFromTemplateInstantiation(_) and + // Exclude compiler generated variables, such as those for anonymous lambda functions + not this.(MemberVariable).isCompilerGenerated() ) - or - this instanceof MemberVariable and - declarationType = "member variable" and - // Exclude memeber variables in instantiated templates, which cannot reasonably be documented. - not this.(MemberVariable).isFromTemplateInstantiation(_) and - // Exclude anonymous lambda functions. - // TODO: replace with the following when support is added. - // not this.(MemberVariable).isCompilerGenerated() - not exists(LambdaExpression lc | lc.getACapture().getField() = this) + } + + private predicate hasDocumentedDefinition() { + // Check if the declaration has a documented definition + exists(DeclarationEntry de | de = getADeclarationEntry() and isDocumented(de)) + } + + private predicate hasOnlyDefinitions() { + // Check if the declaration has only definitions, i.e., no non-definition entries + not exists(DeclarationEntry de | de = getADeclarationEntry() and not de.isDefinition()) } /** Gets a `DeclarationEntry` for this declaration that should be documented. */ @@ -55,20 +112,16 @@ class DocumentableDeclaration extends Declaration { // Find a declaration entry that is not documented result = getADeclarationEntry() and not isDocumented(result) and - ( - // Report any non definition DeclarationEntry that is not documented - // as long as there is no corresponding documented definition (which must be for a forward declaration) - not result.isDefinition() and - not exists(DeclarationEntry de | - de = getADeclarationEntry() and de.isDefinition() and isDocumented(de) - ) - or + if result.isDefinition() + then // Report the definition DeclarationEntry, only if there are no non-definition `DeclarationEntry`'s // The rationale here is that documenting both the non-definition and definition declaration entries // is redundant - result.isDefinition() and - not exists(DeclarationEntry de | de = getADeclarationEntry() and not de.isDefinition()) - ) + hasOnlyDefinitions() + else + // Report any non definition DeclarationEntry that is not documented + // as long as there is no corresponding documented definition (which must be for a forward declaration) + not hasDocumentedDefinition() } /** Gets a string describing the type of declaration. */ @@ -76,11 +129,13 @@ class DocumentableDeclaration extends Declaration { } /** - * A `DeclarationEntry` is considered documented if it has an associated `Comment`, and the `Comment` - * precedes the `DeclarationEntry`. + * A `DeclarationEntry` is considered documented if it has an associated `Comment`, the `Comment` + * precedes the `DeclarationEntry`, and the `Comment` is not a doxygen comment group prefix. */ +cached predicate isDocumented(DeclarationEntry de) { exists(Comment c | c.getCommentedElement() = de | + not c.getContents() = doxygenCommentGroupStrings(true) and exists(Location commentLoc, Location deLoc | commentLoc = c.getLocation() and deLoc = de.getLocation() | @@ -92,6 +147,9 @@ predicate isDocumented(DeclarationEntry de) { commentLoc.getStartColumn() < deLoc.getStartColumn() ) ) + or + // The declaration entry is between a doxygen comment group + isBetweenDoxygenCommentGroup(de.getLocation(), _, _, _) } from DocumentableDeclaration d, DeclarationEntry de diff --git a/cpp/autosar/src/rules/A20-8-1/OwnedPointerValueStoredInUnrelatedSmartPointerAsar.ql b/cpp/autosar/src/rules/A20-8-1/OwnedPointerValueStoredInUnrelatedSmartPointerAsar.ql index 1e974f5145..581a8f8dc2 100644 --- a/cpp/autosar/src/rules/A20-8-1/OwnedPointerValueStoredInUnrelatedSmartPointerAsar.ql +++ b/cpp/autosar/src/rules/A20-8-1/OwnedPointerValueStoredInUnrelatedSmartPointerAsar.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.ownedpointervaluestoredinunrelatedsmartpointer.OwnedPointerValueStoredInUnrelatedSmartPointer -class OwnedPointerValueStoredInUnrelatedSmartPointerAsarQuery extends OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery { +class OwnedPointerValueStoredInUnrelatedSmartPointerAsarQuery extends OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery +{ OwnedPointerValueStoredInUnrelatedSmartPointerAsarQuery() { this = SmartPointers1Package::ownedPointerValueStoredInUnrelatedSmartPointerAsarQuery() } diff --git a/cpp/autosar/src/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.ql b/cpp/autosar/src/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.ql index 5a45cbc9d6..d67058868c 100644 --- a/cpp/autosar/src/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.ql +++ b/cpp/autosar/src/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.ql @@ -40,8 +40,8 @@ import codingstandards.cpp.Iterators from ConstIteratorVariable v, STLContainer c, Expr e where - not isExcluded(v) and - not isExcluded(e) and + not isExcluded(v, IteratorsPackage::iteratorImplicitlyConvertedToConstIteratorQuery()) and + not isExcluded(e, IteratorsPackage::iteratorImplicitlyConvertedToConstIteratorQuery()) and e = v.getAnAssignedValue() and e.getAChild*() = /* see note at top of query */ c.getANonConstIteratorFunctionCall() select e, "Non-const version of container call immediately converted to a `const_iterator`." diff --git a/cpp/autosar/src/rules/A25-1-1/StateRelatedToFunctionObjectIdentityShallNotBeCopied.ql b/cpp/autosar/src/rules/A25-1-1/StateRelatedToFunctionObjectIdentityShallNotBeCopied.ql index d016b12662..93f857d302 100644 --- a/cpp/autosar/src/rules/A25-1-1/StateRelatedToFunctionObjectIdentityShallNotBeCopied.ql +++ b/cpp/autosar/src/rules/A25-1-1/StateRelatedToFunctionObjectIdentityShallNotBeCopied.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.predicatefunctionobjectsshouldnotbemutable.PredicateFunctionObjectsShouldNotBeMutable -class StateRelatedToFunctionObjectIdentityShallNotBeCopiedQuery extends PredicateFunctionObjectsShouldNotBeMutableSharedQuery { +class StateRelatedToFunctionObjectIdentityShallNotBeCopiedQuery extends PredicateFunctionObjectsShouldNotBeMutableSharedQuery +{ StateRelatedToFunctionObjectIdentityShallNotBeCopiedQuery() { this = SideEffects2Package::stateRelatedToFunctionObjectIdentityShallNotBeCopiedQuery() } diff --git a/cpp/autosar/src/rules/A26-5-1/PseudorandomNumbersGeneratedUsingRand.ql b/cpp/autosar/src/rules/A26-5-1/PseudorandomNumbersGeneratedUsingRand.ql index 17411c0685..9dfdfe538e 100644 --- a/cpp/autosar/src/rules/A26-5-1/PseudorandomNumbersGeneratedUsingRand.ql +++ b/cpp/autosar/src/rules/A26-5-1/PseudorandomNumbersGeneratedUsingRand.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.donotuserandforgeneratingpseudorandomnumbers.DoNotUseRandForGeneratingPseudorandomNumbers -class PseudorandomNumbersGeneratedUsingRandQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery { +class PseudorandomNumbersGeneratedUsingRandQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery +{ PseudorandomNumbersGeneratedUsingRandQuery() { this = BannedFunctionsPackage::pseudorandomNumbersGeneratedUsingRandQuery() } diff --git a/cpp/autosar/src/rules/A27-0-2/OperationMayNotNullTerminateCStyleStringAutosar.ql b/cpp/autosar/src/rules/A27-0-2/OperationMayNotNullTerminateCStyleStringAutosar.ql index b012eea69f..69ff628298 100644 --- a/cpp/autosar/src/rules/A27-0-2/OperationMayNotNullTerminateCStyleStringAutosar.ql +++ b/cpp/autosar/src/rules/A27-0-2/OperationMayNotNullTerminateCStyleStringAutosar.ql @@ -16,7 +16,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.operationmaynotnullterminatecstylestring.OperationMayNotNullTerminateCStyleString -class OperationMayNotNullTerminateCStyleStringAutosarQuery extends OperationMayNotNullTerminateCStyleStringSharedQuery { +class OperationMayNotNullTerminateCStyleStringAutosarQuery extends OperationMayNotNullTerminateCStyleStringSharedQuery +{ OperationMayNotNullTerminateCStyleStringAutosarQuery() { this = StringsPackage::operationMayNotNullTerminateCStyleStringAutosarQuery() } diff --git a/cpp/autosar/src/rules/A27-0-4/CStyleStringsUsed.ql b/cpp/autosar/src/rules/A27-0-4/CStyleStringsUsed.ql index be8bda1f0b..b24a4a96cf 100644 --- a/cpp/autosar/src/rules/A27-0-4/CStyleStringsUsed.ql +++ b/cpp/autosar/src/rules/A27-0-4/CStyleStringsUsed.ql @@ -36,5 +36,6 @@ where e = any(FunctionCall fc).getArgument(_) and e.getUnspecifiedType().(PointerType).getBaseType*() instanceof CharType ) and - DataFlow::localFlow(DataFlow::exprNode(cs), DataFlow::exprNode(e)) + DataFlow::localFlow(DataFlow::exprNode(cs), DataFlow::exprNode(e)) and + not cs = any(LocalVariable lv | lv.getName() = "__func__").getInitializer().getExpr() select cs, "Usage of C-style string in $@.", e, "expression" diff --git a/cpp/autosar/src/rules/A3-1-1/ViolationsOfOneDefinitionRule.ql b/cpp/autosar/src/rules/A3-1-1/ViolationsOfOneDefinitionRule.ql index 482b17ca3b..4beb91e8f4 100644 --- a/cpp/autosar/src/rules/A3-1-1/ViolationsOfOneDefinitionRule.ql +++ b/cpp/autosar/src/rules/A3-1-1/ViolationsOfOneDefinitionRule.ql @@ -65,7 +65,7 @@ where or //an non-const object defined in a header exists(GlobalOrNamespaceVariable object | - object.isDefined() and + object.hasDefinition() and not ( object.isConstexpr() or diff --git a/cpp/autosar/src/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql b/cpp/autosar/src/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql index 251f94d6eb..9b250e487a 100644 --- a/cpp/autosar/src/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql +++ b/cpp/autosar/src/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.ql @@ -4,9 +4,10 @@ * @description A function that is not either trivial, a template function, or a member of a * template class may not be defined within a class body. * @kind problem - * @precision very-high + * @precision low * @problem.severity recommendation * @tags external/autosar/id/a3-1-5 + * external/autosar/audit * external/autosar/allocated-target/design * external/autosar/enforcement/partially-automated * external/autosar/obligation/required diff --git a/cpp/autosar/src/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.ql b/cpp/autosar/src/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.ql deleted file mode 100644 index 920875ca3b..0000000000 --- a/cpp/autosar/src/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.ql +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @id cpp/autosar/trivial-or-template-function-defined-outside-class-definition - * @name A3-1-5: A function shall be defined with a class body if and only if it is intended to be inlined - * @description A function that is either trivial, a template function, or a member of a template - * class may not be defined outside of a class body. - * @kind problem - * @precision very-high - * @problem.severity recommendation - * @tags external/autosar/id/a3-1-5 - * external/autosar/allocated-target/design - * external/autosar/enforcement/partially-automated - * external/autosar/obligation/required - */ - -import cpp -import codingstandards.cpp.autosar -import codingstandards.cpp.Class - -/* - * Find instances of `MemberFunction` where the `MemberFunction` is trivial - * and it is not inlined within the class. - */ - -from MemberFunction mf, string kind -where - not isExcluded(mf, ClassesPackage::trivialOrTemplateFunctionDefinedOutsideClassDefinitionQuery()) and - // The member function `mf` is not defined in the class body. - exists(FunctionDeclarationEntry fde | - fde = mf.getClassBodyDeclarationEntry() and not fde.isDefinition() - ) and - //ignore destructors - not mf instanceof Destructor and - // Report functions that are NOT defined in the class body if they are either trivial or - // either a template member or part of a template class (i.e., they should - // be defined in the class body) - ( - if - mf instanceof TemplateOrTemplateClassMemberFunction and - mf instanceof TrivialMemberFunction - then kind = "template" - else - if mf instanceof TrivialMemberFunction - then kind = "trivial" - else - if mf instanceof TemplateOrTemplateClassMemberFunction - then kind = "template" - else none() - ) -select mf, - "The " + kind + " member function " + mf.getName() + " is not defined in the class body of $@.", - mf.getDeclaringType(), mf.getDeclaringType().getName() diff --git a/cpp/autosar/src/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.ql b/cpp/autosar/src/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.ql index e7bff9ef71..06c6ad517c 100644 --- a/cpp/autosar/src/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.ql +++ b/cpp/autosar/src/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.ql @@ -38,4 +38,4 @@ where // Main functions are an exception to the rule not de.getDeclaration() instanceof MainFunction and if de.getDeclaration() instanceof Function then kind = "function" else kind = "object" -select de, "Externally linked " + kind + " " + de.getName() + " not declared in header file." +select de, "Externally linked " + kind + " '" + de.getName() + "' not declared in header file." diff --git a/cpp/autosar/src/rules/A3-9-1/VariableWidthIntegerTypesUsed.ql b/cpp/autosar/src/rules/A3-9-1/VariableWidthIntegerTypesUsed.ql index 46376be1af..fa19ad998f 100644 --- a/cpp/autosar/src/rules/A3-9-1/VariableWidthIntegerTypesUsed.ql +++ b/cpp/autosar/src/rules/A3-9-1/VariableWidthIntegerTypesUsed.ql @@ -1,8 +1,8 @@ /** * @id cpp/autosar/variable-width-integer-types-used * @name A3-9-1: Use fixed-width integer types instead of basic, variable-width, integer types - * @description The basic numerical types of char, int, short, long are not supposed to be used. The - * specific-length types from header need be used instead. + * @description The basic numerical types of signed/unsigned char, int, short, long are not supposed + * to be used. The specific-length types from header need be used instead. * @kind problem * @precision very-high * @problem.severity error @@ -18,32 +18,25 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.EncapsulatingFunctions +import codingstandards.cpp.BuiltInNumericTypes +import codingstandards.cpp.Type +import codingstandards.cpp.Operator -/** - * any `Parameter` in a main function like: - * int main(int argc, char *argv[]) - */ -class ExcludedVariable extends Parameter { - ExcludedVariable() { getFunction() instanceof MainFunction } -} - -from Variable v +from Variable v, Type typeStrippedOfSpecifiers where not isExcluded(v, DeclarationsPackage::variableWidthIntegerTypesUsedQuery()) and + typeStrippedOfSpecifiers = stripSpecifiers(v.getType()) and ( - v.getType() instanceof PlainCharType - or - v.getType() instanceof UnsignedCharType - or - v.getType() instanceof SignedCharType - or - v.getType() instanceof ShortType - or - v.getType() instanceof IntType - or - v.getType() instanceof LongType - or - v.getType() instanceof LongLongType + typeStrippedOfSpecifiers instanceof BuiltInIntegerType or + typeStrippedOfSpecifiers instanceof UnsignedCharType or + typeStrippedOfSpecifiers instanceof SignedCharType ) and - not v instanceof ExcludedVariable + not v instanceof ExcludedVariable and + // Dont consider template instantiations because instantiations with + // Fixed Width Types are recorded after stripping their typedef'd type, + // thereby, causing false positives (#540). + not v.isFromTemplateInstantiation(_) and + //post-increment/post-decrement operators are required by the standard to have a dummy int parameter + not v.(Parameter).getFunction() instanceof PostIncrementOperator and + not v.(Parameter).getFunction() instanceof PostDecrementOperator select v, "Variable '" + v.getName() + "' has variable-width type." diff --git a/cpp/autosar/src/rules/A3-9-1/VariableWidthPlainCharTypeUsed.ql b/cpp/autosar/src/rules/A3-9-1/VariableWidthPlainCharTypeUsed.ql new file mode 100644 index 0000000000..20f74bb511 --- /dev/null +++ b/cpp/autosar/src/rules/A3-9-1/VariableWidthPlainCharTypeUsed.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/autosar/variable-width-plain-char-type-used + * @name A3-9-1: Use a fixed-width integer type instead of a char type + * @description The basic numerical type char is not supposed to be used. The specific-length types + * from header need be used instead. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/autosar/id/a3-9-1 + * correctness + * security + * maintainability + * external/autosar/allocated-target/implementation + * external/autosar/enforcement/automated + * external/autosar/obligation/required + */ + +import cpp +import codingstandards.cpp.autosar +import codingstandards.cpp.Type + +from Variable variable +where + not isExcluded(variable, DeclarationsPackage::variableWidthPlainCharTypeUsedQuery()) and + stripSpecifiers(variable.getType()) instanceof PlainCharType +select variable, "Variable '" + variable.getName() + "' has variable-width char type." diff --git a/cpp/autosar/src/rules/A4-10-1/NullPointerConstantNotNullptr.ql b/cpp/autosar/src/rules/A4-10-1/NullPointerConstantNotNullptr.ql index e77c8265d5..ce3c6f8461 100644 --- a/cpp/autosar/src/rules/A4-10-1/NullPointerConstantNotNullptr.ql +++ b/cpp/autosar/src/rules/A4-10-1/NullPointerConstantNotNullptr.ql @@ -16,17 +16,11 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.commons.NULL +import codingstandards.cpp.rules.nullptrnottheonlyformofthenullpointerconstant.NullptrNotTheOnlyFormOfTheNullPointerConstant -from Literal l -where - not isExcluded(l, LiteralsPackage::nullPointerConstantNotNullptrQuery()) and - // Not the type of the nullptr literal - not l.getType() instanceof NullPointerType and - // Converted to a pointer type - l.getConversion().getType().getUnspecifiedType() instanceof PointerType and - // Value of zero - l.getValue() = "0" and - // Not the StringLiteral "0" - not l instanceof StringLiteral -select l, l.getValueText() + " is used as the null-pointer-constant but is not nullptr." +class NullPointerConstantNotNullptrQuery extends NullptrNotTheOnlyFormOfTheNullPointerConstantSharedQuery +{ + NullPointerConstantNotNullptrQuery() { + this = LiteralsPackage::nullPointerConstantNotNullptrQuery() + } +} diff --git a/cpp/autosar/src/rules/A4-5-1/EnumUsedInArithmeticContexts.ql b/cpp/autosar/src/rules/A4-5-1/EnumUsedInArithmeticContexts.ql index 3f21a66580..af69a4dca4 100644 --- a/cpp/autosar/src/rules/A4-5-1/EnumUsedInArithmeticContexts.ql +++ b/cpp/autosar/src/rules/A4-5-1/EnumUsedInArithmeticContexts.ql @@ -18,44 +18,26 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.Operator +import codingstandards.cpp.Type -/* - * Get an operand to all overloaded operator member functions, except: - * operator[] - * operator= - * operator== - * operator!= - * operator& - * operator< - * operator<= - * operator> - * operator>= - */ - -Expr getAnOperandOfAllowedOverloadedOperator(FunctionCall fc) { - fc.getAnArgument() = result and - fc.getTarget().getName().regexpMatch("operator(?!\\[]$|=$|==$|!=$|&$|<$|<=$|>$|>=$).+") -} - -Expr getAnOperandOfAllowedOperation(Operation o) { - o.getAnOperand() = result and - not ( - o instanceof AssignExpr or - o instanceof BitwiseAndExpr or - o instanceof ComparisonOperation - ) +class AllowedOperatorUse extends OperatorUse { + AllowedOperatorUse() { + this.getOperator() in ["[]", "=", "==", "!=", "<", "<=", ">", ">="] + or + this.(UnaryOperatorUse).getOperator() = "&" + } } -from Expr e, Expr operand +from OperatorUse operatorUse, Access access, Enum enum where - not isExcluded(e, ExpressionsPackage::enumUsedInArithmeticContextsQuery()) and + not isExcluded(access, ExpressionsPackage::enumUsedInArithmeticContextsQuery()) and + operatorUse.getAnOperand() = access and ( - operand = getAnOperandOfAllowedOverloadedOperator(e) - or - operand = getAnOperandOfAllowedOperation(e) + access.(EnumConstantAccess).getTarget().getDeclaringEnum() = enum or + access.(VariableAccess).getType() = enum ) and - ( - operand instanceof EnumConstantAccess or - operand.(VariableAccess).getType() instanceof Enum - ) -select e, "Enum $@ is used as an operand of arithmetic operation.", operand, "expression" + not operatorUse instanceof AllowedOperatorUse and + // Enums that implement the BitmaskType trait are an exception. + not enum instanceof BitmaskType +select access, "Enum $@ is used as an operand of arithmetic operation.", enum, enum.getName() diff --git a/cpp/autosar/src/rules/A4-7-1/IntegerExpressionLeadToDataLoss.ql b/cpp/autosar/src/rules/A4-7-1/IntegerExpressionLeadToDataLoss.ql index 242283d716..a6d7abc456 100644 --- a/cpp/autosar/src/rules/A4-7-1/IntegerExpressionLeadToDataLoss.ql +++ b/cpp/autosar/src/rules/A4-7-1/IntegerExpressionLeadToDataLoss.ql @@ -15,74 +15,22 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import codingstandards.cpp.Overflow import semmle.code.cpp.controlflow.Guards -import semmle.code.cpp.dataflow.TaintTracking import semmle.code.cpp.valuenumbering.GlobalValueNumbering -/** - * A `BinaryArithmeticOperation` which may overflow and is a potentially interesting case to review - * that is not covered by other queries for this rule. - */ -class InterestingBinaryOverflowingExpr extends BinaryArithmeticOperation { - InterestingBinaryOverflowingExpr() { - // Might overflow or underflow - ( - exprMightOverflowNegatively(this) - or - exprMightOverflowPositively(this) - ) and - not this.isAffectedByMacro() and - // Ignore pointer arithmetic - not this instanceof PointerArithmeticOperation and - // Covered by `IntMultToLong.ql` instead - not this instanceof MulExpr and - // Not covered by this query - overflow/underflow in division is rare - not this instanceof DivExpr - } - - /** - * Get a `GVN` which guards this expression which may overflow. - */ - GVN getAGuardingGVN() { - exists(GuardCondition gc, Expr e | - not gc = getABadOverflowCheck() and - TaintTracking::localTaint(DataFlow::exprNode(e), DataFlow::exprNode(gc.getAChild*())) and - gc.controls(this.getBasicBlock(), _) and - result = globalValueNumber(e) - ) - } - - /** - * Identifies a bad overflow check for this overflow expression. - */ - GuardCondition getABadOverflowCheck() { - exists(AddExpr ae, RelationalOperation relOp | - this = ae and - result = relOp and - // Looking for this pattern: - // if (x + y > x) - // use(x + y) - // - globalValueNumber(relOp.getAnOperand()) = globalValueNumber(ae) and - globalValueNumber(relOp.getAnOperand()) = globalValueNumber(ae.getAnOperand()) - | - // Signed overflow checks are insufficient - ae.getUnspecifiedType().(IntegralType).isSigned() - or - // Unsigned overflow checks can still be bad, if the result is promoted. - forall(Expr op | op = ae.getAnOperand() | op.getType().getSize() < any(IntType i).getSize()) and - // Not explicitly converted to a smaller type before the comparison - not ae.getExplicitlyConverted().getType().getSize() < any(IntType i).getSize() - ) - } -} - -from InterestingBinaryOverflowingExpr e +from InterestingOverflowingOperation e where not isExcluded(e, IntegerConversionPackage::integerExpressionLeadToDataLossQuery()) and // Not within a guard condition not exists(GuardCondition gc | gc.getAChild*() = e) and // Not guarded by a check, where the check is not an invalid overflow check - not e.getAGuardingGVN() = globalValueNumber(e.getAChild*()) + not e.hasValidPreCheck() and + // Covered by `IntMultToLong.ql` instead + not e instanceof MulExpr and + // Not covered by this query - overflow/underflow in division is rare + not e instanceof DivExpr and + not e instanceof AssignDivExpr and + not e instanceof RemExpr and + not e instanceof AssignRemExpr select e, "Binary expression ..." + e.getOperator() + "... may overflow." diff --git a/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql b/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql index 45b130e184..f598bb8835 100644 --- a/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql +++ b/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql @@ -15,12 +15,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.nonbooleanifstmt.NonBooleanIfStmt -from IfStmt ifStmt, Expr condition, Type explicitConversionType -where - not isExcluded(ifStmt, ConditionalsPackage::nonBooleanIfConditionQuery()) and - condition = ifStmt.getCondition() and - not ifStmt.isFromUninstantiatedTemplate(_) and - explicitConversionType = condition.getExplicitlyConverted().getType().getUnspecifiedType() and - not explicitConversionType instanceof BoolType -select condition, "If condition has non boolean type " + explicitConversionType + "." +class NonBooleanIfConditionQuery extends NonBooleanIfStmtSharedQuery { + NonBooleanIfConditionQuery() { this = ConditionalsPackage::nonBooleanIfConditionQuery() } +} diff --git a/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql b/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql index 07f8f4de3c..c52c100df8 100644 --- a/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql +++ b/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql @@ -15,11 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.nonbooleaniterationstmt.NonBooleanIterationStmt -from Loop loopStmt, Expr condition, Type explicitConversionType -where - not isExcluded(loopStmt, ConditionalsPackage::nonBooleanIterationConditionQuery()) and - condition = loopStmt.getCondition() and - explicitConversionType = condition.getExplicitlyConverted().getType().getUnspecifiedType() and - not explicitConversionType instanceof BoolType -select condition, "Iteration condition has non boolean type " + explicitConversionType + "." +class NonBooleanIterationConditionQuery extends NonBooleanIterationStmtSharedQuery { + NonBooleanIterationConditionQuery() { + this = ConditionalsPackage::nonBooleanIterationConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/A5-0-3/DeclarationContainLessThanTwoLevelsOfIndirection.ql b/cpp/autosar/src/rules/A5-0-3/DeclarationContainLessThanTwoLevelsOfIndirection.ql index ddd996db5a..2e866c9f2d 100644 --- a/cpp/autosar/src/rules/A5-0-3/DeclarationContainLessThanTwoLevelsOfIndirection.ql +++ b/cpp/autosar/src/rules/A5-0-3/DeclarationContainLessThanTwoLevelsOfIndirection.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.donotusemorethantwolevelsofpointerindirection.DoNotUseMoreThanTwoLevelsOfPointerIndirection -class DeclarationContainLessThanTwoLevelsOfIndirectionQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery { +class DeclarationContainLessThanTwoLevelsOfIndirectionQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery +{ DeclarationContainLessThanTwoLevelsOfIndirectionQuery() { this = PointersPackage::declarationContainLessThanTwoLevelsOfIndirectionQuery() } diff --git a/cpp/autosar/src/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.ql b/cpp/autosar/src/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.ql index db178b36fb..ac2375f6aa 100644 --- a/cpp/autosar/src/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.ql +++ b/cpp/autosar/src/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.ql @@ -18,7 +18,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.Type import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import NonFinalClassToPointerArithmeticExprFlow::PathGraph class ArrayAccessOrPointerArith extends Expr { ArrayAccessOrPointerArith() { @@ -42,12 +42,8 @@ class AddressOfPointerCreation extends ClassPointerCreation, AddressOfExpr { AddressOfPointerCreation() { this.getAnOperand().getUnderlyingType() instanceof Class } } -class NonFinalClassToPointerArithmeticExprConfig extends DataFlow::Configuration { - NonFinalClassToPointerArithmeticExprConfig() { - this = "NonFinalClassToPointerArithmeticExprConfig" - } - - override predicate isSource(DataFlow::Node source) { +module NonFinalClassToPointerArithmeticExprConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(Class c | source.asExpr() instanceof ClassPointerCreation and source.asExpr().getUnderlyingType().(PointerType).getBaseType() = c @@ -56,17 +52,21 @@ class NonFinalClassToPointerArithmeticExprConfig extends DataFlow::Configuration ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(ArrayAccessOrPointerArith e | e.getAnOperand() = sink.asExpr()) } } +module NonFinalClassToPointerArithmeticExprFlow = + DataFlow::Global; + from - ArrayAccessOrPointerArith e, Class clz, Variable v, DataFlow::PathNode source, - DataFlow::PathNode sink + ArrayAccessOrPointerArith e, Class clz, Variable v, + NonFinalClassToPointerArithmeticExprFlow::PathNode source, + NonFinalClassToPointerArithmeticExprFlow::PathNode sink where not isExcluded(e, PointersPackage::pointerArithmeticUsedWithPointersToNonFinalClassesQuery()) and - any(NonFinalClassToPointerArithmeticExprConfig c).hasFlowPath(source, sink) and + NonFinalClassToPointerArithmeticExprFlow::flowPath(source, sink) and v.getAnAssignedValue() = source.getNode().asExpr() and ( e.(PointerArithmeticOperation).getAnOperand() = sink.getNode().asExpr() diff --git a/cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql b/cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql index c20d0ded55..a14681d95b 100644 --- a/cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql +++ b/cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql @@ -18,6 +18,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.LoggingOperation import codingstandards.cpp.Literals +import codingstandards.cpp.Cpp14Literal from Literal l where @@ -26,14 +27,20 @@ where not exists(ConstructorCall cc | cc.getAnArgument() = l) and not exists(ConstructorFieldInit cf | cf.getExpr() = l) and not l = any(LoggingOperation logOp).getALoggedExpr().getAChild*() and + // Exclude arguments to wrapper functions (depth 1) + not exists(FunctionCall fc, LoggerOrStreamWrapperFunction w | + fc.getAnArgument() = l and w.getACallToThisFunction() = fc + ) and + // Exclude Macros with names like *LOG + not exists(MacroInvocation m | m.getMacroName().matches("%LOG") and m.getAnAffectedElement() = l) and // Exclude literal 0 not l.getValue() = "0" and // Exclude character literals - not l instanceof CharLiteral and + not l instanceof Cpp14Literal::CharLiteral and // Exclude `nullptr` not l.getType() instanceof NullPointerType and // Exclude boolean `true` and `false` - not l.getType() instanceof BoolType and + not l instanceof BoolLiteral and // Exclude empty string not l.getValue() = "" and // Template functions use literals to represent calls which are unknown @@ -43,9 +50,16 @@ where // Macro expansions are morally excluded not l = any(MacroInvocation mi).getAnExpandedElement() and // Aggregate literal - not l = any(ArrayOrVectorAggregateLiteral aal).getElementExpr(_).getAChild*() and + not l = any(ArrayOrVectorAggregateLiteral aal).getAnElementExpr(_).getAChild*() and // Ignore x - 1 expressions - not exists(SubExpr se | se.getRightOperand() = l and l.getValue() = "1") -select l, - "Literal value " + getTruncatedLiteralText(l) + " used outside of type initialization " + - l.getAPrimaryQlClass() + not exists(SubExpr se | se.getRightOperand() = l and l.getValue() = "1") and + // Exclude compile time computed integral literals as they can appear as integral literals + // when used as non-type template arguments. + // We limit ourselves to integral literals, because floating point literals as non-type + // template arguments are not supported in C++ 14. Those are supported shince C++ 20. + not l instanceof CompileTimeComputedIntegralLiteral and + // Exclude literals to instantiate a class template per example in the standard + // where an type of std::array is intialized with size 5. + not l = any(ClassTemplateInstantiation cti).getATemplateArgument() and + not l = any(ClassAggregateLiteral cal).getAFieldExpr(_) +select l, "Literal value '" + getTruncatedLiteralText(l) + "' used outside of type initialization." diff --git a/cpp/autosar/src/rules/A5-1-3/LambdaExpressionWithoutParameterList.ql b/cpp/autosar/src/rules/A5-1-3/LambdaExpressionWithoutParameterList.ql index 4583f8675e..7d3d630d95 100644 --- a/cpp/autosar/src/rules/A5-1-3/LambdaExpressionWithoutParameterList.ql +++ b/cpp/autosar/src/rules/A5-1-3/LambdaExpressionWithoutParameterList.ql @@ -21,6 +21,10 @@ where not isExcluded(lambda, LambdasPackage::lambdaExpressionWithoutParameterListQuery()) and lambdaFunction = lambda.getLambdaFunction() and not lambdaFunction.isAffectedByMacro() and + // If it has a parameter, then it will have an + // explicit parameter list. Therefore, proceed to check only if the lambda + // does not have any parameters. + not exists(lambdaFunction.getAParameter()) and // The extractor doesn't store the syntactic information whether the parameter list // is enclosed in parenthesis. Therefore we cannot determine if the parameter list is // explicitly specified when the parameter list is empty. diff --git a/cpp/autosar/src/rules/A5-1-4/MovedLambdaObjectOutlivesCaptureByReference.ql b/cpp/autosar/src/rules/A5-1-4/MovedLambdaObjectOutlivesCaptureByReference.ql index a7280526e3..2cf48c7f6b 100644 --- a/cpp/autosar/src/rules/A5-1-4/MovedLambdaObjectOutlivesCaptureByReference.ql +++ b/cpp/autosar/src/rules/A5-1-4/MovedLambdaObjectOutlivesCaptureByReference.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.danglingcapturewhenmovinglambdaobject.DanglingCaptureWhenMovingLambdaObject -class MovedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenMovingLambdaObjectSharedQuery { +class MovedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenMovingLambdaObjectSharedQuery +{ MovedLambdaObjectOutlivesCaptureByReferenceQuery() { this = LambdasPackage::movedLambdaObjectOutlivesCaptureByReferenceQuery() } diff --git a/cpp/autosar/src/rules/A5-1-4/ReturnedLambdaObjectOutlivesCaptureByReference.ql b/cpp/autosar/src/rules/A5-1-4/ReturnedLambdaObjectOutlivesCaptureByReference.ql index d173d8ab54..aaefa175cc 100644 --- a/cpp/autosar/src/rules/A5-1-4/ReturnedLambdaObjectOutlivesCaptureByReference.ql +++ b/cpp/autosar/src/rules/A5-1-4/ReturnedLambdaObjectOutlivesCaptureByReference.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.danglingcapturewhenreturninglambdaobject.DanglingCaptureWhenReturningLambdaObject -class ReturnedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenReturningLambdaObjectSharedQuery { +class ReturnedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenReturningLambdaObjectSharedQuery +{ ReturnedLambdaObjectOutlivesCaptureByReferenceQuery() { this = LambdasPackage::returnedLambdaObjectOutlivesCaptureByReferenceQuery() } diff --git a/cpp/autosar/src/rules/A5-1-7/LambdaPassedToDecltype.ql b/cpp/autosar/src/rules/A5-1-7/LambdaPassedToDecltype.ql index 5d79a3a362..971d3b9259 100644 --- a/cpp/autosar/src/rules/A5-1-7/LambdaPassedToDecltype.ql +++ b/cpp/autosar/src/rules/A5-1-7/LambdaPassedToDecltype.ql @@ -17,26 +17,27 @@ import cpp import codingstandards.cpp.autosar import semmle.code.cpp.dataflow.DataFlow -class LambdaExpressionToInitializer extends DataFlow::Configuration { - LambdaExpressionToInitializer() { this = "LambdaExpressionToInitializer" } +module LambdaExpressionToInitializerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof LambdaExpression } - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof LambdaExpression } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(Variable v | v.getInitializer().getExpr() = sink.asExpr()) } } +module LambdaExpressionToInitializerFlow = DataFlow::Global; + from Decltype dt, LambdaExpression lambda where not isExcluded(dt, LambdasPackage::lambdaPassedToDecltypeQuery()) and ( dt.getExpr() = lambda or - exists(LambdaExpressionToInitializer config, VariableAccess va, Variable v | + exists(VariableAccess va, Variable v | dt.getExpr() = va and v = va.getTarget() and - config.hasFlow(DataFlow::exprNode(lambda), DataFlow::exprNode(v.getInitializer().getExpr())) + LambdaExpressionToInitializerFlow::flow(DataFlow::exprNode(lambda), + DataFlow::exprNode(v.getInitializer().getExpr())) ) ) select dt, "Lambda $@ passed as operand to decltype.", lambda, "expression" diff --git a/cpp/autosar/src/rules/A5-1-7/LambdaPassedToTypeid.ql b/cpp/autosar/src/rules/A5-1-7/LambdaPassedToTypeid.ql index 2471464364..56952dace9 100644 --- a/cpp/autosar/src/rules/A5-1-7/LambdaPassedToTypeid.ql +++ b/cpp/autosar/src/rules/A5-1-7/LambdaPassedToTypeid.ql @@ -16,21 +16,19 @@ import cpp import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.autosar -import DataFlow::PathGraph +import LambdaExpressionToTypeidFlow::PathGraph -class LambdaExpressionToTypeid extends DataFlow::Configuration { - LambdaExpressionToTypeid() { this = "LambdaExpressionToTypeid" } +module LambdaExpressionToTypeidConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof LambdaExpression } - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof LambdaExpression } - - override predicate isSink(DataFlow::Node sink) { - exists(TypeidOperator op | op.getExpr() = sink.asExpr()) - } + predicate isSink(DataFlow::Node sink) { exists(TypeidOperator op | op.getExpr() = sink.asExpr()) } } -from DataFlow::PathNode source, DataFlow::PathNode sink +module LambdaExpressionToTypeidFlow = DataFlow::Global; + +from LambdaExpressionToTypeidFlow::PathNode source, LambdaExpressionToTypeidFlow::PathNode sink where not isExcluded(source.getNode().asExpr(), LambdasPackage::lambdaPassedToTypeidQuery()) and - any(LambdaExpressionToTypeid config).hasFlowPath(source, sink) + LambdaExpressionToTypeidFlow::flowPath(source, sink) select sink.getNode(), source, sink, "Lambda $@ passed as operand to typeid operator.", source.getNode(), "expression" diff --git a/cpp/autosar/src/rules/A5-1-9/IdenticalLambdaExpressions.ql b/cpp/autosar/src/rules/A5-1-9/IdenticalLambdaExpressions.ql index 8717fd000e..1520955716 100644 --- a/cpp/autosar/src/rules/A5-1-9/IdenticalLambdaExpressions.ql +++ b/cpp/autosar/src/rules/A5-1-9/IdenticalLambdaExpressions.ql @@ -24,6 +24,14 @@ where not lambdaExpression = otherLambdaExpression and not lambdaExpression.isFromTemplateInstantiation(_) and not otherLambdaExpression.isFromTemplateInstantiation(_) and - getLambdaHashCons(lambdaExpression) = getLambdaHashCons(otherLambdaExpression) + getLambdaHashCons(lambdaExpression) = getLambdaHashCons(otherLambdaExpression) and + // Do not report lambdas produced by the same macro in different invocations + not exists(Macro m, MacroInvocation m1, MacroInvocation m2 | + m1 = m.getAnInvocation() and + m2 = m.getAnInvocation() and + not m1 = m2 and // Lambdas in the same macro can be reported + m1.getAnExpandedElement() = lambdaExpression and + m2.getAnExpandedElement() = otherLambdaExpression + ) select lambdaExpression, "Lambda expression is identical to $@ lambda expression.", otherLambdaExpression, "this" diff --git a/cpp/autosar/src/rules/A5-1-9/LambdaEquivalence.qll b/cpp/autosar/src/rules/A5-1-9/LambdaEquivalence.qll index 110c7c734d..cab93608c5 100644 --- a/cpp/autosar/src/rules/A5-1-9/LambdaEquivalence.qll +++ b/cpp/autosar/src/rules/A5-1-9/LambdaEquivalence.qll @@ -211,7 +211,7 @@ private module HashCons { private newtype HC_Params = HC_NoParams() or - HC_ParamCons(HashConsExpr hc, int i, HC_Params list) { mk_ParamCons(hc, i, list, _) } + HC_ParamCons(Type t, string name, int i, HC_Params list) { mk_ParamCons(t, name, i, list, _) } /** * HashConsExpr is the hash-cons of an expression. The relationship between `Expr` @@ -624,11 +624,21 @@ private module HashCons { strictcount(access.getTarget()) = 1 } + /** + * Gets the name of a variable. + * + * Extracted for performance reasons, to avoid magic, which was causing performance issues in getParameter(int i). + */ + pragma[nomagic] + private string getVariableName(Variable v) { result = v.getName() } + /* Note: This changed from the original HashCons module to be able to find structural equivalent expression. */ private predicate mk_Variable(Type t, string name, VariableAccess access) { analyzableVariable(access) and exists(Variable v | - v = access.getTarget() and t = v.getUnspecifiedType() and name = v.getName() + v = access.getTarget() and + t = v.getUnspecifiedType() and + name = getVariableName(v) ) } @@ -989,7 +999,7 @@ private module HashCons { analyzableClassAggregateLiteral(cal) and cal.getUnspecifiedType() = c and exists(Expr e | - e = cal.getFieldExpr(f).getFullyConverted() and + e = cal.getAFieldExpr(f).getFullyConverted() and f.getInitializationOrder() = i and ( hc = hashConsExpr(e) and @@ -1005,9 +1015,9 @@ private module HashCons { private predicate analyzableClassAggregateLiteral(ClassAggregateLiteral cal) { forall(int i | exists(cal.getChild(i)) | strictcount(cal.getChild(i).getFullyConverted()) = 1 and - strictcount(Field f | cal.getChild(i) = cal.getFieldExpr(f)) = 1 and + strictcount(Field f | cal.getChild(i) = cal.getAFieldExpr(f)) = 1 and strictcount(Field f, int j | - cal.getFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder() + cal.getAFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder() ) = 1 ) } @@ -1104,7 +1114,14 @@ private module HashCons { nee.getExpr().getFullyConverted() = child.getAnExpr() } - private predicate mk_StmtCons(HashConsStmt hc, int i, HC_Stmts list, BlockStmt block) { + private class LambdaBlockStmt extends BlockStmt { + LambdaBlockStmt() { + // Restricting to statements inside a lambda expressions. + this.getParentScope*() = any(LambdaExpression le).getLambdaFunction() + } + } + + private predicate mk_StmtCons(HashConsStmt hc, int i, HC_Stmts list, LambdaBlockStmt block) { hc = hashConsStmt(block.getStmt(i)) and ( exists(HashConsStmt head, HC_Stmts tail | @@ -1118,13 +1135,13 @@ private module HashCons { } private predicate mk_StmtConsInner( - HashConsStmt head, HC_Stmts tail, int i, HC_Stmts list, BlockStmt block + HashConsStmt head, HC_Stmts tail, int i, HC_Stmts list, LambdaBlockStmt block ) { list = HC_StmtCons(head, i, tail) and mk_StmtCons(head, i, tail, block) } - private predicate mk_BlockStmtCons(HC_Stmts hc, BlockStmt s) { + private predicate mk_BlockStmtCons(HC_Stmts hc, LambdaBlockStmt s) { if s.getNumStmt() > 0 then exists(HashConsStmt head, HC_Stmts tail | @@ -1275,13 +1292,14 @@ private module HashCons { mk_DeclConsInner(_, _, s.getNumDeclarations() - 1, hc, s) } - private predicate mk_ParamCons(HashConsExpr hc, int i, HC_Params list, Function f) { - hc = hashConsExpr(f.getParameter(i).getAnAccess()) and - ( - exists(HashConsExpr head, HC_Params tail | - mk_ParamConsInner(head, tail, i - 1, list, f) and - i > 0 - ) + private predicate mk_ParamCons(Type t, string name, int i, HC_Params list, Function f) { + exists(Parameter p | + p = f.getParameter(i) and + t = p.getType() and + name = p.getName() + | + mk_ParamConsInner(_, _, _, i - 1, list, f) and + i > 0 or i = 0 and list = HC_NoParams() @@ -1289,10 +1307,10 @@ private module HashCons { } private predicate mk_ParamConsInner( - HashConsExpr head, HC_Params tail, int i, HC_Params list, Function f + Type t, string name, HC_Params tail, int i, HC_Params list, Function f ) { - list = HC_ParamCons(head, i, tail) and - mk_ParamCons(head, i, tail, f) + list = HC_ParamCons(t, name, i, tail) and + mk_ParamCons(t, name, i, tail, f) } private predicate mk_FunctionCons( @@ -1302,7 +1320,7 @@ private module HashCons { name = f.getName() and body = hashConsStmt(f.getBlock()) and if f.getNumberOfParameters() > 0 - then mk_ParamConsInner(_, _, f.getNumberOfParameters() - 1, params, f) + then mk_ParamConsInner(_, _, _, f.getNumberOfParameters() - 1, params, f) else params = HC_NoParams() } @@ -1486,8 +1504,6 @@ private module HashCons { cached HashConsStmt hashConsStmt(Stmt s) { - // Restricting to statements inside a lambda expressions. - s.getParentScope*() = any(LambdaExpression le).getLambdaFunction() and exists(HC_Stmts list | mk_BlockStmtCons(list, s) and result = HC_BlockStmt(list) diff --git a/cpp/autosar/src/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.ql b/cpp/autosar/src/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.ql index 2289dc4e79..825347754d 100644 --- a/cpp/autosar/src/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.ql +++ b/cpp/autosar/src/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.ql @@ -16,19 +16,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.potentiallyvirtualpointeronlycomparestonullptr.PotentiallyVirtualPointerOnlyComparesToNullptr -from - EqualityOperation equalityComparison, MemberFunction virtualFunction, - FunctionAccess accessOperand, Expr otherOperand -where - not isExcluded(equalityComparison, - PointersPackage::pointerToMemberVirtualFunctionWithNullPointerConstantQuery()) and - virtualFunction.isVirtual() and - equalityComparison.getAnOperand() = accessOperand and - accessOperand.getTarget() = virtualFunction and - otherOperand = equalityComparison.getAnOperand() and - not otherOperand = accessOperand and - not otherOperand.getType() instanceof NullPointerType -select equalityComparison, - "A pointer to member virtual function $@ is tested for equality with non-null-pointer-constant $@. ", - virtualFunction, virtualFunction.getName(), otherOperand, otherOperand.toString() +class PointerToMemberVirtualFunctionWithNullPointerConstantQuery extends PotentiallyVirtualPointerOnlyComparesToNullptrSharedQuery +{ + PointerToMemberVirtualFunctionWithNullPointerConstantQuery() { + this = PointersPackage::pointerToMemberVirtualFunctionWithNullPointerConstantQuery() + } +} diff --git a/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql b/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql index 0b6962c06e..66a289dfe0 100644 --- a/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql +++ b/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql @@ -15,10 +15,79 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.Macro -from CStyleCast c +/** + * Gets the macro (if any) that generated the given `CStyleCast`. + * + * If there are nested macro invocations, we identify the most specific macro that generated the + * cast. + */ +Macro getGeneratedFrom(CStyleCast c) { + exists(MacroInvocation mi | + mi = result.getAnInvocation() and + mi.getAGeneratedElement() = c and + mi.getLocation().getStartColumn() = c.getLocation().getStartColumn() and + not exists(MacroInvocation child | + child.getParentInvocation() = mi and + child.getAGeneratedElement() = c + ) + ) +} + +/* + * In theory this query should exclude casts using the "functional notation" syntax, e.g. + * ``` + * int(x); + * ``` + * This is because this is not a C-style cast, as it is not legitimate C code. However, our database + * schema does not distinguish between C-style casts and functional casts, so we cannot exclude just + * those. + * + * In addition, we do not get `CStyleCasts` in cases where the cast is converted to a `ConstructorCall`. + * This holds for both the "functional notation" syntax and the "c-style" syntax, e.g. both of these + * are represented in our model as `ConstructorCall`s only: + * ``` + * class A { public: A(int); }; + * void create() { + * (A)1; + * A(1); + * } + * ``` + * + * As a consequence this query: + * - Produces false positives when primitive types are cast using the "functional notation" syntax. + * - Produces false negatives when a C-style cast is converted to a `ConstructorCall` e.g. when the + * argument type is compatible with a single-argument constructor. + */ + +from CStyleCast c, string extraMessage, Locatable l, string supplementary where not isExcluded(c, BannedSyntaxPackage::traditionalCStyleCastsUsedQuery()) and not c.isImplicit() and - not c.getType() instanceof UnknownType -select c, "Use of explicit C-style Cast" + not c.getType() instanceof UnknownType and + // For casts in templates that occur on types related to a template parameter, the copy of th + // cast in the uninstantiated template is represented as a `CStyleCast` even if in practice all + // the instantiations represent it as a `ConstructorCall`. To avoid the common false positive case + // of using the functional cast notation to call a constructor we exclude all `CStyleCast`s on + // uninstantiated templates, and instead rely on reporting results within instantiations. + not c.isFromUninstantiatedTemplate(_) and + // Exclude casts created from macro invocations of macros defined by third parties + not getGeneratedFrom(c) instanceof LibraryMacro and + // If the cast was generated from a user-provided macro, then report the macro that generated the + // cast, as the macro itself may have generated the cast + if getGeneratedFrom(c) instanceof UserProvidedMacro + then + extraMessage = " generated from macro $@" and + // Add macro as explanatory link + l = getGeneratedFrom(c) and + supplementary = getGeneratedFrom(c).getName() + else ( + // No extra message required + extraMessage = "" and + // No explanatory link required, but we still need to set these to valid values + l = c and + supplementary = "" + ) +select c, "Use of explicit c-style cast to " + c.getType().getName() + extraMessage + ".", l, + supplementary diff --git a/cpp/autosar/src/rules/A5-2-3/RemoveConstOrVolatileQualificationAutosar.ql b/cpp/autosar/src/rules/A5-2-3/RemoveConstOrVolatileQualificationAutosar.ql index d92045eac7..47a9b49f1b 100644 --- a/cpp/autosar/src/rules/A5-2-3/RemoveConstOrVolatileQualificationAutosar.ql +++ b/cpp/autosar/src/rules/A5-2-3/RemoveConstOrVolatileQualificationAutosar.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.removeconstorvolatilequalification.RemoveConstOrVolatileQualification -class RemoveConstOrVolatileQualificationAutosarQuery extends RemoveConstOrVolatileQualificationSharedQuery { +class RemoveConstOrVolatileQualificationAutosarQuery extends RemoveConstOrVolatileQualificationSharedQuery +{ RemoveConstOrVolatileQualificationAutosarQuery() { this = ConstPackage::removeConstOrVolatileQualificationAutosarQuery() } diff --git a/cpp/autosar/src/rules/A5-2-4/ReinterpretCastUsed.ql b/cpp/autosar/src/rules/A5-2-4/ReinterpretCastUsed.ql index 92cf12d8a0..bf5805698d 100644 --- a/cpp/autosar/src/rules/A5-2-4/ReinterpretCastUsed.ql +++ b/cpp/autosar/src/rules/A5-2-4/ReinterpretCastUsed.ql @@ -16,7 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.reinterpretcastused.ReinterpretCastUsed -from ReinterpretCast rc -where not isExcluded(rc, BannedSyntaxPackage::reinterpretCastUsedQuery()) -select rc, "Use of reinterpret_cast." +class ReinterpretCastUsedQuery extends ReinterpretCastUsedSharedQuery { + ReinterpretCastUsedQuery() { this = BannedSyntaxPackage::reinterpretCastUsedQuery() } +} diff --git a/cpp/autosar/src/rules/A5-2-5/ContainerAccessWithoutRangeCheckAutosar.ql b/cpp/autosar/src/rules/A5-2-5/ContainerAccessWithoutRangeCheckAutosar.ql index 1d7fef1685..48e350af7c 100644 --- a/cpp/autosar/src/rules/A5-2-5/ContainerAccessWithoutRangeCheckAutosar.ql +++ b/cpp/autosar/src/rules/A5-2-5/ContainerAccessWithoutRangeCheckAutosar.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.containeraccesswithoutrangecheck.ContainerAccessWithoutRangeCheck -class ContainerAccessWithoutRangeCheckAutosarQuery extends ContainerAccessWithoutRangeCheckSharedQuery { +class ContainerAccessWithoutRangeCheckAutosarQuery extends ContainerAccessWithoutRangeCheckSharedQuery +{ ContainerAccessWithoutRangeCheckAutosarQuery() { this = OutOfBoundsPackage::containerAccessWithoutRangeCheckAutosarQuery() } diff --git a/cpp/autosar/src/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.ql b/cpp/autosar/src/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.ql index dd63288587..b2c3120556 100644 --- a/cpp/autosar/src/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.ql +++ b/cpp/autosar/src/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.ql @@ -17,11 +17,20 @@ import cpp import codingstandards.cpp.autosar -from BinaryLogicalOperation op, BinaryOperation binop +from BinaryLogicalOperation op, BinaryOperation binop, string leftOrRight where not isExcluded(op, OrderOfEvaluationPackage::operandsOfALogicalAndOrNotParenthesizedQuery()) and - op.getAnOperand() = binop and + ( + op.getLeftOperand() = binop and + leftOrRight = "Left" + or + op.getRightOperand() = binop and + leftOrRight = "Right" + ) and + // Ignore cases with the same operator + not op.getOperator() = binop.getOperator() and not exists(ParenthesisExpr p | p = binop.getFullyConverted()) and // Exclude binary operations expanded by a macro. not binop.isInMacroExpansion() -select op, "Binary $@ operand of logical operation is not parenthesized.", binop, "operator" +select op, "$@ of logical operation " + op.getOperator() + " is not parenthesized.", binop, + leftOrRight + " operand " + binop.getOperator() diff --git a/cpp/autosar/src/rules/A5-3-2/NullPointersDereferenced.ql b/cpp/autosar/src/rules/A5-3-2/NullPointersDereferenced.ql index 5ee527943b..e342c53d00 100644 --- a/cpp/autosar/src/rules/A5-3-2/NullPointersDereferenced.ql +++ b/cpp/autosar/src/rules/A5-3-2/NullPointersDereferenced.ql @@ -14,11 +14,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.lifetimes.lifetimeprofile.LifetimeProfile +import codingstandards.cpp.rules.dereferenceofnullpointer.DereferenceOfNullPointer -from NullDereference nd, NullReason nr, string message, Element explanation, string explanationDesc -where - not isExcluded(nd, NullPackage::nullPointersDereferencedQuery()) and - nr = nd.getAnInvalidReason() and - nr.hasMessage(message, explanation, explanationDesc) -select nd, "Null may be dereferenced here " + message, explanation, explanationDesc +class NullPointersDereferencedQuery extends DereferenceOfNullPointerSharedQuery { + NullPointersDereferencedQuery() { this = NullPackage::nullPointersDereferencedQuery() } +} diff --git a/cpp/autosar/src/rules/A5-5-1/NullPointerToMemberAccessNonExistentClassMembers.ql b/cpp/autosar/src/rules/A5-5-1/NullPointerToMemberAccessNonExistentClassMembers.ql index 3fede7950e..7f6a8dae1a 100644 --- a/cpp/autosar/src/rules/A5-5-1/NullPointerToMemberAccessNonExistentClassMembers.ql +++ b/cpp/autosar/src/rules/A5-5-1/NullPointerToMemberAccessNonExistentClassMembers.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.accessofundefinedmemberthroughnullpointer.AccessOfUndefinedMemberThroughNullPointer -class NullPointerToMemberAccessNonExistentClassMembersQuery extends AccessOfUndefinedMemberThroughNullPointerSharedQuery { +class NullPointerToMemberAccessNonExistentClassMembersQuery extends AccessOfUndefinedMemberThroughNullPointerSharedQuery +{ NullPointerToMemberAccessNonExistentClassMembersQuery() { this = PointersPackage::nullPointerToMemberAccessNonExistentClassMembersQuery() } diff --git a/cpp/autosar/src/rules/A5-5-1/PointerToMemberAccessNonExistentClassMembers.ql b/cpp/autosar/src/rules/A5-5-1/PointerToMemberAccessNonExistentClassMembers.ql index 768c7f6699..e3a78ce30c 100644 --- a/cpp/autosar/src/rules/A5-5-1/PointerToMemberAccessNonExistentClassMembers.ql +++ b/cpp/autosar/src/rules/A5-5-1/PointerToMemberAccessNonExistentClassMembers.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.accessofnonexistingmemberthroughpointertomember.AccessOfNonExistingMemberThroughPointerToMember -class PointerToMemberAccessNonExistentClassMembersQuery extends AccessOfNonExistingMemberThroughPointerToMemberSharedQuery { +class PointerToMemberAccessNonExistentClassMembersQuery extends AccessOfNonExistingMemberThroughPointerToMemberSharedQuery +{ PointerToMemberAccessNonExistentClassMembersQuery() { this = PointersPackage::pointerToMemberAccessNonExistentClassMembersQuery() } diff --git a/cpp/autosar/src/rules/A5-5-1/UninitializedStaticPointerToMemberUse.ql b/cpp/autosar/src/rules/A5-5-1/UninitializedStaticPointerToMemberUse.ql index 8802b4fabb..b6aa698e65 100644 --- a/cpp/autosar/src/rules/A5-5-1/UninitializedStaticPointerToMemberUse.ql +++ b/cpp/autosar/src/rules/A5-5-1/UninitializedStaticPointerToMemberUse.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.accessofundefinedmemberthroughuninitializedstaticpointer.AccessOfUndefinedMemberThroughUninitializedStaticPointer -class UninitializedStaticPointerToMemberUseQuery extends AccessOfUndefinedMemberThroughUninitializedStaticPointerSharedQuery { +class UninitializedStaticPointerToMemberUseQuery extends AccessOfUndefinedMemberThroughUninitializedStaticPointerSharedQuery +{ UninitializedStaticPointerToMemberUseQuery() { this = PointersPackage::uninitializedStaticPointerToMemberUseQuery() } diff --git a/cpp/autosar/src/rules/A6-6-1/GotoStatementUsed.ql b/cpp/autosar/src/rules/A6-6-1/GotoStatementUsed.ql index 5e1c10e4c7..03b891e6db 100644 --- a/cpp/autosar/src/rules/A6-6-1/GotoStatementUsed.ql +++ b/cpp/autosar/src/rules/A6-6-1/GotoStatementUsed.ql @@ -16,9 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.gotostatementshouldnotbeused.GotoStatementShouldNotBeUsed -from Stmt s -where - not isExcluded(s, BannedSyntaxPackage::gotoStatementUsedQuery()) and - (s instanceof GotoStmt or s instanceof ComputedGotoStmt) -select s, "Use of goto." +class GotoStatementUsedQuery extends GotoStatementShouldNotBeUsedSharedQuery { + GotoStatementUsedQuery() { this = BannedSyntaxPackage::gotoStatementUsedQuery() } +} diff --git a/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedObjectMissingConstSpecifier.ql b/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedObjectMissingConstSpecifier.ql index d85a638530..b961acce64 100644 --- a/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedObjectMissingConstSpecifier.ql +++ b/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedObjectMissingConstSpecifier.ql @@ -36,5 +36,9 @@ where else cond = " is used for an object" ) and not exists(LambdaExpression lc | lc.getACapture().getField() = v) and - not v.isFromUninstantiatedTemplate(_) + not v.isFromUninstantiatedTemplate(_) and + not v.isCompilerGenerated() and + not v.getType() instanceof RValueReferenceType and + //if the instantiation is not constexpr but the template is, still exclude it as a candidate + not exists(TemplateVariable b | b.getAnInstantiation() = v and b.isConstexpr()) select v, "Non-constant variable " + v.getName() + cond + " and is not modified." diff --git a/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.ql b/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.ql deleted file mode 100644 index d4ecbdebfa..0000000000 --- a/cpp/autosar/src/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.ql +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @id cpp/autosar/declaration-unmodified-param-missing-const-specifier - * @name A7-1-1: Constexpr or const specifiers shall be used for immutable parameter usage - * @description `Constexpr`/`const` specifiers prevent unintentional data modification for - * parameters intended as immutable. - * @kind problem - * @precision high - * @problem.severity warning - * @tags external/autosar/id/a7-1-1 - * correctness - * maintainability - * readability - * external/autosar/allocated-target/implementation - * external/autosar/enforcement/automated - * external/autosar/obligation/required - */ - -import cpp -import codingstandards.cpp.autosar -import codingstandards.cpp.ConstHelpers - -from FunctionParameter v, string cond -where - not isExcluded(v, ConstPackage::declarationUnmodifiedParamMissingConstSpecifierQuery()) and - v instanceof AccessedParameter and - ( - isNotDirectlyModified(v) and - not v.getAnAccess().isAddressOfAccessNonConst() and - notPassedAsArgToNonConstParam(v) and - notAssignedToNonLocalNonConst(v) and - if v instanceof NonConstPointerorReferenceParameter - then - notUsedAsQualifierForNonConst(v) and - notReturnedFromNonConstFunction(v) and - cond = " points to an object" - else cond = " is used for an object" - ) and - //exclude already consts - if v.getType() instanceof ReferenceType - then not v.getType().(DerivedType).getBaseType+().isConst() - else not v.getType().isConst() -select v, "Non-constant parameter " + v.getName() + cond + " and is not modified." diff --git a/cpp/autosar/src/rules/A7-1-2/FunctionMissingConstexpr.ql b/cpp/autosar/src/rules/A7-1-2/FunctionMissingConstexpr.ql deleted file mode 100644 index 1cd68447fe..0000000000 --- a/cpp/autosar/src/rules/A7-1-2/FunctionMissingConstexpr.ql +++ /dev/null @@ -1,160 +0,0 @@ -/** - * @id cpp/autosar/function-missing-constexpr - * @name A7-1-2: The constexpr specifier shall be used for functions whose return value can be determined at compile time - * @description Using 'constexpr' makes it clear that a function is intended to return a compile - * time constant. - * @kind problem - * @precision high - * @problem.severity recommendation - * @tags external/autosar/id/a7-1-2 - * maintainability - * external/autosar/allocated-target/implementation - * external/autosar/enforcement/automated - * external/autosar/obligation/required - */ - -import cpp -import codingstandards.cpp.autosar -import codingstandards.cpp.TrivialType - -/** Gets a non-variant member field. */ -Field getANonVariantField(Class c) { - result = c.getAField() and - result.isInitializable() and - not result instanceof AnonymousUnionField -} - -/** - * A `Field` holding an "anonymous union" as described by `[class.union]`. - * - * For example, the union in this example: - * ``` - * class C { - * union { - * int x; - * short y; - * }; - * } - * ``` - */ -class AnonymousUnionField extends Field { - AnonymousUnionField() { hasName("(unknown field)") } - - /** - * Get a direct or indirect union member. - * - * Indirect members can come from nested anonymous unions. - */ - Field getAVariantMember() { - exists(Field f | f = getType().(Union).getAField() | - if f instanceof AnonymousUnionField - then result = f.(AnonymousUnionField).getAVariantMember() - else result = f - ) - } - - /** - * Holds if one variant member of this anonymous union field is initialized using NSDMI. - */ - predicate isExplicitlyInitialized() { exists(getAVariantMember().getInitializer().getExpr()) } -} - -/** - * Get a union which is not initialized by NSDMI. - */ -AnonymousUnionField getAnUninitializedAnonymousUnionField(Class c) { - result = c.getAField() and - not result.isExplicitlyInitialized() -} - -/** - * A function that can be `constexpr` specified according to the constraints for a `constexpr` - * function as specified in `[dcl.constexpr]/3`. - */ -class EffectivelyConstExprFunction extends Function { - EffectivelyConstExprFunction() { - // Not already marked as constexpr - not isDeclaredConstexpr() and - // Not virtual - not isVirtual() and - // Returns a literal type (which can be 'void') - (isLiteralType(getType()) or this instanceof Constructor) and - // Exclude cases that shouldn't be const or can't be const - not this instanceof Destructor and - not this instanceof CopyAssignmentOperator and - not this instanceof MoveAssignmentOperator and - not this.isCompilerGenerated() and - // All parameters are literal types - forall(Parameter p | p = getAParameter() | isLiteralType(p.getType())) and - // The function body is either deleted, defaulted or does not include one of the precluding - // statement kinds and is both side-effect free and created by the user - ( - isDeleted() - or - isDefaulted() - or - not this = any(AsmStmt a).getEnclosingFunction() and - not this = any(GotoStmt g).getEnclosingFunction() and - not this = any(TryStmt t).getEnclosingFunction() and - not exists(LocalVariable lv | this = lv.getFunction() | - not isLiteralType(lv.getType()) - or - lv instanceof StaticStorageDurationVariable - or - lv.isThreadLocal() - or - not exists(lv.getInitializer().getExpr()) - ) and - // For `constexpr` functions, the compiler only checks the rules above - it doesn't check - // whether the function can be evaluated as a compile time constant until the function is used, - // and then only confirms that it evaluates to a compile-time constant for a specific set of - // arguments used in another constexpr calculation. We approximate this by identifying the set - // of functions that are (conservatively) side-effect free. - isSideEffectFree() and - // "User defined" in some way - hasDefinition() and - not isCompilerGenerated() - ) and - ( - // A constructor should satisfy the constraints as specified in `[dcl.constexpr]/4`. - this instanceof Constructor - implies - ( - // No virtual base class - not getDeclaringType().getDerivation(_).isVirtual() and - ( - // All non-variant members initialized by this constructor - forall(Field f | f = getANonVariantField(getDeclaringType()) | - exists(ConstructorFieldInit cfi | - // Even if this field has a `getInitializer()` a `ConstructorFieldInit` will also be - // present on each constructor - cfi.getEnclosingFunction() = this and cfi.getTarget() = f - ) - ) and - // At least one variant member is initialized for each `AnonymousUnionField` which is not - // initialized with a `Field.getInitializer()`. This is different to the non-variant - // member case above - forall(AnonymousUnionField f | - f = getAnUninitializedAnonymousUnionField(getDeclaringType()) - | - exists(ConstructorFieldInit cfi | - cfi.getEnclosingFunction() = this and cfi.getTarget() = f.getAVariantMember() - ) - ) - or - // The function is deleted or defaulted, and every field has an NSDMI, and there are no - // uninitialized anonymous union fields - (isDeleted() or isDefaulted()) and - forall(Field f | f = getANonVariantField(getDeclaringType()) | - exists(f.getInitializer().getExpr()) - ) and - not exists(getAnUninitializedAnonymousUnionField(getDeclaringType())) - ) - ) - ) - } -} - -from EffectivelyConstExprFunction ecef -where not isExcluded(ecef, ConstPackage::functionMissingConstexprQuery()) -select ecef, ecef.getName() + " function could be marked as 'constexpr'." diff --git a/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql b/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql index e3981b3836..af3a00fadb 100644 --- a/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql +++ b/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql @@ -15,8 +15,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import codingstandards.cpp.SideEffect +import semmle.code.cpp.controlflow.SSA +import codingstandards.cpp.Expr predicate isZeroInitializable(Variable v) { not exists(v.getInitializer().getExpr()) and @@ -33,20 +35,32 @@ predicate isTypeZeroInitializable(Type t) { t.getUnderlyingType() instanceof ArrayType } -from Variable v +from Variable v, string msg where not isExcluded(v, ConstPackage::variableMissingConstexprQuery()) and v.hasDefinition() and not v.isConstexpr() and not v instanceof Parameter and not v.isAffectedByMacro() and + ( + not v instanceof MemberVariable + or + // In case member functions are left un-instantiated, it is possible + // the member variable could be modified in them. + // Hence, don't raise an alert in case this member variable's class + // has a member function that doesn't have a definition. + not exists(MemberFunction mf | + mf.getDeclaringType() = v.getDeclaringType() and + mf.isFromUninstantiatedTemplate(_) + ) + ) and isLiteralType(v.getType()) and // Unfortunately, `isConstant` is not sufficient here because it doesn't include calls to // constexpr constructors, and does not take into account zero initialization ( v.getInitializer().getExpr().isConstant() or - v.getInitializer().getExpr().(Call).getTarget().isConstexpr() + any(Call call | isCompileTimeEvaluatedCall(call)) = v.getInitializer().getExpr() or isZeroInitializable(v) or @@ -60,5 +74,10 @@ where // Not assigned by a user in a constructor not exists(ConstructorFieldInit cfi | cfi.getTarget() = v and not cfi.isCompilerGenerated()) and // Ignore union members - not v.getDeclaringType() instanceof Union -select v, "Variable " + v.getName() + " could be marked 'constexpr'." + not v.getDeclaringType() instanceof Union and + // Exclude variables in uninstantiated templates, as they may be incomplete + not v.isFromUninstantiatedTemplate(_) and + // Exclude compiler generated variables, which are not user controllable + not v.isCompilerGenerated() and + if v instanceof MemberVariable and not v.isStatic() then msg = " and static." else msg = "." +select v, "Variable '" + v.getName() + "' could be marked 'constexpr'" + msg diff --git a/cpp/autosar/src/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.ql b/cpp/autosar/src/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.ql index 54968dc223..5d34f89c7d 100644 --- a/cpp/autosar/src/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.ql +++ b/cpp/autosar/src/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.ql @@ -20,37 +20,50 @@ import cpp import codingstandards.cpp.autosar /** - * Holds if declaration `e` using a `TypedefType` is CV-qualified - * - * For example, given `using intconstptr = int * const`: - * the predicate holds for `const/volatile intconstptr ptr1`, but not for `intconstptr ptr2` + * Unwrap layers of indirection that occur on the right side of the type. */ -predicate containsExtraSpecifiers(VariableDeclarationEntry e) { - e.getType().toString().matches("const %") or - e.getType().toString().matches("volatile %") +Type unwrapIndirection(Type type) { + if type instanceof DerivedType and not type instanceof SpecifiedType + then result = unwrapIndirection(type.(DerivedType).getBaseType()) + else result = type } // DeclStmts that have a TypedefType name use (ie TypeMention) in them //AND TypeMention.getStartColumn() - DeclStmt.getStartColumn() > len(const) //AND the declared thing contains one of these "extra" specifiers in the DeclarationEntry Location -from VariableDeclarationEntry e, TypedefType t, TypeMention tm +from + VariableDeclarationEntry e, TypedefType t, TypeMention tm, string message, Element explainer, + string explainerMessage where not isExcluded(e, ConstPackage::cvQualifiersNotPlacedOnTheRightHandSideQuery()) and - containsExtraSpecifiers(e) and + // Variable type is specified, and has the typedef type as a base type + unwrapIndirection(e.getType()).(SpecifiedType).getBaseType() = t and exists(string filepath, int startline | e.getLocation().hasLocationInfo(filepath, startline, _, _, _) and tm.getLocation().hasLocationInfo(filepath, startline, _, _, _) and e = t.getATypeNameUse() and tm.getMentionedType() = t and + // TypeMention occurs before the variable declaration + tm.getLocation().getStartColumn() < e.getLocation().getStartColumn() and exists(DeclStmt s | s.getDeclarationEntry(_) = e and - //const could fit in there + // TypeMention occurs after the start of the StmtDecl, with enough space for const/volatile tm.getLocation().getStartColumn() - s.getLocation().getStartColumn() > 5 - //volatile could fit in there - //but the above condition subsumes this one - //l.getStartColumn() - tm.getLocation().getStartColumn() > 8 ) + ) and + if exists(t.getFile().getRelativePath()) + then + message = + "There is possibly a const or volatile specifier on the left hand side of typedef name $@." and + explainer = t and + explainerMessage = t.getName() + else ( + // Type occurs outside source root, so don't link + message = + "There is possibly a const or volatile specifier on the left hand side of typedef name " + + t.getName() + "." and + // explainer not used in this case + explainer = e and + explainerMessage = "" ) -select e, - "There is possibly a const or volatile specifier on the left hand side of typedef name $@.", t, - t.getName() +select e, message, explainer, explainerMessage diff --git a/cpp/autosar/src/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.ql b/cpp/autosar/src/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.ql index b3da12685c..7c91ade133 100644 --- a/cpp/autosar/src/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.ql +++ b/cpp/autosar/src/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.ql @@ -19,9 +19,8 @@ import cpp import codingstandards.cpp.autosar -// for readability we define a "fundamental" type -class FundamentalType extends Type { - FundamentalType() { this instanceof BuiltInType } +class FundamentalType extends BuiltInType { + FundamentalType() { not this instanceof ErroneousType and not this instanceof UnknownType } } from Variable v @@ -33,12 +32,16 @@ where // exclude uninstantiated templates and rely on the instantiated templates, because an uninstantiated template may not contain the information required to determine if the usage is allowed. not v.isFromUninstantiatedTemplate(_) and not ( - // find ones where + // Initialized by function call v.getInitializer().getExpr() instanceof FunctionCall or + // Initialized by lambda expression v.getInitializer().getExpr() instanceof LambdaExpression or - v.getInitializer().getExpr() instanceof ClassAggregateLiteral - ) + // Initialized by non-fundamental type + not v.getInitializer().getExpr().getType() instanceof FundamentalType + ) and + // Exclude compiler generated variables + not v.isCompilerGenerated() select v, "Use of auto in variable definition is not the result of a function call, lambda expression, or non-fundamental type initializer." diff --git a/cpp/autosar/src/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.ql b/cpp/autosar/src/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.ql index d33d3d8e71..addd8af697 100644 --- a/cpp/autosar/src/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.ql +++ b/cpp/autosar/src/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.ql @@ -19,28 +19,28 @@ import codingstandards.cpp.autosar class UniqueLineStmt extends Locatable { UniqueLineStmt() { not isAffectedByMacro() and - exists(Declaration d | - this = d.getADeclarationEntry() and - not d instanceof Parameter and - not d instanceof TemplateParameter and - not d instanceof FunctionTemplateSpecialization and - // TODO - Needs to be enhanced to solve issues with - // templated inner classes. - not d instanceof MemberFunction and - not d.isFromTemplateInstantiation(_) and - not d.(Function).isCompilerGenerated() and - not d.(Variable).isCompilerGenerated() and - not exists(RangeBasedForStmt f | f.getADeclaration() = d) and - not exists(DeclStmt declStmt, ForStmt f | - f.getInitialization() = declStmt and - declStmt.getADeclaration() = d - ) and - not exists(LambdaCapture lc | lc.getField().getADeclarationEntry() = this) + ( + exists(Declaration d | + this = d.getADeclarationEntry() and + not d instanceof Parameter and + not d instanceof TypeTemplateParameter and + // TODO - Needs to be enhanced to solve issues with + // templated inner classes. + not d instanceof Function and + not d.isFromTemplateInstantiation(_) and + not d.(Variable).isCompilerGenerated() and + not exists(RangeBasedForStmt f | f.getADeclaration() = d) and + not exists(DeclStmt declStmt, ForStmt f | + f.getInitialization() = declStmt and + declStmt.getADeclaration() = d + ) and + not exists(LambdaCapture lc | lc.getField().getADeclarationEntry() = this) + ) + or + this instanceof ExprStmt and + not exists(ForStmt f | f.getInitialization().getAChild*() = this) and + not exists(LambdaExpression l | l.getLambdaFunction().getBlock().getAChild*() = this) ) - or - this instanceof ExprStmt and - not exists(ForStmt f | f.getInitialization().getAChild*() = this) and - not exists(LambdaExpression l | l.getLambdaFunction().getBlock().getAChild*() = this) } } @@ -52,11 +52,12 @@ where DeclarationsPackage::identifierDeclarationAndInitializationNotOnSeparateLinesQuery()) and not e1 = e2 and not e1.(DeclarationEntry) = e2 and - exists(Location l1, Location l2 | - e1.getLocation() = l1 and - e2.getLocation() = l2 and - not l1 = l2 and - l1.getFile() = l2.getFile() and - l1.getStartLine() = l2.getStartLine() + //omit the cases where there is one struct identifier on a struct var line used with typedef + not exists(Struct s | s.getADeclarationEntry() = e1 and e1 instanceof TypeDeclarationEntry) and + not exists(Struct s | s.getATypeNameUse() = e1 and e1 instanceof TypeDeclarationEntry) and + exists(string file, int startline | + e1.getLocation().hasLocationInfo(file, startline, _, _, _) and + e2.getLocation().hasLocationInfo(file, startline, _, _, _) and + not e1.getLocation() = e2.getLocation() ) select e1, "Expression statement and identifier are on the same line." diff --git a/cpp/autosar/src/rules/A7-2-1/NonEnumeratorEnumValue.ql b/cpp/autosar/src/rules/A7-2-1/NonEnumeratorEnumValue.ql index 9b41c97129..f4dcd7f32e 100644 --- a/cpp/autosar/src/rules/A7-2-1/NonEnumeratorEnumValue.ql +++ b/cpp/autosar/src/rules/A7-2-1/NonEnumeratorEnumValue.ql @@ -47,7 +47,7 @@ where then description = "Cast to enum $@ with from expression with value " + c.getExpr().getValue().toFloat() + - "_+ which is not one of the enumerator values in function " + + " which is not one of the enumerator values in function " + c.getEnclosingFunction().getName() + "." else if exists(upperBound(c.getExpr())) diff --git a/cpp/autosar/src/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.ql b/cpp/autosar/src/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.ql index cf5273f45d..42924945cd 100644 --- a/cpp/autosar/src/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.ql +++ b/cpp/autosar/src/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.ql @@ -17,9 +17,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.enumerationnotdefinedwithanexplicitunderlyingtype.EnumerationNotDefinedWithAnExplicitUnderlyingType -from Enum e -where - not isExcluded(e, DeclarationsPackage::enumerationUnderlyingBaseTypeNotExplicitlyDefinedQuery()) and - not e.hasExplicitUnderlyingType() -select e, "Base type of enumeration is not explicitly specified." +class EnumerationUnderlyingBaseTypeNotExplicitlyDefinedQuery extends EnumerationNotDefinedWithAnExplicitUnderlyingTypeSharedQuery +{ + EnumerationUnderlyingBaseTypeNotExplicitlyDefinedQuery() { + this = DeclarationsPackage::enumerationUnderlyingBaseTypeNotExplicitlyDefinedQuery() + } +} diff --git a/cpp/autosar/src/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.ql b/cpp/autosar/src/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.ql index ac6e1b6ff9..75cb2016b5 100644 --- a/cpp/autosar/src/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.ql +++ b/cpp/autosar/src/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.ql @@ -16,56 +16,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.definitionnotconsideredforunqualifiedlookup.DefinitionNotConsideredForUnqualifiedLookup -/** - * Holds if `functionDecl` is a possible intended target of the `usingDecl`. - */ -pragma[noinline] -predicate isPossibleIntendedTarget( - FunctionDeclarationEntry functionDecl, UsingDeclarationEntry usingDecl -) { - // Extracted to improve the join order. With this approach, we first compute a set of using - // declarations and a set of possible intended targets - functionDecl.getDeclaration().isTopLevel() and - functionDecl.getDeclaration().getQualifiedName() = usingDecl.getDeclaration().getQualifiedName() and - functionDecl.getDeclaration().getNamespace().getParentNamespace*() = usingDecl.getParentScope() +class DefinitionNotConsideredForUnqualifiedLookupQuery extends DefinitionNotConsideredForUnqualifiedLookupSharedQuery +{ + DefinitionNotConsideredForUnqualifiedLookupQuery() { + this = ScopePackage::definitionNotConsideredForUnqualifiedLookupQuery() + } } - -/** - * Holds if `functionDecl` is a possible intended target of the `usingDecl`, and they exist at the - * given locations. - */ -pragma[noinline] -predicate isPossibleIntendedTargetLocation( - FunctionDeclarationEntry functionDecl, UsingDeclarationEntry usingDecl, File usingsFile, - File unavailableFile, int usingsStartLine, int unavailableStartLine -) { - // Extracted to improve the join order. With this approach, we take the set of possible intended - // targets computed in isPossibleIntendedTargets, and compute the files and start lines. - // This helps avoid the join order preferred by the optimiser if this is all written directly in - // the from-where-select, where it will eagerly join: - // - // usingDeclarationEntries -> enclosing files -> all other elements in those files - // - // which is expensive when there are a lot of files with using declarations - isPossibleIntendedTarget(functionDecl, usingDecl) and - usingsFile = usingDecl.getFile() and - unavailableFile = functionDecl.getFile() and - usingsStartLine = usingDecl.getLocation().getStartLine() and - unavailableStartLine = functionDecl.getLocation().getStartLine() -} - -from FunctionDeclarationEntry unavailableDecl, UsingDeclarationEntry usingDecl -where - not isExcluded(unavailableDecl, ScopePackage::definitionNotConsideredForUnqualifiedLookupQuery()) and - exists(File usingsFile, File unavailableFile, int usingsStartLine, int unavailableStartLine | - isPossibleIntendedTargetLocation(unavailableDecl, usingDecl, usingsFile, unavailableFile, - usingsStartLine, unavailableStartLine) and - // An approximation of order where we want the using to preceed the new declaration. - usingsFile = unavailableFile and - usingsStartLine < unavailableStartLine - ) -select unavailableDecl, - "Definition for '" + unavailableDecl.getName() + - "' is not available for unqualified lookup because it is declared after $@", usingDecl, - "using-declaration" diff --git a/cpp/autosar/src/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.ql b/cpp/autosar/src/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.ql index 0083fb0cb4..fd9602f218 100644 --- a/cpp/autosar/src/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.ql +++ b/cpp/autosar/src/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.ql @@ -15,24 +15,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.hiddeninheritednonoverridablememberfunction.HiddenInheritedNonOverridableMemberFunction -from FunctionDeclarationEntry overridingDecl, FunctionDeclarationEntry hiddenDecl -where - not isExcluded(overridingDecl, ScopePackage::hiddenInheritedNonOverridableMemberFunctionQuery()) and - // Check if we are overriding a non-virtual inherited member function - overridingDecl.getName() = hiddenDecl.getName() and - overridingDecl.getDeclaration().getDeclaringType().getABaseClass() = - hiddenDecl.getDeclaration().getDeclaringType() and - not hiddenDecl.getDeclaration().isVirtual() and - // Where the hidden member function isn't explicitly brought in scope through a using declaration. - not exists(UsingDeclarationEntry ude | - ude.getDeclaration() = hiddenDecl.getDeclaration() and - ude.getEnclosingElement() = overridingDecl.getDeclaration().getDeclaringType() and - ude.getLocation().getStartLine() < overridingDecl.getLocation().getStartLine() - ) and - // Exclude compiler generated member functions which include things like copy constructor that hide base class - // copy constructors. - not overridingDecl.getDeclaration().isCompilerGenerated() -select overridingDecl, - "Declaration for member '" + overridingDecl.getName() + - "' hides non-overridable inherited member function $@", hiddenDecl, hiddenDecl.getName() +class HiddenInheritedNonOverridableMemberFunctionQuery extends HiddenInheritedNonOverridableMemberFunctionSharedQuery +{ + HiddenInheritedNonOverridableMemberFunctionQuery() { + this = ScopePackage::hiddenInheritedNonOverridableMemberFunctionQuery() + } +} diff --git a/cpp/autosar/src/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.ql b/cpp/autosar/src/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.ql index 4a252ecb51..aa9105f9de 100644 --- a/cpp/autosar/src/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.ql +++ b/cpp/autosar/src/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.ql @@ -15,41 +15,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.hiddeninheritedoverridablememberfunction.HiddenInheritedOverridableMemberFunction -from FunctionDeclarationEntry overridingDecl, FunctionDeclarationEntry hiddenDecl -where - not isExcluded(overridingDecl, ScopePackage::hiddenInheritedOverridableMemberFunctionQuery()) and - // Check if we are overriding a virtual inherited member function - hiddenDecl.getDeclaration().isVirtual() and - // The overriding declaration hides the hidden declaration if: - ( - // 1. the overriding declaration overrides a function in a base class that is an overload of the hidden declaration - // and the hidden declaration isn't overriden in the same class. - exists(FunctionDeclarationEntry overridenDecl | - overridingDecl.getDeclaration().(MemberFunction).overrides(overridenDecl.getDeclaration()) and - overridenDecl.getDeclaration().getAnOverload() = hiddenDecl.getDeclaration() and - not exists(MemberFunction overridingFunc | - hiddenDecl.getDeclaration().(MemberFunction).getAnOverridingFunction() = overridingFunc and - overridingFunc.getDeclaringType() = overridingDecl.getDeclaration().getDeclaringType() - ) - ) and - // and the hidden declaration isn't explicitly brought in scope through a using declaration. - not exists(UsingDeclarationEntry ude | - ude.getDeclaration() = hiddenDecl.getDeclaration() and - ude.getEnclosingElement() = overridingDecl.getDeclaration().getDeclaringType() and - ude.getLocation().getStartLine() < overridingDecl.getLocation().getStartLine() - ) - or - // 2. if the overriding declaration doesn't override a base member function but has the same name - // as the hidden declaration - not overridingDecl.getDeclaration().(MemberFunction).overrides(_) and - overridingDecl.getName() = hiddenDecl.getName() and - overridingDecl.getDeclaration().getDeclaringType().getABaseClass() = - hiddenDecl.getDeclaration().getDeclaringType() - ) and - // Limit the results to the declarations and not the definitions, if any. - (overridingDecl.getDeclaration().hasDefinition() implies not overridingDecl.isDefinition()) and - (hiddenDecl.getDeclaration().hasDefinition() implies not hiddenDecl.isDefinition()) -select overridingDecl, - "Declaration for member '" + overridingDecl.getName() + - "' hides overridable inherited member function $@", hiddenDecl, hiddenDecl.getName() +class HiddenInheritedOverridableMemberFunctionQuery extends HiddenInheritedOverridableMemberFunctionSharedQuery +{ + HiddenInheritedOverridableMemberFunctionQuery() { + this = ScopePackage::hiddenInheritedOverridableMemberFunctionQuery() + } +} diff --git a/cpp/autosar/src/rules/A7-4-1/AsmDeclarationUsed.ql b/cpp/autosar/src/rules/A7-4-1/AsmDeclarationUsed.ql index d94811ff18..44489151da 100644 --- a/cpp/autosar/src/rules/A7-4-1/AsmDeclarationUsed.ql +++ b/cpp/autosar/src/rules/A7-4-1/AsmDeclarationUsed.ql @@ -15,7 +15,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.asmdeclarationused.AsmDeclarationUsed -from AsmStmt a -where not isExcluded(a, BannedSyntaxPackage::asmDeclarationUsedQuery()) -select a, "Use of asm declaration" +class AsmDeclarationUsedQuery extends AsmDeclarationUsedSharedQuery { + AsmDeclarationUsedQuery() { this = BannedSyntaxPackage::asmDeclarationUsedQuery() } +} diff --git a/cpp/autosar/src/rules/A7-5-2/RecursiveFunctions.ql b/cpp/autosar/src/rules/A7-5-2/RecursiveFunctions.ql index 13883624b3..6b305d9ca9 100644 --- a/cpp/autosar/src/rules/A7-5-2/RecursiveFunctions.ql +++ b/cpp/autosar/src/rules/A7-5-2/RecursiveFunctions.ql @@ -16,21 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.functionscallthemselveseitherdirectlyorindirectly.FunctionsCallThemselvesEitherDirectlyOrIndirectly -class RecursiveCall extends FunctionCall { - RecursiveCall() { - this.getTarget().calls*(this.getEnclosingFunction()) and - not this.getTarget().hasSpecifier("is_constexpr") - } +class RecursiveFunctionsQuery extends FunctionsCallThemselvesEitherDirectlyOrIndirectlySharedQuery { + RecursiveFunctionsQuery() { this = FunctionsPackage::recursiveFunctionsQuery() } } - -from RecursiveCall call, string msg, FunctionCall fc -where - not isExcluded(fc, FunctionsPackage::recursiveFunctionsQuery()) and - fc.getTarget() = call.getTarget() and - if fc.getTarget() = fc.getEnclosingFunction() - then msg = "This call directly invokes its containing function $@." - else - msg = - "The function " + fc.getEnclosingFunction() + " is indirectly recursive via this call to $@." -select fc, msg, fc.getTarget(), fc.getTarget().getName() diff --git a/cpp/autosar/src/rules/A7-6-1/FunctionNoReturnAttributeConditionAutosar.ql b/cpp/autosar/src/rules/A7-6-1/FunctionNoReturnAttributeConditionAutosar.ql index f9cdb1741f..9ebeff7b58 100644 --- a/cpp/autosar/src/rules/A7-6-1/FunctionNoReturnAttributeConditionAutosar.ql +++ b/cpp/autosar/src/rules/A7-6-1/FunctionNoReturnAttributeConditionAutosar.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.functionnoreturnattributecondition.FunctionNoReturnAttributeCondition -class FunctionNoReturnAttributeConditionAutosarQuery extends FunctionNoReturnAttributeConditionSharedQuery { +class FunctionNoReturnAttributeConditionAutosarQuery extends FunctionNoReturnAttributeConditionSharedQuery +{ FunctionNoReturnAttributeConditionAutosarQuery() { this = FunctionsPackage::functionNoReturnAttributeConditionAutosarQuery() } diff --git a/cpp/autosar/src/rules/A8-4-13/SharedPtrPassedToFunctionWithImproperSemantics.ql b/cpp/autosar/src/rules/A8-4-13/SharedPtrPassedToFunctionWithImproperSemantics.ql index df757685fb..422818f4c9 100644 --- a/cpp/autosar/src/rules/A8-4-13/SharedPtrPassedToFunctionWithImproperSemantics.ql +++ b/cpp/autosar/src/rules/A8-4-13/SharedPtrPassedToFunctionWithImproperSemantics.ql @@ -19,19 +19,15 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.SmartPointers -Expr underlyingObjectAffectingSharedPointerExpr(Function f) { - result = - any(VariableAccess va, FunctionCall fc | - va.getEnclosingFunction() = f and - // strip the type so as to include reference parameter types - va.getType().stripType() instanceof AutosarSharedPointer and - fc.getTarget().getDeclaringType().stripType() instanceof AutosarSharedPointer and - fc.getQualifier() = va and - // include only calls to methods which modify the underlying object - fc.getTarget().hasName(["operator=", "reset", "swap"]) - | - va - ) +VariableAccess underlyingObjectAffectingSharedPointerExpr(Function f) { + exists(FunctionCall fc | + // Find a call in the function + fc.getEnclosingFunction() = f and + // include only calls to methods which modify the underlying object + fc = any(AutosarSharedPointer s).getAModifyingCall() and + // Report the qualifier + fc.getQualifier() = result + ) } predicate flowsToUnderlyingObjectAffectingExpr(Parameter p) { diff --git a/cpp/autosar/src/rules/A8-4-4/FunctionReturnMultipleValueCondition.ql b/cpp/autosar/src/rules/A8-4-4/FunctionReturnMultipleValueCondition.ql index cba4d49e69..03f0c3cea6 100644 --- a/cpp/autosar/src/rules/A8-4-4/FunctionReturnMultipleValueCondition.ql +++ b/cpp/autosar/src/rules/A8-4-4/FunctionReturnMultipleValueCondition.ql @@ -16,7 +16,6 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.dataflow.DataFlow abstract class OutputValue extends Element { abstract string getOutputName(); @@ -50,6 +49,7 @@ OutputValue getAnOutputValue(Function f) { from Function f, int outputValues where + not isExcluded(f, FunctionsPackage::functionReturnMultipleValueConditionQuery()) and not f.isCompilerGenerated() and not f.isAffectedByMacro() and not f.isFromUninstantiatedTemplate(_) and diff --git a/cpp/autosar/src/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.ql b/cpp/autosar/src/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.ql index e188241672..6bd21b93ee 100644 --- a/cpp/autosar/src/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.ql +++ b/cpp/autosar/src/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.ql @@ -15,8 +15,9 @@ import cpp import codingstandards.cpp.autosar -import TriviallySmallType +import TriviallyCopyableSmallType import codingstandards.cpp.CommonTypes as CommonTypes +import codingstandards.cpp.Class /* * For the purposes of this rule, "cheap to copy" is defined as a trivially copyable type that is no @@ -25,7 +26,7 @@ import codingstandards.cpp.CommonTypes as CommonTypes * In this rule, we will look cases where a "cheap to copy" type is not passed by value. */ -from Parameter v, TriviallySmallType t +from Parameter v, TriviallyCopyableSmallType t where not isExcluded(v, ClassesPackage::inParametersForCheapToCopyTypesNotPassedByValueQuery()) and exists(ReferenceType rt | @@ -34,8 +35,11 @@ where ) and t.isConst() and not exists(CatchBlock cb | cb.getParameter() = v) and - not exists(CopyConstructor cc | cc.getAParameter() = v) and - not v.isFromUninstantiatedTemplate(_) + not exists(SpecialMemberFunction cc | cc.getAParameter() = v) and + not exists(Operator op | op.getAParameter() = v) and + not v.isFromUninstantiatedTemplate(_) and + not v.isFromTemplateInstantiation(_) select v, - "Parameter " + v.getName() + " is the trivially copyable type " + t.getName() + - " but it is passed by reference instead of by value." + "Parameter '" + v.getName() + + "' is the trivially copyable type $@ but it is passed by reference instead of by value.", t, + t.getName() diff --git a/cpp/autosar/src/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.ql b/cpp/autosar/src/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.ql index f6d481a54a..d1de0182b6 100644 --- a/cpp/autosar/src/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.ql +++ b/cpp/autosar/src/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.ql @@ -15,7 +15,7 @@ import cpp import codingstandards.cpp.autosar -import TriviallySmallType +import TriviallyCopyableSmallType import codingstandards.cpp.CommonTypes as CommonTypes /* @@ -28,11 +28,12 @@ import codingstandards.cpp.CommonTypes as CommonTypes from Parameter v where not isExcluded(v, ClassesPackage::inParametersForNotCheapToCopyTypesNotPassedByReferenceQuery()) and - not v.getType() instanceof TriviallySmallType and + not v.getType() instanceof TriviallyCopyableSmallType and not v.getType().getUnderlyingType() instanceof ReferenceType and not exists(CatchBlock cb | cb.getParameter() = v) and - not v.isFromUninstantiatedTemplate(_) + not v.isFromUninstantiatedTemplate(_) and + not v.isFromTemplateInstantiation(_) select v, - "Parameter " + v.getName() + - " is the trivially non-copyable type $@ but it is passed by value instead of by reference.", + "Parameter '" + v.getName() + + "' is the trivially non-copyable type $@ but it is passed by value instead of by reference.", v.getType(), v.getType().getName() diff --git a/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll b/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll new file mode 100644 index 0000000000..09c6cdae69 --- /dev/null +++ b/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll @@ -0,0 +1,22 @@ +import cpp +import codingstandards.cpp.autosar +import codingstandards.cpp.types.TrivialType + +/** + * Get the largest word size, in bytes. Some projects may have multiple different + * `VoidPointerType` sizes. + */ +int wordSize() { result = max(VoidPointerType v | | v.getSize()) } + +/** + * Converts bytes to words + */ +bindingset[bytes] +int minWordsRequiredToRepresentBytes(int bytes) { result = (1.0 * bytes / wordSize()).ceil() } + +class TriviallyCopyableSmallType extends Type { + TriviallyCopyableSmallType() { + isTriviallyCopyableType(this) and + exists(int size | size = this.getSize() | minWordsRequiredToRepresentBytes(size) <= 2) + } +} diff --git a/cpp/autosar/src/rules/A8-4-7/TriviallySmallType.qll b/cpp/autosar/src/rules/A8-4-7/TriviallySmallType.qll deleted file mode 100644 index 8279fd8d94..0000000000 --- a/cpp/autosar/src/rules/A8-4-7/TriviallySmallType.qll +++ /dev/null @@ -1,18 +0,0 @@ -import cpp -import codingstandards.cpp.autosar - -/** - * Get the largest word size, in bytes. Some projects may have multiple different - * `VoidPointerType` sizes. - */ -int wordSize() { result = max(VoidPointerType v | | v.getSize()) } - -/** - * Converts bytes to words - */ -bindingset[bytes] -int bytesToWords(int bytes) { result = bytes / wordSize() } - -class TriviallySmallType extends Type { - TriviallySmallType() { exists(int size | size = this.getSize() | bytesToWords(size) <= 2) } -} diff --git a/cpp/autosar/src/rules/A8-4-8/OutputParametersUsed.ql b/cpp/autosar/src/rules/A8-4-8/OutputParametersUsed.ql index aa480a95f7..c2fc51fcdf 100644 --- a/cpp/autosar/src/rules/A8-4-8/OutputParametersUsed.ql +++ b/cpp/autosar/src/rules/A8-4-8/OutputParametersUsed.ql @@ -23,31 +23,60 @@ import codingstandards.cpp.ConstHelpers import codingstandards.cpp.Operator /** - * Non-const T& and T* `Parameter`s to `Function`s + * Holds if p is passed as a non-const reference or pointer and is modified. + * This holds for in-out or out-only parameters. */ -class NonConstReferenceOrPointerParameterCandidate extends FunctionParameter { - NonConstReferenceOrPointerParameterCandidate() { - this instanceof NonConstReferenceParameter - or - this instanceof NonConstPointerParameter - } +predicate isOutParameter(NonConstPointerorReferenceParameter p) { + any(VariableEffect ve).getTarget() = p +} + +/** + * Holds if parameter `p` is a parameter to a user defined assignment operator that + * is defined outside of a class body. + * These require an in-out parameter as the first argument. + */ +predicate isNonMemberUserAssignmentParameter(NonConstPointerorReferenceParameter p) { + p.getFunction() instanceof UserAssignmentOperator and + not p.isMember() +} + +/** + * Holds if parameter `p` is a parameter to a stream insertion operator that + * is defined outside of a class body. + * These require an in-out parameter as the first argument. + * + * e.g., `std::ostream& operator<<(std::ostream& os, const T& obj)` + */ +predicate isStreamInsertionStreamParameter(NonConstPointerorReferenceParameter p) { + exists(StreamInsertionOperator op | not op.isMember() | op.getParameter(0) = p) } -pragma[inline] -predicate isFirstAccess(VariableAccess va) { - not exists(VariableAccess otherVa | - otherVa.getTarget() = va.getTarget() or - otherVa.getQualifier().(VariableAccess).getTarget() = va.getTarget() - | - otherVa.getASuccessor() = va +/** + * Holds if parameter `p` is a parameter to a stream insertion operator that + * is defined outside of a class body. + * These require an in-out parameter as the first argument and an out parameter for the second. + * + * e.g., `std::istream& operator>>(std::istream& is, T& obj)` + */ +predicate isStreamExtractionParameter(NonConstPointerorReferenceParameter p) { + exists(StreamExtractionOperator op | not op.isMember() | + op.getParameter(0) = p + or + op.getParameter(1) = p ) } -from NonConstReferenceOrPointerParameterCandidate p, VariableEffect ve +predicate isException(NonConstPointerorReferenceParameter p) { + isNonMemberUserAssignmentParameter(p) and p.getIndex() = 0 + or + isStreamInsertionStreamParameter(p) + or + isStreamExtractionParameter(p) +} + +from NonConstPointerorReferenceParameter p where not isExcluded(p, ConstPackage::outputParametersUsedQuery()) and - ve.getTarget() = p and - isFirstAccess(ve.getAnAccess()) and - not ve instanceof AnyAssignOperation and - not ve instanceof CrementOperation -select p, "Out parameter " + p.getName() + " that is modified before being read." + isOutParameter(p) and + not isException(p) +select p, "Out parameter '" + p.getName() + "' used." diff --git a/cpp/autosar/src/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.ql b/cpp/autosar/src/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.ql index 0b5c8d70be..3b30eb676a 100644 --- a/cpp/autosar/src/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.ql +++ b/cpp/autosar/src/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.ql @@ -21,6 +21,7 @@ import codingstandards.cpp.autosar import codingstandards.cpp.FunctionParameter import codingstandards.cpp.ConstHelpers import codingstandards.cpp.Operator +import semmle.code.cpp.dataflow.DataFlow /** * Non-const T& `Parameter`s to `Function`s @@ -60,7 +61,7 @@ where //also not having a nonconst member accessed through the param notUsedAsQualifierForNonConst(p) and not exists(ClassAggregateLiteral l, Field f | - DataFlow::localExprFlow(p.getAnAccess(), l.getFieldExpr(f)) and + DataFlow::localExprFlow(p.getAnAccess(), l.getAFieldExpr(f)) and not f.isConst() ) and // Exclude parameters that are used to initialize member fields. diff --git a/cpp/autosar/src/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.ql b/cpp/autosar/src/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.ql index 2a44ca650e..180cbf7224 100644 --- a/cpp/autosar/src/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.ql +++ b/cpp/autosar/src/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.ql @@ -17,50 +17,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.initializerlistconstructoristheonlyconstructor.InitializerListConstructorIsTheOnlyConstructor -class StdInitializerList extends Class { - StdInitializerList() { hasQualifiedName("std", "initializer_list") } -} - -/** - * An _initializer-list constructor_ according to `[dcl.init.list]`. - * - * A `Constructor` where the first parameter refers to `std::initializer_list`, and any remaining - * parameters have default arguments. - */ -class InitializerListConstructor extends Constructor { - InitializerListConstructor() { - // The first parameter is a `std::intializer_list` parameter - exists(Type firstParamType | firstParamType = getParameter(0).getType() | - // Either directly `std::initializer_list` - firstParamType instanceof StdInitializerList - or - //A reference to `std::initializer_list` - firstParamType.(ReferenceType).getBaseType().getUnspecifiedType() instanceof - StdInitializerList - ) and - // All parameters other than the fi - forall(Parameter other | other = getParameter([1 .. (getNumberOfParameters() - 1)]) | - exists(other.getInitializer()) - ) +class ConfusingUseOfInitializerListConstructorsQuery extends InitializerListConstructorIsTheOnlyConstructorSharedQuery +{ + ConfusingUseOfInitializerListConstructorsQuery() { + this = InitializationPackage::confusingUseOfInitializerListConstructorsQuery() } } - -from Constructor c, InitializerListConstructor stdInitializerConstructor, string paramList -where - not isExcluded(c, InitializationPackage::confusingUseOfInitializerListConstructorsQuery()) and - // Not an initializer-list constructor - not c instanceof InitializerListConstructor and - // Constructor is not a special member function constructor - not c instanceof CopyConstructor and - not c instanceof MoveConstructor and - not c.getNumberOfParameters() = 0 and // default constructor - // And there is an initalizer-list constructor - stdInitializerConstructor = c.getDeclaringType().getAConstructor() and - // Determine the parameter type list of the constructor - paramList = - concat(string parameter | parameter = c.getAParameter().getType().getName() | parameter, ",") -select c, - "The constructor " + c.getQualifiedName() + "(" + paramList + - ") may be ignored in favour of $@ when using braced initialization.", stdInitializerConstructor, - "the constructor accepting std::initializer_list" diff --git a/cpp/autosar/src/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql b/cpp/autosar/src/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql index be623f171e..478f8dcdf0 100644 --- a/cpp/autosar/src/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql +++ b/cpp/autosar/src/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql @@ -57,7 +57,8 @@ class AccessAwareMemberFunction extends MemberFunction { from Class c, AccessAwareMemberFunction mf, FieldAccess a, ReturnStmt rs where - not isExcluded(c) and + not isExcluded(c, + ClassesPackage::returnsNonConstRawPointersOrReferencesToPrivateOrProtectedDataQuery()) and not isExcluded(mf, ClassesPackage::returnsNonConstRawPointersOrReferencesToPrivateOrProtectedDataQuery()) and // Find all of the methods within this class diff --git a/cpp/autosar/src/rules/A9-6-1/DataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayout.ql b/cpp/autosar/src/rules/A9-6-1/DataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayout.ql index 1d89b275f6..0fd09210f7 100644 --- a/cpp/autosar/src/rules/A9-6-1/DataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayout.ql +++ b/cpp/autosar/src/rules/A9-6-1/DataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayout.ql @@ -23,6 +23,6 @@ from HardwareOrProtocolInterfaceClass c where not isExcluded(c, ClassesPackage::dataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayoutQuery()) and - not c.isPOD() + not c.isPod() select c, "Data type used for hardware interface or communication protocol is not standard layout and trivial." diff --git a/cpp/autosar/src/rules/M0-1-1/UnreachableCode.ql b/cpp/autosar/src/rules/M0-1-1/UnreachableCode.ql index 5018313850..5e2cc22d08 100644 --- a/cpp/autosar/src/rules/M0-1-1/UnreachableCode.ql +++ b/cpp/autosar/src/rules/M0-1-1/UnreachableCode.ql @@ -17,39 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.deadcode.UnreachableCode +import codingstandards.cpp.rules.unreachablecode.UnreachableCode -/* - * This query identifies unreachable code at the level of `BasicBlock`. We use `BasicBlock`s for - * this because they represent sequences of statements which, according to the CFG, are either - * all unreachable or all reachable, because control flow cannot escape from the middle of the - * basic block. - * - * We use the `BasicBlock.isUnreachable()` predicate to identify `BasicBlock`s which are unreachable - * according to our calculated control flow graph. In practice, this can resolve expressions used in - * conditions which are constant, accesses of constant values (even across function boundaries), and - * operations, recursively, on such expressions. There is no attempt made to resolve conditional - * expressions which are not statically constant or derived directly from statically constant variables. - * - * One potential problem with using `BasicBlock`s is that for template functions the `BasicBlock` is - * duplicated across multiple `Function` instances, one for uninstantiated templates, and one for - * each instantiation. Rather than considering each template instantiation independently, we instead - * only report a `BasicBlock` in a template as unreachable, if it is unreachable in all template - * instantiations (and in the uninstantiated template). This helps avoid flagging examples such as - * `return 1` as dead code in this example, where `T::isVal()` is statically deducible in some - * template instantiations: - * ``` - * template int f() { - * if (T::isVal()) return 1; - * return 2; - * } - * ``` - */ - -from UnreachableBasicBlock b -where - // None of the basic blocks are excluded - not isExcluded(b.getABasicBlock(), DeadCodePackage::unreachableCodeQuery()) and - // Exclude results where at least one of the basic blocks appears in a macro expansion, as - // macros can easily result in unreachable blocks through no fault of the user of the macro - not inMacroExpansion(b.getABasicBlock()) -select b, "This statement in function $@ is unreachable.", b.getPrimaryFunction() as f, f.getName() +class UnreachableCodeQuery extends UnreachableCodeSharedQuery { + UnreachableCodeQuery() { this = DeadCodePackage::unreachableCodeQuery() } +} diff --git a/cpp/autosar/src/rules/M0-1-10/UnusedFunction.ql b/cpp/autosar/src/rules/M0-1-10/UnusedFunction.ql index b8593e75c0..f175cb8992 100644 --- a/cpp/autosar/src/rules/M0-1-10/UnusedFunction.ql +++ b/cpp/autosar/src/rules/M0-1-10/UnusedFunction.ql @@ -26,5 +26,6 @@ where then name = unusedFunction.getQualifiedName() else name = unusedFunction.getName() ) and - not unusedFunction.isDeleted() + not unusedFunction.isDeleted() and + not unusedFunction instanceof SpecialMemberFunction select unusedFunction, "Function " + name + " is " + unusedFunction.getDeadCodeType() diff --git a/cpp/autosar/src/rules/M0-1-10/UnusedSplMemberFunction.ql b/cpp/autosar/src/rules/M0-1-10/UnusedSplMemberFunction.ql new file mode 100644 index 0000000000..bcbf6f4e1b --- /dev/null +++ b/cpp/autosar/src/rules/M0-1-10/UnusedSplMemberFunction.ql @@ -0,0 +1,31 @@ +/** + * @id cpp/autosar/unused-spl-member-function + * @name M0-1-10: Every defined function should be called at least once + * @description Uncalled functions complicate the program and can indicate a possible mistake on the + * part of the programmer. + * @kind problem + * @precision medium + * @problem.severity warning + * @tags external/autosar/id/m0-1-10 + * readability + * maintainability + * external/autosar/allocated-target/implementation + * external/autosar/enforcement/automated + * external/autosar/obligation/advisory + */ + +import cpp +import codingstandards.cpp.autosar +import codingstandards.cpp.deadcode.UnusedFunctions + +from UnusedFunctions::UnusedSplMemberFunction unusedSplMemFunction, string name +where + not isExcluded(unusedSplMemFunction, DeadCodePackage::unusedFunctionQuery()) and + ( + if exists(unusedSplMemFunction.getQualifiedName()) + then name = unusedSplMemFunction.getQualifiedName() + else name = unusedSplMemFunction.getName() + ) and + not unusedSplMemFunction.isDeleted() +select unusedSplMemFunction, + "Special member function " + name + " is " + unusedSplMemFunction.getDeadCodeType() diff --git a/cpp/autosar/src/rules/M0-1-2/InfeasiblePath.ql b/cpp/autosar/src/rules/M0-1-2/InfeasiblePath.ql index d8a5c07d95..83e056472b 100644 --- a/cpp/autosar/src/rules/M0-1-2/InfeasiblePath.ql +++ b/cpp/autosar/src/rules/M0-1-2/InfeasiblePath.ql @@ -17,6 +17,8 @@ import cpp import codingstandards.cpp.autosar import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import codingstandards.cpp.deadcode.UnreachableCode +import semmle.code.cpp.controlflow.Guards /** * A "conditional" node in the control flow graph, i.e. one that can potentially have a true and false path. @@ -44,6 +46,7 @@ class BreakingLoop extends Loop { predicate hasCFGDeducedInfeasiblePath( ConditionalControlFlowNode cond, boolean infeasiblePath, string explanation ) { + not cond.isFromTemplateInstantiation(_) and // No true successor, so the true path has already been deduced as infeasible not exists(cond.getATrueSuccessor()) and infeasiblePath = true and @@ -147,17 +150,22 @@ predicate isConstantRelationalOperation( /** * Holds if the `ConditionalNode` has an infeasible `path` for the reason given in `explanation`. */ -predicate hasInfeasiblePath( - ConditionalControlFlowNode node, boolean infeasiblePath, string explanation -) { - hasCFGDeducedInfeasiblePath(node, infeasiblePath, explanation) and - not isConstantRelationalOperation(node, infeasiblePath, _) - or - isConstantRelationalOperation(node, infeasiblePath, explanation) +predicate hasInfeasiblePath(ConditionalControlFlowNode node, string message) { + exists(boolean infeasiblePath, string explanation | + not node.isFromTemplateInstantiation(_) and + if node.isFromUninstantiatedTemplate(_) + then message = "The path is unreachable in a template." + else message = "The " + infeasiblePath + " path is infeasible because " + explanation + "." + | + hasCFGDeducedInfeasiblePath(node, infeasiblePath, explanation) and + not isConstantRelationalOperation(node, infeasiblePath, _) + or + isConstantRelationalOperation(node, infeasiblePath, explanation) + ) } -from ConditionalControlFlowNode cond, boolean infeasiblePath, string explanation +from ConditionalControlFlowNode cond, string explanation where - not isExcluded(cond) and - hasInfeasiblePath(cond, infeasiblePath, explanation) -select cond, "The " + infeasiblePath + " path is infeasible because " + explanation + "." + not isExcluded(cond, DeadCodePackage::infeasiblePathQuery()) and + hasInfeasiblePath(cond, explanation) +select cond, explanation diff --git a/cpp/autosar/src/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.ql b/cpp/autosar/src/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.ql index 617fbd5f8a..ba6b6df20a 100644 --- a/cpp/autosar/src/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.ql +++ b/cpp/autosar/src/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.ql @@ -22,5 +22,7 @@ from PotentiallyUnusedGlobalOrNamespaceVariable v where not isExcluded(v, DeadCodePackage::unusedGlobalOrNamespaceVariableQuery()) and // No variable access - not exists(v.getAnAccess()) -select v, "Variable " + v.getQualifiedName() + " is unused." + not exists(v.getAnAccess()) and + // Exclude members whose value is compile time and is potentially used to inintialize a template + not maybeACompileTimeTemplateArgument(v) +select v, "Variable '" + v.getQualifiedName() + "' is unused." diff --git a/cpp/autosar/src/rules/M0-1-3/UnusedLocalVariable.ql b/cpp/autosar/src/rules/M0-1-3/UnusedLocalVariable.ql index 50aa5ea919..e89e9ec135 100644 --- a/cpp/autosar/src/rules/M0-1-3/UnusedLocalVariable.ql +++ b/cpp/autosar/src/rules/M0-1-3/UnusedLocalVariable.ql @@ -18,9 +18,41 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.deadcode.UnusedVariables +// This predicate is similar to getUseCount for M0-1-4 except that it also +// considers static_asserts. This was created to cater for M0-1-3 specifically +// and hence, doesn't attempt to reuse the M0-1-4 specific predicate +// - getUseCount() +int getUseCountConservatively(Variable v) { + result = + count(VariableAccess access | access = v.getAnAccess()) + + count(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) + + // For constexpr variables used as template arguments, we don't see accesses (just the + // appropriate literals). We therefore take a conservative approach and count the number of + // template instantiations that use the given constant, and consider each one to be a use + // of the variable + count(ClassTemplateInstantiation cti | + cti.getTemplateArgument(_).(Expr).getValue() = getConstExprValue(v) + ) + + // For static asserts too, check if there is a child which has the same value + // as the constexpr variable. + count(StaticAssert s | s.getCondition().getAChild*().getValue() = getConstExprValue(v)) + + // In case an array type uses a constant in the same scope as the constexpr variable, + // consider it as used. + countUsesInLocalArraySize(v) +} + from PotentiallyUnusedLocalVariable v where not isExcluded(v, DeadCodePackage::unusedLocalVariableQuery()) and // Local variable is never accessed - not exists(v.getAnAccess()) -select v, "Local variable " + v.getName() + " in " + v.getFunction().getName() + " is not used." + not exists(v.getAnAccess()) and + // Sometimes multiple objects representing the same entities are created in + // the AST. Check if those are not accessed as well. Refer issue #658 + not exists(LocalScopeVariable another | + another.getDefinitionLocation() = v.getDefinitionLocation() and + another.hasName(v.getName()) and + exists(another.getAnAccess()) and + another != v + ) and + getUseCountConservatively(v) = 0 +select v, "Local variable '" + v.getName() + "' in '" + v.getFunction().getName() + "' is not used." diff --git a/cpp/autosar/src/rules/M0-1-3/UnusedMemberVariable.ql b/cpp/autosar/src/rules/M0-1-3/UnusedMemberVariable.ql index 5ebfac4d3f..a27f9cbcab 100644 --- a/cpp/autosar/src/rules/M0-1-3/UnusedMemberVariable.ql +++ b/cpp/autosar/src/rules/M0-1-3/UnusedMemberVariable.ql @@ -25,5 +25,7 @@ where // No variable access not exists(v.getAnAccess()) and // No explicit initialization in a constructor - not exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) -select v, "Member variable " + v.getName() + " is unused." + not exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) and + // Exclude members whose value is compile time and is potentially used to inintialize a template + not maybeACompileTimeTemplateArgument(v) +select v, "Member variable '" + v.getName() + "' is unused." diff --git a/cpp/autosar/src/rules/M0-1-4/SingleUseMemberPODVariable.ql b/cpp/autosar/src/rules/M0-1-4/SingleUseMemberPODVariable.ql index 5ac8f30160..c1dd812e80 100644 --- a/cpp/autosar/src/rules/M0-1-4/SingleUseMemberPODVariable.ql +++ b/cpp/autosar/src/rules/M0-1-4/SingleUseMemberPODVariable.ql @@ -24,5 +24,5 @@ where not isExcluded(v, DeadCodePackage::singleUseMemberPODVariableQuery()) and isSingleUseNonVolatilePODVariable(v) select v, - "Member POD variable " + v.getName() + " in " + v.getDeclaringType().getName() + " is only $@.", - getSingleUse(v), "used once" + "Member POD variable '" + v.getName() + "' in '" + v.getDeclaringType().getName() + + "' is only $@.", getSingleUse(v), "used once" diff --git a/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll b/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll index c750bb130c..8985f7254e 100644 --- a/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll +++ b/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll @@ -1,19 +1,71 @@ /** A module providing predicates that support identifying single use non volatile POD variables. */ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import codingstandards.cpp.deadcode.UnusedVariables +/** + * Gets the number of uses of variable `v` in an opaque assignment, where an opaque assignment is a cast from one type to the other, and `v` is assumed to be a member of the resulting type. + * e.g., + * struct foo { + * int bar; + * } + * + * struct foo * v = (struct foo*)buffer; + */ +Expr getIndirectSubObjectAssignedValue(MemberVariable subobject) { + // struct foo * ptr = (struct foo*)buffer; + exists(Struct someStruct, Variable instanceOfSomeStruct | someStruct.getAMember() = subobject | + instanceOfSomeStruct.getType().(PointerType).getBaseType() = someStruct and + exists(Cast assignedValue | + // Exclude cases like struct foo * v = nullptr; + not assignedValue.isImplicit() and + // `v` is a subobject of another type that reinterprets another object. We count that as a use of `v`. + assignedValue.getExpr() = instanceOfSomeStruct.getAnAssignedValue() and + result = assignedValue + ) + or + // struct foo; read(..., (char *)&foo); + instanceOfSomeStruct.getType() = someStruct and + exists(Call externalInitializerCall, Cast castToCharPointer, int n | + externalInitializerCall.getArgument(n).(AddressOfExpr).getOperand() = + instanceOfSomeStruct.getAnAccess() and + externalInitializerCall.getArgument(n) = castToCharPointer.getExpr() and + castToCharPointer.getType().(PointerType).getBaseType().getUnspecifiedType() instanceof + CharType and + result = externalInitializerCall + ) + or + // the object this subject is part of is initialized and we assume this initializes the subobject. + instanceOfSomeStruct.getType() = someStruct and + result = instanceOfSomeStruct.getInitializer().getExpr() + ) +} + /** Gets a "use" count according to rule M0-1-4. */ int getUseCount(Variable v) { - exists(int initializers | - // We enforce that it's a POD type variable, so if it has an initializer it is explicit - (if v.hasInitializer() then initializers = 1 else initializers = 0) and - result = - initializers + - count(VariableAccess access | access = v.getAnAccess() and not access.isCompilerGenerated()) - + count(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) - ) + // We enforce that it's a POD type variable, so if it has an initializer it is explicit + result = + count(getAUserInitializedValue(v)) + + count(VariableAccess access | access = v.getAnAccess() and not access.isCompilerGenerated()) + + // For constexpr variables used as template arguments, we don't see accesses (just the + // appropriate literals). We therefore take a conservative approach and count the number of + // template instantiations that use the given constant, and consider each one to be a use + // of the variable + count(ClassTemplateInstantiation cti | + cti.getTemplateArgument(_).(Expr).getValue() = getConstExprValue(v) + ) + count(getIndirectSubObjectAssignedValue(v)) +} + +Expr getAUserInitializedValue(Variable v) { + ( + result = v.getInitializer().getExpr() + or + exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v and result = cfi.getExpr()) + or + exists(ClassAggregateLiteral l | not l.isCompilerGenerated() | result = l.getAFieldExpr(v)) + ) and + not result.isCompilerGenerated() } /** Gets a single use of `v`, if `isSingleUseNonVolatilePODVariable` holds. */ diff --git a/cpp/autosar/src/rules/M0-1-9/DeadCode.ql b/cpp/autosar/src/rules/M0-1-9/DeadCode.ql index 6d35a019fd..f1c4f03895 100644 --- a/cpp/autosar/src/rules/M0-1-9/DeadCode.ql +++ b/cpp/autosar/src/rules/M0-1-9/DeadCode.ql @@ -16,116 +16,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.deadcode.UselessAssignments -import codingstandards.cpp.deadcode.UnreachableCode +import codingstandards.cpp.rules.deadcode.DeadCode -/* - * This query finds the following kinds of dead code statement: - * - A declaration of a non-static stack variable whose initializing expression is pure and that is never subsequently accessed in live code. - * - A block that contain only dead statements. - * - A do loop whose condition is pure, and whose body contains only dead statements. - * - An if statement whose condition is pure, and whose then and else clauses (where they exist) only contain dead statements. - * - A label statement to which the code never jumps. - * - A while loop whose condition is pure, and whose body contains only dead statements. - * - Expression statements whose expressions are pure. - * - Writes to a non-static stack variable that is never subsequently read in live code. - */ - -/** - * Holds if the `Stmt` `s` is either dead or unreachable. - */ -predicate isDeadOrUnreachableStmt(Stmt s) { - isDeadStmt(s) - or - s.getBasicBlock() = any(UnreachableBasicBlock ubb).getABasicBlock() +class MisraCppDeadCodeQuery extends DeadCodeSharedQuery { + MisraCppDeadCodeQuery() { this = DeadCodePackage::deadCodeQuery() } } - -/** - * Holds if the `Stmt` `s` is dead, i.e. could be executed, but its removal would not meaningfully - * affect the program. - */ -predicate isDeadStmt(Stmt s) { - // A `DeclStmt` is dead code if: - // - All the declarations are variable declarations - // - None of those variables are ever accessed in non-dead code - // - The initializers for each of the variables are pure - exists(DeclStmt ds | - ds = s and - // Use forex so that we don't flag "fake" generated `DeclStmt`s (e.g. those generated by the - // extractor for static_asserts) with no actual declarations - forex(Declaration d | d = ds.getADeclaration() | - exists(LocalScopeVariable v | - d = v and - v.getInitializer().getExpr().isPure() and - not exists(VariableAccess va | - va.getTarget() = v and - not isDeadOrUnreachableStmt(va.getEnclosingStmt()) - ) - ) - ) - ) - or - // A block that only contains dead statements. - exists(BlockStmt b | - b = s and - forall(Stmt child | child = b.getAStmt() | isDeadStmt(child)) and - // If this is a catch block, we should only report it as dead if it is the last catch block. - not exists(TryStmt ts, int i | - ts.getCatchClause(i) = b and - i < (ts.getNumberOfCatchClauses() - 1) - ) - ) - or - // A do statement whose condition is pure, and whose body contains only dead statements. - exists(DoStmt ds | - ds = s and - ds.getCondition().isPure() and - isDeadOrUnreachableStmt(ds.getStmt()) - ) - or - // An if statement whose condition is pure, and whose then and else clauses (where they exist) are dead or unreachable - exists(IfStmt is | - is = s and - is.getCondition().isPure() and - // Then part is either dead or unreachable - isDeadOrUnreachableStmt(is.getThen()) and - (exists(is.getElse()) implies isDeadOrUnreachableStmt(is.getElse())) - ) - or - // A while statement whose condition is pure, and whose body is a dead or unreachable statement - exists(WhileStmt ws | - ws = s and - ws.getCondition().isPure() and - isDeadOrUnreachableStmt(ws.getStmt()) - ) - or - // An expression statement which is pure - s.(ExprStmt).getExpr().isPure() - or - exists(SsaDefinition sd, LocalScopeVariable v | - // A useless definition - isUselessSsaDefinition(sd, v) and - s.(ExprStmt).getExpr() = sd.getDefinition() and - // The defining value is pure - sd.getDefiningValue(v).isPure() - ) - or - // Any TryStmt with a dead body is dead. We ignore the catch blocks, because if the body is dead, - // no exception can be thrown, and so the catch blocks are unreachable - exists(TryStmt ts | s = ts and isDeadStmt(ts.getStmt())) -} - -from Stmt s -where - not isExcluded(s, DeadCodePackage::deadCodeQuery()) and - isDeadStmt(s) and - // Report only the highest level dead statement, to avoid over reporting - not isDeadStmt(s.getParentStmt()) and - // MISRA defines dead code as an "_executed_ statement whose removal would not affect the program - // output". We therefore exclude unreachable statements as they are, by definition, not executed. - not s.getBasicBlock() = any(UnreachableBasicBlock ubb).getABasicBlock() and - // Exclude code generated by macros, because the code may be "live" in other instantiations - not s.isAffectedByMacro() and - // Exclude compiler generated statements - not s.isCompilerGenerated() -select s, "This statement is dead code." diff --git a/cpp/autosar/src/rules/M0-2-1/DoNotPassAliasedPointerToParam.ql b/cpp/autosar/src/rules/M0-2-1/DoNotPassAliasedPointerToParam.ql new file mode 100644 index 0000000000..d99ae486fc --- /dev/null +++ b/cpp/autosar/src/rules/M0-2-1/DoNotPassAliasedPointerToParam.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/autosar/do-not-pass-aliased-pointer-to-param + * @name M0-2-1: Do not pass aliased pointers as parameters of functions where it is undefined behaviour for those pointers to overlap + * @description Passing a aliased pointers as parameters of certain functions is undefined behavior. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/autosar/id/m0-2-1 + * correctness + * external/autosar/allocated-target/implementation + * external/autosar/enforcement/automated + * external/autosar/obligation/required + */ + +import cpp +import codingstandards.cpp.autosar +import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared + +class DoNotPassAliasedPointerToRestrictQualifiedParamQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery +{ + DoNotPassAliasedPointerToRestrictQualifiedParamQuery() { + this = RepresentationPackage::doNotPassAliasedPointerToParamQuery() + } +} diff --git a/cpp/autosar/src/rules/M0-3-2/FunctionErroneousReturnValueNotTested.ql b/cpp/autosar/src/rules/M0-3-2/FunctionErroneousReturnValueNotTested.ql index cd94d63ffc..77e6278960 100644 --- a/cpp/autosar/src/rules/M0-3-2/FunctionErroneousReturnValueNotTested.ql +++ b/cpp/autosar/src/rules/M0-3-2/FunctionErroneousReturnValueNotTested.ql @@ -19,54 +19,11 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.dataflow.DataFlow -import semmle.code.cpp.controlflow.Guards +import codingstandards.cpp.rules.functionerroneousreturnvaluenottested.FunctionErroneousReturnValueNotTested -from FunctionCall fc -where - not isExcluded(fc, ExpressionsPackage::functionErroneousReturnValueNotTestedQuery()) and - fc.getTarget() - .hasGlobalOrStdName([ - // fcntl.h - "open", "openat", "fcntl", "creat", - // locale.h - "setlocale", - // stdlib.h - "system", "getenv", "getenv_s", - // signal.h - "signal", "raise", - // setjmp.h - "setjmp", - // stdio.h - "fopen", "fopen_s", "freopen", "freopen_s", "fclose", "fcloseall", "fflush", "setvbuf", - "fgetc", "getc", "fgets", "fputc", "getchar", "gets", "gets_s", "putchar", "puts", - "ungetc", "scanf", "fscanf", "sscanf", "scanf_s", "fscanf_s", "sscanf_s", "vscanf", - "vfscanf", "vsscanf", "vscanf_s", "vfscanf_s", "vsscanf_s", "printf", "fprintf", - "sprintf", "snprintf", "printf_s", "fprintf_s", "sprintf_s", "snprintf_s", "vprintf", - "vfprintf", "vsprintf", "vsnprintf", "vprintf_s", "vfprintf_s", "vsprintf_s", - "vsnprintf_s", "ftell", "fgetpos", "fseek", "fsetpos", "remove", "rename", "tmpfile", - "tmpfile_s", "tmpnam", "tmpnam_s", - // string.h - "strcpy_s", "strncpy_s", "strcat_s", "strncat_s", "memset_s", "memcpy_s", "memmove_s", - "strerror_s", - // threads.h - "thrd_create", "thrd_sleep", "thrd_detach", "thrd_join", "mtx_init", "mtx_lock", - "mtx_timedlock", "mtx_trylock", "mtx_unlock", "cnd_init", "cnd_signal", "cnd_broadcast", - "cnd_wait", "cnd_timedwait", "tss_create", "tss_get", "tss_set", - // time.h - "time", "clock", "timespec_get", "asctime_s", "ctime_s", "gmtime", "gmtime_s", - "localtime", "localtime_s", - // unistd.h - "write", "read", "close", "unlink", - // wchar.h - "fgetwc", "getwc", "fgetws", "fputwc", "putwc", "fputws", "getwchar", "putwchar", - "ungetwc", "wscanf", "fwscanf", "swscanf", "wscanf_s", "fwscanf_s", "swscanf_s", - "vwscanf", "vfwscanf", "vswscanf", "vwscanf_s", "vfwscanf_s", "vswscanf_s", "wprintf", - "fwprintf", "swprintf", "wprintf_s", "fwprintf_s", "swprintf_s", "snwprintf_s", - "vwprintf", "vfwprintf", "vswprintf", "vwprintf_s", "vfwprintf_s", "vswprintf_s", - "vsnwprintf_s" - ]) and - forall(GuardCondition gc | - not DataFlow::localFlow(DataFlow::exprNode(fc), DataFlow::exprNode(gc.getAChild*())) - ) -select fc, "Return value is not tested for errors." +class FunctionErrorInformationUntestedQuery extends FunctionErroneousReturnValueNotTestedSharedQuery +{ + FunctionErrorInformationUntestedQuery() { + this = ExpressionsPackage::functionErroneousReturnValueNotTestedQuery() + } +} diff --git a/cpp/autosar/src/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.ql b/cpp/autosar/src/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.ql index 6b6cead0ea..c16e5461f0 100644 --- a/cpp/autosar/src/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.ql +++ b/cpp/autosar/src/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.ql @@ -14,24 +14,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.virtualandnonvirtualclassinthehierarchy.VirtualAndNonVirtualClassInTheHierarchy -from Class c1, Class c2, Class c3, Class base, ClassDerivation cd1, ClassDerivation cd2 -where - not isExcluded(c3, - InheritancePackage::accessibleBaseClassBothVirtualAndNonVirtualInHierarchyQuery()) and - // for each pair of classes, get all of their derivations - cd1 = c1.getADerivation() and - cd2 = c2.getADerivation() and - // where they share the same base class - base = cd1.getBaseClass() and - base = cd2.getBaseClass() and - // but one is virtual, and one is not, and the derivations are in different classes - cd1.isVirtual() and - not cd2.isVirtual() and - // and there is some 'other class' that derives from both of these classes - c3.derivesFrom*(c1) and - c3.derivesFrom*(c2) and - // and the base class is accessible from the 'other class' - c3.getAMemberFunction().getEnclosingAccessHolder().canAccessClass(base, c3) -select c3, "Class inherits base class $@, which is derived virtual by $@ and non-virtual by $@.", - base, base.getName(), cd1, cd1.getDerivedClass().toString(), c2, cd2.getDerivedClass().toString() +class AccessibleBaseClassBothVirtualAndNonVirtualInHierarchyQuery extends VirtualAndNonVirtualClassInTheHierarchySharedQuery +{ + AccessibleBaseClassBothVirtualAndNonVirtualInHierarchyQuery() { + this = InheritancePackage::accessibleBaseClassBothVirtualAndNonVirtualInHierarchyQuery() + } +} diff --git a/cpp/autosar/src/rules/M11-0-1/MemberDataInNonPodClassTypesNotPrivate.ql b/cpp/autosar/src/rules/M11-0-1/MemberDataInNonPodClassTypesNotPrivate.ql index b60594b8a9..a9902a72e0 100644 --- a/cpp/autosar/src/rules/M11-0-1/MemberDataInNonPodClassTypesNotPrivate.ql +++ b/cpp/autosar/src/rules/M11-0-1/MemberDataInNonPodClassTypesNotPrivate.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.cpp.autosar class NonPODType extends Class { - NonPODType() { not this.isPOD() } + NonPODType() { not this.isPod() } } from NonPODType p, Field f diff --git a/cpp/autosar/src/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.ql b/cpp/autosar/src/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.ql index 35f41c179a..4b6c037aba 100644 --- a/cpp/autosar/src/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.ql +++ b/cpp/autosar/src/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.ql @@ -14,79 +14,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.objectsdynamictypeusedfromconstructorordestructor.ObjectsDynamicTypeUsedFromConstructorOrDestructor -predicate thisCall(FunctionCall c) { - c.getQualifier() instanceof ThisExpr or - c.getQualifier().(PointerDereferenceExpr).getChild(0) instanceof ThisExpr -} - -predicate virtualThisCall(FunctionCall c, Function overridingFunction) { - c.isVirtual() and - thisCall(c) and - overridingFunction = c.getTarget().(VirtualFunction).getAnOverridingFunction() -} - -class DynamicTypeExpr extends Expr { - DynamicTypeExpr() { - this instanceof TypeidOperator and - this.getEnclosingFunction().getDeclaringType().isPolymorphic() - or - this instanceof DynamicCast - or - virtualThisCall(this.(FunctionCall), _) +class DynamicTypeOfThisUsedFromConstructorOrDestructorQuery extends ObjectsDynamicTypeUsedFromConstructorOrDestructorSharedQuery +{ + DynamicTypeOfThisUsedFromConstructorOrDestructorQuery() { + this = InheritancePackage::dynamicTypeOfThisUsedFromConstructorOrDestructorQuery() } } - -/* - * Catch most cases: go into functions in the same class, but only catch direct - * references to "this". - */ - -predicate nonVirtualMemberFunction(MemberFunction mf, Class c) { - mf = c.getAMemberFunction() and - not mf instanceof Constructor and - not mf instanceof Destructor and - not mf.isVirtual() -} - -predicate callFromNonVirtual(MemberFunction source, Class c, MemberFunction targ) { - exists(FunctionCall fc | - fc.getEnclosingFunction() = source and fc.getTarget() = targ and thisCall(fc) - ) and - targ = c.getAMemberFunction() and - nonVirtualMemberFunction(source, c) -} - -predicate indirectlyInvokesDynamicTypeExpr(MemberFunction caller, DynamicTypeExpr target) { - target = - any(DynamicTypeExpr expr | - expr.getEnclosingFunction() = caller and - nonVirtualMemberFunction(caller, caller.getDeclaringType()) - ) - or - exists(MemberFunction mid | - indirectlyInvokesDynamicTypeExpr(mid, target) and - callFromNonVirtual(caller, caller.getDeclaringType(), mid) - ) -} - -from DynamicTypeExpr expr, FunctionCall call, MemberFunction mf, string explanation -where - not isExcluded(expr, InheritancePackage::dynamicTypeOfThisUsedFromConstructorOrDestructorQuery()) and - ( - mf instanceof Constructor or - mf instanceof Destructor - ) and - ( - mf = expr.getEnclosingFunction() and - explanation = "$@ uses the dynamic type of its own object." - or - mf != expr.getEnclosingFunction() and - mf = call.getEnclosingFunction() and - thisCall(call) and - indirectlyInvokesDynamicTypeExpr(call.getTarget(), expr) and - explanation = - "$@ calls " + call.getTarget().getQualifiedName() + - ", which uses the dynamic type of its own object." - ) -select expr, explanation, mf, mf.getQualifiedName() diff --git a/cpp/autosar/src/rules/M14-5-3/CopyAssignmentOperatorNotDeclared.ql b/cpp/autosar/src/rules/M14-5-3/CopyAssignmentOperatorNotDeclared.ql index 05e99d6e66..1b41fe81bc 100644 --- a/cpp/autosar/src/rules/M14-5-3/CopyAssignmentOperatorNotDeclared.ql +++ b/cpp/autosar/src/rules/M14-5-3/CopyAssignmentOperatorNotDeclared.ql @@ -34,10 +34,10 @@ class TemplateAssignmentOperatorMember extends MemberFunction { } /** - * is a copy assigment operator candidate if it has only one param and form in [T, T&, const T&, volatile T&, const volatile T&] + * is a copy assignment operator candidate if it has only one param and form in [T, T&, const T&, volatile T&, const volatile T&] */ predicate hasGenericCopyCompatibleParameter() { - exists(TemplateParameter tp, Type pType | + exists(TypeTemplateParameter tp, Type pType | pType = this.getAParameter().getType().getUnspecifiedType() and //Parameter Type ( tp = pType //T diff --git a/cpp/autosar/src/rules/M14-6-1/NameInDependentBase.qll b/cpp/autosar/src/rules/M14-6-1/NameInDependentBase.qll deleted file mode 100644 index 93c99c3d76..0000000000 --- a/cpp/autosar/src/rules/M14-6-1/NameInDependentBase.qll +++ /dev/null @@ -1,67 +0,0 @@ -import cpp -import codingstandards.cpp.autosar - -/** - * Just the reverse of `Class.getABaseClass()` - */ -Class getParent(Class child) { child.getABaseClass() = result } - -/** - * There is a `MemberFunction` in parent class with same name - * as a `FunctionCall` that exists in a child `MemberFunction` - */ -FunctionCall parentMemberFunctionCall(Class child, Class parent) { - exists(MemberFunction parentFunction, Function other | - not other = parentFunction and - parent.getAMember() = parentFunction and - other.getName() = parentFunction.getName() and - result = other.getACallToThisFunction() and - result.getEnclosingFunction() = child.getAMemberFunction() - ) -} - -/** - * There is a `MemberFunction` in parent class with same name - * as a `FunctionAccess` that exists in a child `MemberFunction` - */ -FunctionAccess parentMemberFunctionAccess(Class child, Class parent) { - exists(MemberFunction parentFunction, Function other | - not other = parentFunction and - parent.getAMember() = parentFunction and - other.getName() = parentFunction.getName() and - result = other.getAnAccess() and - result.getEnclosingFunction() = child.getAMemberFunction() - ) -} - -/** - * There is a `MemberVariable` in parent class with same name - * as a `VariableAccess` that exists in a child `MemberFunction` - */ -Access parentMemberAccess(Class child, Class parent) { - exists(MemberVariable parentMember, Variable other | - not other = parentMember and - parent.getAMemberVariable() = parentMember and - other.getName() = parentMember.getName() and - result = other.getAnAccess() and - result.getEnclosingFunction() = child.getAMemberFunction() - ) -} - -/** - * A `NameQualifiableElement` without a name qualifier - */ -predicate missingNameQualifier(NameQualifiableElement element) { - not exists(NameQualifier q | q = element.getNameQualifier()) -} - -/** - * heuristics: do not care about: - * compiler generated calls - * superclass constructor use, in constructor delegation - */ -predicate isCustomExcluded(Expr fn) { - fn.isCompilerGenerated() or - fn instanceof ConstructorDirectInit or - fn.(FunctionCall).getTarget() instanceof Operator -} diff --git a/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.ql b/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.ql index 7d23ddb949..486a428474 100644 --- a/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.ql +++ b/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.ql @@ -16,26 +16,11 @@ import cpp import codingstandards.cpp.autosar -import NameInDependentBase +import codingstandards.cpp.rules.namenotreferredusingaqualifiedidorthis.NameNotReferredUsingAQualifiedIdOrThis -from Class c, Class p, NameQualifiableElement fn -where - not isExcluded(fn, TemplatesPackage::nameNotReferredUsingAQualifiedIdOrThisQuery()) and - not isCustomExcluded(fn) and - p = getParent(c) and - missingNameQualifier(fn) and - ( - fn instanceof FunctionAccess and - fn = parentMemberFunctionAccess(c, p) - or - fn instanceof FunctionCall and - fn = parentMemberFunctionCall(c, p) and - not exists(Expr e | e = fn.(FunctionCall).getQualifier()) - or - fn instanceof VariableAccess and - not fn.(VariableAccess).getTarget() instanceof Parameter and - fn = parentMemberAccess(c, p) and - not exists(Expr e | e = fn.(VariableAccess).getQualifier()) - ) and - not fn.isAffectedByMacro() -select fn, "Use of identifier that also exists in a base class that is not fully qualified." +class NameNotReferredUsingAQualifiedIdOrThisQuery extends NameNotReferredUsingAQualifiedIdOrThisSharedQuery +{ + NameNotReferredUsingAQualifiedIdOrThisQuery() { + this = TemplatesPackage::nameNotReferredUsingAQualifiedIdOrThisQuery() + } +} diff --git a/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.ql b/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.ql index e231967ad1..ea56b841ed 100644 --- a/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.ql +++ b/cpp/autosar/src/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.ql @@ -16,25 +16,11 @@ import cpp import codingstandards.cpp.autosar -import NameInDependentBase +import codingstandards.cpp.rules.namenotreferredusingaqualifiedidorthisaudit.NameNotReferredUsingAQualifiedIdOrThisAudit -from Class c, Class p, NameQualifiableElement fn -where - not isExcluded(fn, TemplatesPackage::nameNotReferredUsingAQualifiedIdOrThisAuditQuery()) and - not isCustomExcluded(fn) and - p = getParent(c) and - missingNameQualifier(fn) and - ( - fn instanceof FunctionAccess and - fn = parentMemberFunctionAccess(c, p) - or - fn instanceof FunctionCall and - fn = parentMemberFunctionCall(c, p) and - not exists(Expr e | e = fn.(FunctionCall).getQualifier()) - or - fn instanceof VariableAccess and - not fn.(VariableAccess).getTarget() instanceof Parameter and - fn = parentMemberAccess(c, p) and - not exists(Expr e | e = fn.(VariableAccess).getQualifier()) - ) -select fn, "Use of identifier that also exists in a base class that is not fully qualified." +class NameNotReferredUsingAQualifiedIdOrThisAuditQuery extends NameNotReferredUsingAQualifiedIdOrThisAuditSharedQuery +{ + NameNotReferredUsingAQualifiedIdOrThisAuditQuery() { + this = TemplatesPackage::nameNotReferredUsingAQualifiedIdOrThisAuditQuery() + } +} diff --git a/cpp/autosar/src/rules/M15-1-3/EmptyThrowOutsideCatch.ql b/cpp/autosar/src/rules/M15-1-3/EmptyThrowOutsideCatch.ql index 7e263d66bb..9f99e7c356 100644 --- a/cpp/autosar/src/rules/M15-1-3/EmptyThrowOutsideCatch.ql +++ b/cpp/autosar/src/rules/M15-1-3/EmptyThrowOutsideCatch.ql @@ -15,9 +15,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.emptythrowonlywithinacatchhandler.EmptyThrowOnlyWithinACatchHandler -from ReThrowExpr re -where - not isExcluded(re, Exceptions1Package::emptyThrowOutsideCatchQuery()) and - not re.getEnclosingElement+() instanceof CatchBlock -select re, "Rethrow outside catch block" +class EmptyThrowOutsideCatchQuery extends EmptyThrowOnlyWithinACatchHandlerSharedQuery { + EmptyThrowOutsideCatchQuery() { this = Exceptions1Package::emptyThrowOutsideCatchQuery() } +} diff --git a/cpp/autosar/src/rules/M15-3-3/DestroyedValueReferencedInDestructorCatchBlock.ql b/cpp/autosar/src/rules/M15-3-3/DestroyedValueReferencedInDestructorCatchBlock.ql index 6346e3fc6a..ba0c499add 100644 --- a/cpp/autosar/src/rules/M15-3-3/DestroyedValueReferencedInDestructorCatchBlock.ql +++ b/cpp/autosar/src/rules/M15-3-3/DestroyedValueReferencedInDestructorCatchBlock.ql @@ -18,7 +18,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.destroyedvaluereferencedindestructorcatchblock.DestroyedValueReferencedInDestructorCatchBlock -class DestroyedValueReferencedInDestructorCatchBlockQuery extends DestroyedValueReferencedInDestructorCatchBlockSharedQuery { +class DestroyedValueReferencedInDestructorCatchBlockQuery extends DestroyedValueReferencedInDestructorCatchBlockSharedQuery +{ DestroyedValueReferencedInDestructorCatchBlockQuery() { this = Exceptions2Package::destroyedValueReferencedInDestructorCatchBlockQuery() } diff --git a/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql b/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql index 105ba04144..38312b7b1a 100644 --- a/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql +++ b/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql @@ -18,7 +18,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument -class FunctionLikeMacroArgsContainHashTokenCQueryQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery { +class FunctionLikeMacroArgsContainHashTokenCQueryQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery +{ FunctionLikeMacroArgsContainHashTokenCQueryQuery() { this = MacrosPackage::functionLikeMacroArgsContainHashTokenQuery() } diff --git a/cpp/autosar/src/rules/M16-0-6/FunctionLikeMacroParameterNotEnclosedInParentheses.ql b/cpp/autosar/src/rules/M16-0-6/FunctionLikeMacroParameterNotEnclosedInParentheses.ql index 02eb1fe3a5..13dd7b7a11 100644 --- a/cpp/autosar/src/rules/M16-0-6/FunctionLikeMacroParameterNotEnclosedInParentheses.ql +++ b/cpp/autosar/src/rules/M16-0-6/FunctionLikeMacroParameterNotEnclosedInParentheses.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.macroparameternotenclosedinparentheses.MacroParameterNotEnclosedInParentheses -class MacroParameterNotEnclosedInParenthesesCQueryQuery extends MacroParameterNotEnclosedInParenthesesSharedQuery { +class MacroParameterNotEnclosedInParenthesesCQueryQuery extends MacroParameterNotEnclosedInParenthesesSharedQuery +{ MacroParameterNotEnclosedInParenthesesCQueryQuery() { this = MacrosPackage::functionLikeMacroParameterNotEnclosedInParenthesesQuery() } diff --git a/cpp/autosar/src/rules/M16-0-7/UndefinedMacroIdentifiersUsedIn.ql b/cpp/autosar/src/rules/M16-0-7/UndefinedMacroIdentifiersUsedIn.ql index 9e0ceb1051..bd3293ab1b 100644 --- a/cpp/autosar/src/rules/M16-0-7/UndefinedMacroIdentifiersUsedIn.ql +++ b/cpp/autosar/src/rules/M16-0-7/UndefinedMacroIdentifiersUsedIn.ql @@ -19,7 +19,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.undefinedmacroidentifiers.UndefinedMacroIdentifiers -class UndefinedMacroIdentifiersUsedInQuery extends UndefinedMacroIdentifiersQuery { +class UndefinedMacroIdentifiersUsedInQuery extends UndefinedMacroIdentifiersSharedQuery { UndefinedMacroIdentifiersUsedInQuery() { this = MacrosPackage::charactersOccurInHeaderFileNameOrInIncludeDirectiveQuery() } diff --git a/cpp/autosar/src/rules/M16-1-1/DefinedMacro.qll b/cpp/autosar/src/rules/M16-1-1/DefinedMacro.qll index 6fed938b38..91d6f614a0 100644 --- a/cpp/autosar/src/rules/M16-1-1/DefinedMacro.qll +++ b/cpp/autosar/src/rules/M16-1-1/DefinedMacro.qll @@ -2,27 +2,27 @@ import cpp import codingstandards.cpp.autosar /** - * A helper class describing macros wrapping defined operator + * A helper class describing macros wrapping the defined operator */ -class DefinedMacro extends Macro { - DefinedMacro() { - this.getBody().regexpMatch("defined\\s*\\(.*") +class MacroUsesDefined extends Macro { + MacroUsesDefined() { + // Uses `defined` directly + exists(this.getBody().regexpFind("\\bdefined\\b", _, _)) or - this.getBody().regexpMatch("defined[\\s]+|defined$") + // Uses a macro that uses `defined` (directly or indirectly) + exists(MacroUsesDefined dm | exists(this.getBody().regexpFind(dm.getRegexForMatch(), _, _))) } - Macro getAUse() { - result = this or - anyAliasing(result, this) + /** + * Gets a regex for matching uses of this macro. + */ + string getRegexForMatch() { + exists(string arguments | + // If there are arguments + if getHead() = getName() then arguments = "" else arguments = "\\s*\\(" + | + // Use word boundary matching to find identifiers that match + result = "\\b" + getName() + "\\b" + arguments + ) } } - -predicate directAlias(Macro alias, Macro aliased) { - not alias.getBody() = alias.getBody().replaceAll(aliased.getHead(), "") -} - -predicate anyAliasing(Macro alias, Macro inQ) { - directAlias(alias, inQ) - or - exists(Macro intermediate | anyAliasing(intermediate, inQ) and anyAliasing(alias, intermediate)) -} diff --git a/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql b/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql index fc86674d9d..761ef27ebb 100644 --- a/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql +++ b/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql @@ -14,18 +14,12 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.PreprocessorDirective import DefinedMacro -from DefinedMacro m, PreprocessorBranch e +from PreprocessorIfOrElif e, MacroUsesDefined m where - ( - e instanceof PreprocessorIf or - e instanceof PreprocessorElif - ) and - ( - e.getHead().regexpMatch(m.getAUse().getHead() + "\\s*\\(.*") - or - e.getHead().regexpMatch(m.getAUse().getHead().replaceAll("(", "\\(").replaceAll(")", "\\)")) - ) and - not isExcluded(e) + not isExcluded(e, MacrosPackage::definedPreProcessorOperatorInOneOfTheTwoStandardFormsQuery()) and + // A`#if` or `#elif` which uses a macro which uses `defined` + exists(e.getHead().regexpFind(m.getRegexForMatch(), _, _)) select e, "The macro $@ expands to 'defined'", m, m.getName() diff --git a/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.ql b/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.ql index f5dcb2f5dc..2a53875067 100644 --- a/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.ql +++ b/cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.ql @@ -15,6 +15,7 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.PreprocessorDirective //get what comes after each 'defined' used with or without parenth string matchesDefinedOperator(PreprocessorBranch e) { @@ -34,12 +35,8 @@ string matchesDefinedOperator(PreprocessorBranch e) { ) } -from PreprocessorBranch e, string arg +from PreprocessorIfOrElif e, string arg where - ( - e instanceof PreprocessorIf or - e instanceof PreprocessorElif - ) and arg = matchesDefinedOperator(e) and not arg.regexpMatch("^\\w*$") and not isExcluded(e, MacrosPackage::definedPreProcessorOperatorInOneOfTheTwoStandardFormsQuery()) diff --git a/cpp/autosar/src/rules/M16-3-2/HashOperatorsShouldNotBeUsed.ql b/cpp/autosar/src/rules/M16-3-2/HashOperatorsShouldNotBeUsed.ql index e0b40b6ba8..887b67471f 100644 --- a/cpp/autosar/src/rules/M16-3-2/HashOperatorsShouldNotBeUsed.ql +++ b/cpp/autosar/src/rules/M16-3-2/HashOperatorsShouldNotBeUsed.ql @@ -17,6 +17,6 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.hashoperatorsused.HashOperatorsUsed -class HashOperatorsShallNotBeUsedInQuery extends HashOperatorsUsedQuery { +class HashOperatorsShallNotBeUsedInQuery extends HashOperatorsUsedSharedQuery { HashOperatorsShallNotBeUsedInQuery() { this = MacrosPackage::hashOperatorsShouldNotBeUsedQuery() } } diff --git a/cpp/autosar/src/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.ql b/cpp/autosar/src/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.ql index 6a6f55e0f4..5e821fc5ff 100644 --- a/cpp/autosar/src/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.ql +++ b/cpp/autosar/src/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.ql @@ -15,17 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.donotusesetjmporlongjmpshared.DoNotUseSetjmpOrLongjmpShared -predicate isLongJumpCall(FunctionCall fc) { fc.getTarget().hasGlobalOrStdName("longjmp") } - -predicate isSetJumpCall(MacroInvocation mi) { mi.getMacroName() = "setjmp" } - -from Element jmp, string callType -where - not isExcluded(jmp, BannedFunctionsPackage::setjmpMacroAndTheLongjmpFunctionUsedQuery()) and - ( - isLongJumpCall(jmp) and callType = "longjmp function" - or - isSetJumpCall(jmp) and callType = "setjmp macro" - ) -select jmp, "Use of banned " + callType + "." +class SetjmpMacroAndTheLongjmpFunctionUsedQuery extends DoNotUseSetjmpOrLongjmpSharedSharedQuery { + SetjmpMacroAndTheLongjmpFunctionUsedQuery() { + this = BannedFunctionsPackage::setjmpMacroAndTheLongjmpFunctionUsedQuery() + } +} diff --git a/cpp/autosar/src/rules/M18-2-1/MacroOffsetofUsed.ql b/cpp/autosar/src/rules/M18-2-1/MacroOffsetofUsed.ql index a572497418..cd347be44d 100644 --- a/cpp/autosar/src/rules/M18-2-1/MacroOffsetofUsed.ql +++ b/cpp/autosar/src/rules/M18-2-1/MacroOffsetofUsed.ql @@ -15,9 +15,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.macrooffsetofused.MacroOffsetofUsed -from MacroInvocation mi -where - not isExcluded(mi, BannedFunctionsPackage::macroOffsetofUsedQuery()) and - mi.getMacroName() = "offsetof" -select mi, "Use of banned macro " + mi.getMacroName() + "." +class MacroOffsetofUsedQuery extends MacroOffsetofUsedSharedQuery { + MacroOffsetofUsedQuery() { this = BannedFunctionsPackage::macroOffsetofUsedQuery() } +} diff --git a/cpp/autosar/src/rules/M18-7-1/CsignalFunctionsUsed.ql b/cpp/autosar/src/rules/M18-7-1/CsignalFunctionsUsed.ql index ff264baffc..4df4715848 100644 --- a/cpp/autosar/src/rules/M18-7-1/CsignalFunctionsUsed.ql +++ b/cpp/autosar/src/rules/M18-7-1/CsignalFunctionsUsed.ql @@ -16,10 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.csignalfunctionsused.CsignalFunctionsUsed -from FunctionCall fc, Function f -where - not isExcluded(fc, BannedLibrariesPackage::csignalFunctionsUsedQuery()) and - f = fc.getTarget() and - f.hasGlobalOrStdName(["signal", "raise"]) -select fc, "Use of function '" + f.getQualifiedName() + "'." +class CsignalFunctionsUsedQuery extends CsignalFunctionsUsedSharedQuery { + CsignalFunctionsUsedQuery() { this = BannedLibrariesPackage::csignalFunctionsUsedQuery() } +} diff --git a/cpp/autosar/src/rules/M18-7-1/CsignalTypesUsed.ql b/cpp/autosar/src/rules/M18-7-1/CsignalTypesUsed.ql index c91d56c572..89e9ca169a 100644 --- a/cpp/autosar/src/rules/M18-7-1/CsignalTypesUsed.ql +++ b/cpp/autosar/src/rules/M18-7-1/CsignalTypesUsed.ql @@ -16,10 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.csignaltypesused.CsignalTypesUsed -from TypeMention tm, UserType ut -where - not isExcluded(tm, BannedLibrariesPackage::csignalTypesUsedQuery()) and - ut = tm.getMentionedType() and - ut.hasGlobalOrStdName("sig_atomic_t") -select tm, "Use of type '" + ut.getQualifiedName() + "'." +class CsignalTypesUsedQuery extends CsignalTypesUsedSharedQuery { + CsignalTypesUsedQuery() { this = BannedLibrariesPackage::csignalTypesUsedQuery() } +} diff --git a/cpp/autosar/src/rules/M2-10-1/DifferentIdentifiersNotTypographicallyUnambiguous.ql b/cpp/autosar/src/rules/M2-10-1/DifferentIdentifiersNotTypographicallyUnambiguous.ql index 9fe2bc2887..cb073ce6c5 100644 --- a/cpp/autosar/src/rules/M2-10-1/DifferentIdentifiersNotTypographicallyUnambiguous.ql +++ b/cpp/autosar/src/rules/M2-10-1/DifferentIdentifiersNotTypographicallyUnambiguous.ql @@ -19,7 +19,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.differentidentifiersnottypographicallyunambiguous.DifferentIdentifiersNotTypographicallyUnambiguous -class DifferentIdentifiersNotTypographicallyUnambiguousQuery extends DifferentIdentifiersNotTypographicallyUnambiguousSharedQuery { +class DifferentIdentifiersNotTypographicallyUnambiguousQuery extends DifferentIdentifiersNotTypographicallyUnambiguousSharedQuery +{ DifferentIdentifiersNotTypographicallyUnambiguousQuery() { this = NamingPackage::differentIdentifiersNotTypographicallyUnambiguousQuery() } diff --git a/cpp/autosar/src/rules/M2-13-2/UseOfNonZeroOctalLiteral.ql b/cpp/autosar/src/rules/M2-13-2/UseOfNonZeroOctalLiteral.ql index 2bd35e2484..b689edab6b 100644 --- a/cpp/autosar/src/rules/M2-13-2/UseOfNonZeroOctalLiteral.ql +++ b/cpp/autosar/src/rules/M2-13-2/UseOfNonZeroOctalLiteral.ql @@ -16,10 +16,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.Cpp14Literal +import codingstandards.cpp.rules.useofnonzerooctalliteral.UseOfNonZeroOctalLiteral -from Cpp14Literal::OctalLiteral octalLiteral -where - not isExcluded(octalLiteral, LiteralsPackage::useOfNonZeroOctalLiteralQuery()) and - not octalLiteral.getValue() = "0" -select octalLiteral, "Non zero octal literal " + octalLiteral.getValueText() + "." +class UseOfNonZeroOctalLiteralQuery extends UseOfNonZeroOctalLiteralSharedQuery { + UseOfNonZeroOctalLiteralQuery() { this = LiteralsPackage::useOfNonZeroOctalLiteralQuery() } +} diff --git a/cpp/autosar/src/rules/M2-13-3/MissingUSuffix.ql b/cpp/autosar/src/rules/M2-13-3/MissingUSuffix.ql index 25cae1e03f..5bfa338864 100644 --- a/cpp/autosar/src/rules/M2-13-3/MissingUSuffix.ql +++ b/cpp/autosar/src/rules/M2-13-3/MissingUSuffix.ql @@ -18,18 +18,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.Cpp14Literal +import codingstandards.cpp.rules.unsignedintegerliteralsnotappropriatelysuffixed.UnsignedIntegerLiteralsNotAppropriatelySuffixed -from Cpp14Literal::NumericLiteral nl, string literalKind -where - not isExcluded(nl, LiteralsPackage::missingUSuffixQuery()) and - ( - nl instanceof Cpp14Literal::OctalLiteral and literalKind = "Octal" - or - nl instanceof Cpp14Literal::HexLiteral and literalKind = "Hex" - ) and - // This either directly has an unsigned integer type, or it is converted to an unsigned integer type - nl.getType().getUnspecifiedType().(IntegralType).isUnsigned() and - // The literal already has a `u` or `U` suffix. - not nl.getValueText().regexpMatch(".*[lL]*[uU][lL]*") -select nl, literalKind + " literal is an unsigned integer but does not include a 'U' suffix." +class MissingUSuffixQuery extends UnsignedIntegerLiteralsNotAppropriatelySuffixedSharedQuery { + MissingUSuffixQuery() { this = LiteralsPackage::missingUSuffixQuery() } +} diff --git a/cpp/autosar/src/rules/M2-7-1/SlashStarUsedWithinACStyleComment.ql b/cpp/autosar/src/rules/M2-7-1/SlashStarUsedWithinACStyleComment.ql index 768acb0532..356a361cb1 100644 --- a/cpp/autosar/src/rules/M2-7-1/SlashStarUsedWithinACStyleComment.ql +++ b/cpp/autosar/src/rules/M2-7-1/SlashStarUsedWithinACStyleComment.ql @@ -16,9 +16,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.charactersequenceusedwithinacstylecomment.CharacterSequenceUsedWithinACStyleComment -from CStyleComment c -where - not isExcluded(c, CommentsPackage::slashStarUsedWithinACStyleCommentQuery()) and - exists(c.getContents().regexpFind("./\\*", _, _)) -select c, "C-style /* comment includes nested /*." +class SlashStarUsedWithinACStyleCommentQuery extends CharacterSequenceUsedWithinACStyleCommentSharedQuery +{ + SlashStarUsedWithinACStyleCommentQuery() { + this = CommentsPackage::slashStarUsedWithinACStyleCommentQuery() + } +} diff --git a/cpp/autosar/src/rules/M27-0-1/CstdioFunctionsUsed.ql b/cpp/autosar/src/rules/M27-0-1/CstdioFunctionsUsed.ql index 55254581a6..5656fc2edf 100644 --- a/cpp/autosar/src/rules/M27-0-1/CstdioFunctionsUsed.ql +++ b/cpp/autosar/src/rules/M27-0-1/CstdioFunctionsUsed.ql @@ -17,26 +17,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.cstdiofunctionsused.CstdioFunctionsUsed -from FunctionCall fc, Function f -where - not isExcluded(fc, BannedLibrariesPackage::cstdioFunctionsUsedQuery()) and - f = fc.getTarget() and - f.hasGlobalOrStdName([ - "remove", "rename", "tmpfile", "tmpnam", - // File access - "fclose", "fflush", "fopen", "freopen", "setbuf", "setvbuf", - // Formatted input/output - "fprintf", "fscanf", "printf", "scanf", "snprintf", "sprintf", "sscanf", "vfprintf", - "vfscanf", "vprintf", "vscanf", "vsnprintf", "vsprintf", "vsscanf", - // Character input/output - "fgetc", "fgets", "fputc", "fputs", "getc", "getchar", "gets", "putc", "putchar", "puts", - "ungetc", - // Direct input/output - "fread", "fwrite", - // File positioning - "fgetpos", "fseek", "fsetpos", "ftell", "rewind", - // Error handling - "clearerr", "feof", "ferror", "perror" - ]) -select fc, "Use of function '" + f.getQualifiedName() + "'." +class CstdioFunctionsUsedQuery extends CstdioFunctionsUsedSharedQuery { + CstdioFunctionsUsedQuery() { this = BannedLibrariesPackage::cstdioFunctionsUsedQuery() } +} diff --git a/cpp/autosar/src/rules/M27-0-1/CstdioMacrosUsed.ql b/cpp/autosar/src/rules/M27-0-1/CstdioMacrosUsed.ql index eadbc874f9..311baeb195 100644 --- a/cpp/autosar/src/rules/M27-0-1/CstdioMacrosUsed.ql +++ b/cpp/autosar/src/rules/M27-0-1/CstdioMacrosUsed.ql @@ -17,12 +17,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.cstdiomacrosused.CstdioMacrosUsed -from MacroInvocation mi -where - not isExcluded(mi, BannedLibrariesPackage::cstdioMacrosUsedQuery()) and - mi.getMacroName() in [ - "BUFSIZ", "EOF", "FILENAME_MAX", "FOPEN_MAX", "L_tmpnam", "NULL", "TMP_MAX", "_IOFBF", - "IOLBF", "_IONBF", "SEEK_CUR", "SEEK_END", "SEEK_SET" - ] -select mi, "Use of macro '" + mi.getMacroName() + "'." +class CstdioMacrosUsedQuery extends CstdioMacrosUsedSharedQuery { + CstdioMacrosUsedQuery() { this = BannedLibrariesPackage::cstdioMacrosUsedQuery() } +} diff --git a/cpp/autosar/src/rules/M27-0-1/CstdioTypesUsed.ql b/cpp/autosar/src/rules/M27-0-1/CstdioTypesUsed.ql index 442264fad1..3a1f647c22 100644 --- a/cpp/autosar/src/rules/M27-0-1/CstdioTypesUsed.ql +++ b/cpp/autosar/src/rules/M27-0-1/CstdioTypesUsed.ql @@ -17,10 +17,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.cstdiotypesused.CstdioTypesUsed -from TypeMention tm, UserType ut -where - not isExcluded(tm, BannedLibrariesPackage::cstdioTypesUsedQuery()) and - ut = tm.getMentionedType() and - ut.hasGlobalOrStdName(["FILE", "fpos_t", "size_t"]) -select tm, "Use of type '" + ut.getQualifiedName() + "'." +class CstdioTypesUsedQuery extends CstdioTypesUsedSharedQuery { + CstdioTypesUsedQuery() { this = BannedLibrariesPackage::cstdioTypesUsedQuery() } +} diff --git a/cpp/autosar/src/rules/M3-1-2/FunctionsDeclaredAtBlockScope.ql b/cpp/autosar/src/rules/M3-1-2/FunctionsDeclaredAtBlockScope.ql index 5cfc679596..87d9af147b 100644 --- a/cpp/autosar/src/rules/M3-1-2/FunctionsDeclaredAtBlockScope.ql +++ b/cpp/autosar/src/rules/M3-1-2/FunctionsDeclaredAtBlockScope.ql @@ -20,7 +20,7 @@ import codingstandards.cpp.autosar from DeclStmt decl, Function f where - not isExcluded(decl) and + not isExcluded(decl, DeclarationsPackage::functionsDeclaredAtBlockScopeQuery()) and not isExcluded(f, DeclarationsPackage::functionsDeclaredAtBlockScopeQuery()) and decl.getADeclaration() = f select f, "Function " + f.getName() + " is declared at block scope." diff --git a/cpp/autosar/src/rules/M3-2-1/DeclarationsOfAFunctionShallHaveCompatibleTypes.ql b/cpp/autosar/src/rules/M3-2-1/DeclarationsOfAFunctionShallHaveCompatibleTypes.ql index 2aa8535a35..68e948e0ce 100644 --- a/cpp/autosar/src/rules/M3-2-1/DeclarationsOfAFunctionShallHaveCompatibleTypes.ql +++ b/cpp/autosar/src/rules/M3-2-1/DeclarationsOfAFunctionShallHaveCompatibleTypes.ql @@ -22,7 +22,7 @@ import codingstandards.cpp.Typehelpers from FunctionDeclarationEntry f1, FunctionDeclarationEntry f2 where - not isExcluded(f1) and + not isExcluded(f1, DeclarationsPackage::declarationsOfAFunctionShallHaveCompatibleTypesQuery()) and not isExcluded(f2, DeclarationsPackage::declarationsOfAFunctionShallHaveCompatibleTypesQuery()) and not f1 = f2 and f1.getDeclaration() = f2.getDeclaration() and diff --git a/cpp/autosar/src/rules/M3-2-3/MultipleDeclarationViolation.ql b/cpp/autosar/src/rules/M3-2-3/MultipleDeclarationViolation.ql index 3af15858ca..30d94facb1 100644 --- a/cpp/autosar/src/rules/M3-2-3/MultipleDeclarationViolation.ql +++ b/cpp/autosar/src/rules/M3-2-3/MultipleDeclarationViolation.ql @@ -20,7 +20,7 @@ import codingstandards.cpp.Scope from DeclarationEntry de, DeclarationEntry otherDeclaration, string kind where - not isExcluded(de) and + not isExcluded(de, ScopePackage::multipleDeclarationViolationQuery()) and exists(Declaration d | de.getDeclaration() = d and otherDeclaration.getDeclaration() = d and diff --git a/cpp/autosar/src/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.ql b/cpp/autosar/src/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.ql index 150547078c..1698eb97a6 100644 --- a/cpp/autosar/src/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.ql +++ b/cpp/autosar/src/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.ql @@ -15,28 +15,11 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.Linkage +import codingstandards.cpp.rules.identifierwithexternallinkageonedefinitionshared.IdentifierWithExternalLinkageOneDefinitionShared -from Declaration d, DeclarationEntry de1, DeclarationEntry de2 -where - not isExcluded(d) and - not isExcluded([de1, de2]) and - hasExternalLinkage(d) and - d.isTopLevel() and - d = de1.getDeclaration() and - d = de2.getDeclaration() and - de1 != de2 and - de1.isDefinition() and - de2.isDefinition() and - // exceptions - not d instanceof TemplateClass and - (d instanceof Function implies not d.(Function).isInline()) and - // Apply an ordering based on location to enforce that (de1, de2) = (de2, de1) and we only report (de1, de2). - ( - de1.getFile().getAbsolutePath() < de2.getFile().getAbsolutePath() - or - de1.getFile().getAbsolutePath() = de2.getFile().getAbsolutePath() and - de1.getLocation().getStartLine() < de2.getLocation().getStartLine() - ) -select de1, "The identifier " + de1.getName() + " has external linkage and is redefined $@.", de2, - "here" +class IdentifierWithExternalLinkageOneDefinitionQuery extends IdentifierWithExternalLinkageOneDefinitionSharedSharedQuery +{ + IdentifierWithExternalLinkageOneDefinitionQuery() { + this = ScopePackage::identifierWithExternalLinkageShallHaveOneDefinitionQuery() + } +} diff --git a/cpp/autosar/src/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.ql b/cpp/autosar/src/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.ql index 86823160bd..e01f2e3343 100644 --- a/cpp/autosar/src/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.ql +++ b/cpp/autosar/src/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.ql @@ -15,14 +15,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.missingstaticspecifierfunctionredeclarationshared.MissingStaticSpecifierFunctionRedeclarationShared -from FunctionDeclarationEntry fde, FunctionDeclarationEntry redeclaration -where - not isExcluded(redeclaration) and - fde.hasSpecifier("static") and - fde.getDeclaration().isTopLevel() and - redeclaration.getDeclaration() = fde.getDeclaration() and - not redeclaration.hasSpecifier("static") and - fde != redeclaration -select redeclaration, "The redeclaration of $@ with internal linkage misses the static specifier.", - fde, "function" +class MissingStaticSpecifierOnFunctionRedeclarationQuery extends MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery +{ + MissingStaticSpecifierOnFunctionRedeclarationQuery() { + this = ScopePackage::missingStaticSpecifierOnFunctionRedeclarationQuery() + } +} diff --git a/cpp/autosar/src/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.ql b/cpp/autosar/src/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.ql index 6a99cb820a..e6dceb6ca3 100644 --- a/cpp/autosar/src/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.ql +++ b/cpp/autosar/src/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.ql @@ -15,277 +15,11 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.Scope -import codingstandards.cpp.SideEffect -import codingstandards.cpp.sideeffect.Customizations +import codingstandards.cpp.rules.unnecessaryexposedidentifierdeclarationshared.UnnecessaryExposedIdentifierDeclarationShared -class ExternalCall extends Call { - ExternalCall() { - exists(Function f | this.getTarget() = f | - not f.hasDefinition() and not f.isCompilerGenerated() - ) +class UnnecessaryExposedIdentifierDeclarationQuery extends UnnecessaryExposedIdentifierDeclarationSharedSharedQuery +{ + UnnecessaryExposedIdentifierDeclarationQuery() { + this = ScopePackage::unnecessaryExposedIdentifierDeclarationQuery() } } - -class LoopOrSwitchBody extends BlockStmt { - LoopOrSwitchBody() { - exists(Loop l | l.getStmt() = this.getParentScope*()) - or - exists(SwitchStmt ss | ss.getStmt() = this) - } -} - -/* Gets a scope for `b` that is an ancestor of `b`, but is not a loop or switch scope. */ -Scope getCandidateScope(Scope b) { - if b instanceof LoopOrSwitchBody or b instanceof ControlStructure - then result = getCandidateScope(b.getStrictParent()) - else - if b.isGenerated() - then result = b.getStrictParent() - else result = b -} - -private predicate getLocationInfo( - CandidateDeclaration d, PreprocessorBranchDirective pbd, int startline, int endline, string path1, - string path2 -) { - d.getLocation().getEndLine() = endline and - pbd.getLocation().getStartLine() = startline and - d.getFile().getAbsolutePath() = path1 and - pbd.getFile().getAbsolutePath() = path2 -} - -predicate isStrictlyBefore(CandidateDeclaration d, PreprocessorBranchDirective branch) { - exists(string path, int startLine, int endLine | - getLocationInfo(d, branch, startLine, endLine, path, path) and - endLine < startLine - ) -} - -Variable getADependentVariable(Variable v) { - exists(VariableAccess va | - va.getTarget() = result and v.getInitializer().getExpr().getAChild*() = va - ) -} - -/** - * Holds if it is assigned a value that is modified in between the declaration of `v` and a use of `v`. - */ -predicate isTempVariable(LocalVariable v) { - exists( - DeclStmt ds, VariableDeclarationEntry vde, Variable dependentVariable, Expr sideEffect, - VariableAccess va - | - v.getAnAccess() = va and - dependentVariable = getADependentVariable(v) and - exists( - BasicBlock declarationStmtBb, BasicBlock sideEffectBb, BasicBlock variableAccessBb, - int declarationStmtPos, int sideEffectPos, int variableAccessPos - | - declarationStmtBb.getNode(declarationStmtPos) = ds and - variableAccessBb.getNode(variableAccessPos) = va - | - ( - ( - sideEffect.(VariableEffect).getTarget() = dependentVariable and - if not sideEffect.getEnclosingFunction() = va.getEnclosingFunction() - then - exists(FunctionCall call | - call.getEnclosingFunction() = va.getEnclosingFunction() and - call.getTarget().calls(sideEffect.getEnclosingFunction()) and - sideEffectBb.getNode(sideEffectPos) = call - ) - else sideEffectBb.getNode(sideEffectPos) = sideEffect - ) - or - dependentVariable instanceof GlobalVariable and - sideEffect instanceof ExternalCall and - ds.getEnclosingFunction() = sideEffect.getEnclosingFunction() and - sideEffectBb.getNode(sideEffectPos) = sideEffect - ) and - ( - declarationStmtBb.getASuccessor+() = sideEffectBb - or - declarationStmtBb = sideEffectBb and declarationStmtPos < sideEffectPos - ) and - ( - sideEffectBb.getASuccessor+() = variableAccessBb - or - sideEffectBb = variableAccessBb and sideEffectPos < variableAccessPos - ) - ) and - vde.getDeclaration() = v and - ds.getDeclarationEntry(_) = vde - ) -} - -private predicate isTypeUse(Type t1, Type t2) { - t1.getUnspecifiedType() = t2 - or - t1.(PointerType).getBaseType().getUnspecifiedType() = t2 - or - t1.(ReferenceType).getBaseType().getUnspecifiedType() = t2 - or - t1.(ArrayType).getBaseType().getUnspecifiedType() = t2 -} - -newtype TDeclarationAccess = - ObjectAccess(Variable v, VariableAccess va) { va = v.getAnAccess() } or - /* Type access can be done in a declaration or an expression (e.g., static member function call) */ - TypeAccess(Type t, Element access) { - isTypeUse(access.(Variable).getUnspecifiedType(), t) - or - exists(ClassTemplateInstantiation cti | - isTypeUse(cti.getATemplateArgument(), t) and - access.(Variable).getUnspecifiedType() = cti - ) - or - exists(FunctionTemplateInstantiation fti | - isTypeUse(fti.getATemplateArgument(), t) and - fti = access - ) - or - exists(FunctionCall call, MemberFunction mf | - call = access and call.getTarget() = mf and mf.isStatic() and mf.getDeclaringType() = t - ) - or - exists(Function f | - isTypeUse(f.getType(), t) and - f = access - ) - } - -class DeclarationAccess extends TDeclarationAccess { - Location getLocation() { - exists(VariableAccess va, Variable v | this = ObjectAccess(v, va) and result = va.getLocation()) - or - exists(Element access | - this = TypeAccess(_, access) and - result = access.getLocation() - ) - } - - string toString() { - exists(Variable v | this = ObjectAccess(v, _) and result = "Object access for " + v.getName()) - or - exists(Type t | - this = TypeAccess(t, _) and - result = "Type access for " + t.getName() - ) - } - - /* Gets the declaration that is being accessed. */ - Declaration getDeclaration() { - this = ObjectAccess(result, _) - or - this = TypeAccess(result, _) - } - - /* Gets the declaration or expression that uses the type being accessed. */ - Element getUnderlyingTypeAccess() { this = TypeAccess(_, result) } - - VariableAccess getUnderlyingObjectAccess() { this = ObjectAccess(_, result) } - - /* Gets the scope of the access. */ - Scope getScope() { - exists(VariableAccess va | - va = getUnderlyingObjectAccess() and - result.getAnExpr() = va - ) - or - exists(Element e | e = getUnderlyingTypeAccess() and result = e.getParentScope()) - } - - /* Holds if a type access is generated from the template instantiation `instantionion` */ - predicate isFromTemplateInstantiation(Element instantiation) { - exists(Element access | - this = TypeAccess(_, access) and access.isFromTemplateInstantiation(instantiation) - ) - } - - predicate isCompilerGenerated() { - exists(VariableAccess va | va = getUnderlyingObjectAccess() and va.isCompilerGenerated()) - or - exists(Element e | - e = getUnderlyingTypeAccess() and - (compgenerated(underlyingElement(e)) or compgenerated(underlyingElement(e.getParentScope()))) - ) - } -} - -class CandidateDeclaration extends Declaration { - CandidateDeclaration() { - this instanceof LocalVariable - or - this instanceof GlobalOrNamespaceVariable - or - this instanceof Type and - not this instanceof ClassTemplateInstantiation and - not this instanceof TemplateParameter - } -} - -/* Gets the scopes that include all the declaration accesses for declaration `d`. */ -Scope possibleScopesForDeclaration(CandidateDeclaration d) { - forex(Scope scope, DeclarationAccess da | - da.getDeclaration() = d and - // Exclude declaration accesses that are compiler generated so we can minimize the visibility of types. - // Otherwise, for example, we cannot reduce the scope of classes with compiler generated member functions based on - // declaration accesses. - not da.isCompilerGenerated() and - not da.isFromTemplateInstantiation(_) and - scope = da.getScope() - | - result = scope.getStrictParent*() - ) and - // Limit the best scope to block statements and namespaces or control structures - (result instanceof BlockStmt or result instanceof Namespace) -} - -/* Gets the smallest scope that includes all the declaration accesses of declaration `d`. */ -Scope bestScopeForDeclarationEntry(CandidateDeclaration d, Scope currentScope) { - result = possibleScopesForDeclaration(d) and - not exists(Scope other | other = possibleScopesForDeclaration(d) | result = other.getAnAncestor()) and - currentScope.getADeclaration() = d and - result.getAnAncestor() = currentScope and - not result instanceof LoopOrSwitchBody and - not result.isGenerated() -} - -/** - * Gets a string suitable for printing a scope in an alert message, that includes an `$@` - * formatting string. - * - * This is necessary because some scopes (e.g. `Namespace`) do not have meaningful - * locations in the database and the alert message will not render the name if that is the case. - */ -string getScopeDescription(Scope s) { - if s instanceof GlobalNamespace then result = "the global namespace scope$@" else result = "$@" -} - -from CandidateDeclaration d, Scope candidateScope, Scope currentScope -where - not isExcluded(d, ScopePackage::unnecessaryExposedIdentifierDeclarationQuery()) and - candidateScope = bestScopeForDeclarationEntry(d, currentScope) and - // We can't reduce the scope if the value stored in the declaration is changed before the declared - // variable is used, because this would change the semantics of the use. - (d instanceof Variable implies not isTempVariable(d)) and - not exists(AddressOfExpr e | e.getAddressable() = d) and - // We can't reduce the scope of the declaration if its minimal scope resides inside a preprocessor - // branch directive while the current scope isn't. This can result in an incorrect program - // where a variable is used but not declared. - not exists(PreprocessorBranchDirective branch | - isStrictlyBefore(d, branch) and - branch = candidateScope.getAnEnclosingPreprocessorBranchDirective() - ) and - // We can't promote a class to a local class if it has static data members (See [class.local] paragraph 4 N3797.) - ( - (d instanceof Class and candidateScope.getStrictParent() instanceof Function) - implies - not exists(Variable member | d.(Class).getAMember() = member and member.isStatic()) - ) and - not candidateScope.isAffectedByMacro() -select d, - "The declaration " + d.getName() + " should be moved from " + getScopeDescription(currentScope) + - " into the " + getScopeDescription(candidateScope) + " too minimize its visibility.", - currentScope, "scope", candidateScope, "scope" diff --git a/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInObjectDeclarations.ql b/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInObjectDeclarations.ql index 7fa5f6078d..7e27160690 100644 --- a/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInObjectDeclarations.ql +++ b/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInObjectDeclarations.ql @@ -18,7 +18,7 @@ import codingstandards.cpp.autosar from VariableDeclarationEntry decl1, VariableDeclarationEntry decl2 where - not isExcluded(decl1) and + not isExcluded(decl1, DeclarationsPackage::typesNotIdenticalInObjectDeclarationsQuery()) and not isExcluded(decl2, DeclarationsPackage::typesNotIdenticalInObjectDeclarationsQuery()) and decl1.getDeclaration() = decl2.getDeclaration() and not decl1.getType() = decl2.getType() diff --git a/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInReturnDeclarations.ql b/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInReturnDeclarations.ql index b99c656692..7f11f777b4 100644 --- a/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInReturnDeclarations.ql +++ b/cpp/autosar/src/rules/M3-9-1/TypesNotIdenticalInReturnDeclarations.ql @@ -15,12 +15,10 @@ import cpp import codingstandards.cpp.autosar -import cpp -import codingstandards.cpp.autosar from FunctionDeclarationEntry f1, FunctionDeclarationEntry f2 where - not isExcluded(f1) and + not isExcluded(f1, DeclarationsPackage::typesNotIdenticalInReturnDeclarationsQuery()) and not isExcluded(f2, DeclarationsPackage::typesNotIdenticalInReturnDeclarationsQuery()) and f1.getDeclaration() = f2.getDeclaration() and not f1.getType() = f2.getType() diff --git a/cpp/autosar/src/rules/M4-5-3/CharUsedAsOperandsToDisallowedBuiltInOperators.ql b/cpp/autosar/src/rules/M4-5-3/CharUsedAsOperandsToDisallowedBuiltInOperators.ql index 7df022ba37..100d9f5d76 100644 --- a/cpp/autosar/src/rules/M4-5-3/CharUsedAsOperandsToDisallowedBuiltInOperators.ql +++ b/cpp/autosar/src/rules/M4-5-3/CharUsedAsOperandsToDisallowedBuiltInOperators.ql @@ -22,7 +22,7 @@ import codingstandards.cpp.autosar from Operation o where - not isExcluded(o) and + not isExcluded(o, ExpressionsPackage::charUsedAsOperandsToDisallowedBuiltInOperatorsQuery()) and not ( o instanceof EqualityOperation or o instanceof BitwiseAndExpr or diff --git a/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql b/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql index 3b6e436c56..8e48c05ada 100644 --- a/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql +++ b/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql @@ -16,23 +16,19 @@ import cpp import codingstandards.cpp.autosar -from Variable v, Expr aexp +from Conversion c where - not isExcluded(v, + not isExcluded(c, StringsPackage::signedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValuesQuery()) and - // We find cases where it is an explicitly signed char type with an assignment - // to a non-numeric type. NOTE: This rule addresses cases where the char type - // is used character data only, the rule does not explicitly cover this. - // Please see M5-0-11 for explicit handling of this case. Get types that are - // char, except for ones that are 'plain', meaning the sign is explicit. + /* 1. Focus on implicit conversions only (explicit conversions are acceptable). */ + c.isImplicit() and + /* 2. The target type is explicitly signed or unsigned char. */ ( - v.getUnspecifiedType() instanceof SignedCharType or - v.getUnspecifiedType() instanceof UnsignedCharType + c.getUnspecifiedType() instanceof SignedCharType or + c.getUnspecifiedType() instanceof UnsignedCharType ) and - // Identify places where these explicitly signed types are being assigned to a - // non-numeric type. - aexp = v.getAnAssignedValue() and - aexp.getUnspecifiedType() instanceof CharType -select aexp, - "Assignment of an non-integer type to variable $@ which is a variable with an explicitly signed char type", - v, v.getName() + /* 3. Check if the source expression is a plain char type, i.e. not explicitly signed / unsigned. */ + c.getExpr().getUnspecifiedType() instanceof PlainCharType +select c, + "This expression of plain char type is implicitly converted to '" + + c.getUnspecifiedType().getName() + "'." diff --git a/cpp/autosar/src/rules/M5-0-15/IndexingNotTheOnlyFormOfPointerArithmetic.ql b/cpp/autosar/src/rules/M5-0-15/IndexingNotTheOnlyFormOfPointerArithmetic.ql index ab545a5225..90cf3132ac 100644 --- a/cpp/autosar/src/rules/M5-0-15/IndexingNotTheOnlyFormOfPointerArithmetic.ql +++ b/cpp/autosar/src/rules/M5-0-15/IndexingNotTheOnlyFormOfPointerArithmetic.ql @@ -18,7 +18,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.useonlyarrayindexingforpointerarithmetic.UseOnlyArrayIndexingForPointerArithmetic -class IndexingNotTheOnlyFormOfPointerArithmeticQuery extends UseOnlyArrayIndexingForPointerArithmeticSharedQuery { +class IndexingNotTheOnlyFormOfPointerArithmeticQuery extends UseOnlyArrayIndexingForPointerArithmeticSharedQuery +{ IndexingNotTheOnlyFormOfPointerArithmeticQuery() { this = PointersPackage::indexingNotTheOnlyFormOfPointerArithmeticQuery() } diff --git a/cpp/autosar/src/rules/M5-0-16/PointerAndDerivedPointerAccessDifferentArray.ql b/cpp/autosar/src/rules/M5-0-16/PointerAndDerivedPointerAccessDifferentArray.ql index 8cbacf949f..a403c6b533 100644 --- a/cpp/autosar/src/rules/M5-0-16/PointerAndDerivedPointerAccessDifferentArray.ql +++ b/cpp/autosar/src/rules/M5-0-16/PointerAndDerivedPointerAccessDifferentArray.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.donotusepointerarithmetictoaddressdifferentarrays.DoNotUsePointerArithmeticToAddressDifferentArrays -class PointerAndDerivedPointerAccessDifferentArrayQuery extends DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery { +class PointerAndDerivedPointerAccessDifferentArrayQuery extends DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery +{ PointerAndDerivedPointerAccessDifferentArrayQuery() { this = PointersPackage::pointerAndDerivedPointerAccessDifferentArrayQuery() } diff --git a/cpp/autosar/src/rules/M5-0-17/PointerSubtractionOnDifferentArrays.ql b/cpp/autosar/src/rules/M5-0-17/PointerSubtractionOnDifferentArrays.ql index 3000685f54..d6d4f6130a 100644 --- a/cpp/autosar/src/rules/M5-0-17/PointerSubtractionOnDifferentArrays.ql +++ b/cpp/autosar/src/rules/M5-0-17/PointerSubtractionOnDifferentArrays.ql @@ -16,40 +16,41 @@ import cpp import codingstandards.cpp.autosar import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import ArrayToPointerDiffOperandFlow::PathGraph -class ArrayToPointerDiffOperandConfig extends DataFlow::Configuration { - ArrayToPointerDiffOperandConfig() { this = "ArrayToPointerDiffOperandConfig" } - - override predicate isSource(DataFlow::Node source) { +module ArrayToPointerDiffOperandConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr().(VariableAccess).getType() instanceof ArrayType or // Consider array to pointer decay for parameters. source.asExpr().(VariableAccess).getTarget().(Parameter).getType() instanceof ArrayType } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(PointerDiffExpr e | e.getAnOperand() = sink.asExpr()) } - override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { // Add a flow step from the base to the array expression to track pointers to elements of the array. exists(ArrayExpr e | e.getArrayBase() = pred.asExpr() and e = succ.asExpr()) } } +module ArrayToPointerDiffOperandFlow = DataFlow::Global; + from PointerDiffExpr pointerSubstraction, Variable currentOperandPointee, Variable otherOperandPointee, - DataFlow::PathNode source, DataFlow::PathNode sink, string side + ArrayToPointerDiffOperandFlow::PathNode source, ArrayToPointerDiffOperandFlow::PathNode sink, + string side where not isExcluded(pointerSubstraction, PointersPackage::pointerSubtractionOnDifferentArraysQuery()) and - exists(ArrayToPointerDiffOperandConfig c, Variable sourceLeft, Variable sourceRight | - c.hasFlow(DataFlow::exprNode(sourceLeft.getAnAccess()), + exists(Variable sourceLeft, Variable sourceRight | + ArrayToPointerDiffOperandFlow::flow(DataFlow::exprNode(sourceLeft.getAnAccess()), DataFlow::exprNode(pointerSubstraction.getLeftOperand())) and - c.hasFlow(DataFlow::exprNode(sourceRight.getAnAccess()), + ArrayToPointerDiffOperandFlow::flow(DataFlow::exprNode(sourceRight.getAnAccess()), DataFlow::exprNode(pointerSubstraction.getRightOperand())) and not sourceLeft = sourceRight and - c.hasFlowPath(source, sink) and + ArrayToPointerDiffOperandFlow::flowPath(source, sink) and ( source.getNode().asExpr() = sourceLeft.getAnAccess() and sink.getNode().asExpr() = pointerSubstraction.getLeftOperand() and diff --git a/cpp/autosar/src/rules/M5-0-18/AppliedToObjectsOfPointerType.ql b/cpp/autosar/src/rules/M5-0-18/AppliedToObjectsOfPointerType.ql index da6f82dd48..d8113cde03 100644 --- a/cpp/autosar/src/rules/M5-0-18/AppliedToObjectsOfPointerType.ql +++ b/cpp/autosar/src/rules/M5-0-18/AppliedToObjectsOfPointerType.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.donotuserelationaloperatorswithdifferingarrays.DoNotUseRelationalOperatorsWithDifferingArrays -class AppliedToObjectsOfPointerTypeQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery { +class AppliedToObjectsOfPointerTypeQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery +{ AppliedToObjectsOfPointerTypeQuery() { this = PointersPackage::appliedToObjectsOfPointerTypeQuery() } diff --git a/cpp/autosar/src/rules/M5-0-2/GratuitousUseOfParentheses.ql b/cpp/autosar/src/rules/M5-0-2/GratuitousUseOfParentheses.ql index 1cb823fc5b..41d3eb6944 100644 --- a/cpp/autosar/src/rules/M5-0-2/GratuitousUseOfParentheses.ql +++ b/cpp/autosar/src/rules/M5-0-2/GratuitousUseOfParentheses.ql @@ -84,7 +84,7 @@ predicate isGratuitousUseOfParentheses(ParenthesisExpr pe) { from ParenthesisExpr p where - not isExcluded(p) and + not isExcluded(p, OrderOfEvaluationPackage::gratuitousUseOfParenthesesQuery()) and isGratuitousUseOfParentheses(p) and not p.isInMacroExpansion() select p, "Gratuitous use of parentheses around $@.", p.getExpr(), p.getExpr().toString() diff --git a/cpp/autosar/src/rules/M5-0-2/InsufficientUseOfParentheses.ql b/cpp/autosar/src/rules/M5-0-2/InsufficientUseOfParentheses.ql new file mode 100644 index 0000000000..1dda0df93f --- /dev/null +++ b/cpp/autosar/src/rules/M5-0-2/InsufficientUseOfParentheses.ql @@ -0,0 +1,44 @@ +/** + * @id cpp/autosar/insufficient-use-of-parentheses + * @name M5-0-2: Limited dependence should be placed on C++ operator precedence rules in expressions + * @description The use of parentheses can be used to emphasize precedence and increase code + * readability. + * @kind problem + * @precision medium + * @problem.severity recommendation + * @tags external/autosar/id/m5-0-2 + * external/autosar/audit + * readability + * external/autosar/allocated-target/implementation + * external/autosar/enforcement/partially-automated + * external/autosar/obligation/advisory + */ + +import cpp +import codingstandards.cpp.autosar +import semmle.code.cpp.commons.Assertions + +class InsufficientlyParenthesizedExpr extends Expr { + InsufficientlyParenthesizedExpr() { + // Exclude expressions affected by macros, including assertions, because + // it is unclear that the expression must be parenthesized since it seems + // to be the top-level expression instead of an operand of a binary or ternary operation. + not this.isAffectedByMacro() and + ( + exists(BinaryOperation root, BinaryOperation child | child = this | + root.getAnOperand() = child and + root.getOperator() != child.getOperator() and + not any(ParenthesisExpr pe).getExpr() = child + ) + or + exists(ConditionalExpr root, BinaryOperation child | child = this | + root.getAnOperand() = child and + not any(ParenthesisExpr pe).getExpr() = child + ) + ) + } +} + +from InsufficientlyParenthesizedExpr e +where not isExcluded(e, OrderOfEvaluationPackage::insufficientUseOfParenthesesQuery()) +select e, "Dependence on operator precedence rules." diff --git a/cpp/autosar/src/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.ql b/cpp/autosar/src/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.ql index e1a8f568e8..6d0554bf11 100644 --- a/cpp/autosar/src/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.ql +++ b/cpp/autosar/src/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.ql @@ -16,19 +16,29 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.Conversion predicate isBinaryBitwiseOperation(Operation o, VariableAccess l, VariableAccess r) { exists(BinaryBitwiseOperation bbo | bbo = o | l = bbo.getLeftOperand() and r = bbo.getRightOperand() ) or - exists(AssignBitwiseOperation abo | abo = o | l = abo.getLValue() and r = abo.getRValue()) + exists(AssignBitwiseOperation abo | abo = o | + l = abo.getLValue() and + r = abo.getRValue() + ) } -from Operation o, Variable left, Variable right +from + Operation o, VariableAccess left, VariableAccess right, Type leftUnderlyingType, + Type rightUnderlyingType where not isExcluded(o, ExpressionsPackage::bitwiseOperatorOperandsHaveDifferentUnderlyingTypeQuery()) and not o.isFromUninstantiatedTemplate(_) and - isBinaryBitwiseOperation(o, left.getAnAccess(), right.getAnAccess()) and - left.getUnderlyingType() != right.getUnderlyingType() -select o, "Operands of the '" + o.getOperator() + "' operation have different underlying types." + isBinaryBitwiseOperation(o, left, right) and + leftUnderlyingType = MisraConversion::getUnderlyingType(left) and + rightUnderlyingType = MisraConversion::getUnderlyingType(right) and + leftUnderlyingType != rightUnderlyingType +select o, + "Operands of the '" + o.getOperator() + "' operation have different underlying types '" + + leftUnderlyingType.getName() + "' and '" + rightUnderlyingType.getName() + "'." diff --git a/cpp/autosar/src/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.ql b/cpp/autosar/src/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.ql index 0819259a25..871e9828c8 100644 --- a/cpp/autosar/src/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.ql +++ b/cpp/autosar/src/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.ql @@ -18,11 +18,13 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.SideEffect import codingstandards.cpp.sideeffect.DefaultEffects +import codingstandards.cpp.Expr from BinaryLogicalOperation op, Expr rhs where not isExcluded(op, SideEffects1Package::rightHandOperandOfALogicalAndOperatorsContainSideEffectsQuery()) and rhs = op.getRightOperand() and - hasSideEffect(rhs) + hasSideEffect(rhs) and + not rhs instanceof UnevaluatedExprExtension select op, "The $@ may have a side effect that is not always evaluated.", rhs, "right-hand operand" diff --git a/cpp/autosar/src/rules/M5-18-1/CommaOperatorUsed.ql b/cpp/autosar/src/rules/M5-18-1/CommaOperatorUsed.ql index 54e81f9a04..2ce4319df1 100644 --- a/cpp/autosar/src/rules/M5-18-1/CommaOperatorUsed.ql +++ b/cpp/autosar/src/rules/M5-18-1/CommaOperatorUsed.ql @@ -18,7 +18,5 @@ import codingstandards.cpp.autosar import codingstandards.cpp.rules.commaoperatorused.CommaOperatorUsed class CommaOperatorUsedQuery extends CommaOperatorUsedSharedQuery { - CommaOperatorUsedQuery() { - this = BannedSyntaxPackage::commaOperatorUsedQuery() - } + CommaOperatorUsedQuery() { this = BannedSyntaxPackage::commaOperatorUsedQuery() } } diff --git a/cpp/autosar/src/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.ql b/cpp/autosar/src/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.ql index 2ce54f07e1..d5d5490fe9 100644 --- a/cpp/autosar/src/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.ql +++ b/cpp/autosar/src/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.ql @@ -21,12 +21,11 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import codingstandards.cpp.rules.constantunsignedintegerexpressionswraparound.ConstantUnsignedIntegerExpressionsWrapAround -from BinaryArithmeticOperation bao -where - not isExcluded(bao, ExpressionsPackage::constantUnsignedIntegerExpressionsWrapAroundQuery()) and - bao.isConstant() and - bao.getFullyConverted().getUnderlyingType().(IntegralType).isUnsigned() and - convertedExprMightOverflow(bao) -select bao, "Use of a constant, unsigned, integer expression that over- or under-flows." +class ConstantUnsignedIntegerExpressionsWrapAroundQuery extends ConstantUnsignedIntegerExpressionsWrapAroundSharedQuery +{ + ConstantUnsignedIntegerExpressionsWrapAroundQuery() { + this = ExpressionsPackage::constantUnsignedIntegerExpressionsWrapAroundQuery() + } +} diff --git a/cpp/autosar/src/rules/M5-2-10/IncrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression.ql b/cpp/autosar/src/rules/M5-2-10/IncrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression.ql index fea2a90398..ef9940ff5a 100644 --- a/cpp/autosar/src/rules/M5-2-10/IncrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression.ql +++ b/cpp/autosar/src/rules/M5-2-10/IncrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression.ql @@ -16,10 +16,12 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.Expr -from CrementOperation cop, Operation op, string name +from CrementOperation cop, ArithmeticOperation op, string name where - not isExcluded(cop) and + not isExcluded(cop, + OrderOfEvaluationPackage::incrementAndDecrementOperatorsMixedWithOtherOperatorsInExpressionQuery()) and not isExcluded(op, OrderOfEvaluationPackage::incrementAndDecrementOperatorsMixedWithOtherOperatorsInExpressionQuery()) and op.getAnOperand() = cop and diff --git a/cpp/autosar/src/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.ql b/cpp/autosar/src/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.ql index 4207b4d56c..943fc026e8 100644 --- a/cpp/autosar/src/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.ql +++ b/cpp/autosar/src/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.ql @@ -15,33 +15,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.arraypassedasfunctionargumentdecaytoapointer.ArrayPassedAsFunctionArgumentDecayToAPointer -predicate arrayToPointerDecay(Access ae, Parameter p) { - ( - p.getType() instanceof PointerType and - // exclude parameters of void* because then it assumed the caller can pass in dimensions through other means. - // examples are uses in `memset` or `memcpy` - not p.getType() instanceof VoidPointerType - or - p.getType() instanceof ArrayType - ) and - ae.getType() instanceof ArrayType and - // exclude char[] arrays because we assume that we can determine its dimension by looking for a NULL byte. - not ae.getType().(ArrayType).getBaseType() instanceof CharType +class IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointerQuery extends ArrayPassedAsFunctionArgumentDecayToAPointerSharedQuery +{ + IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointerQuery() { + this = PointersPackage::identifierWithArrayTypePassedAsFunctionArgumentDecayToAPointerQuery() + } } - -from - FunctionCall fc, Function f, Parameter decayedArray, Variable array, VariableAccess arrayAccess, - int i -where - not isExcluded(fc, - PointersPackage::identifierWithArrayTypePassedAsFunctionArgumentDecayToAPointerQuery()) and - arrayAccess = array.getAnAccess() and - f = fc.getTarget() and - arrayAccess = fc.getArgument(i) and - decayedArray = f.getParameter(i) and - arrayToPointerDecay(arrayAccess, decayedArray) and - not arrayAccess.isAffectedByMacro() -select fc.getArgument(i), - "The array $@ decays to the pointer $@ when passed as an argument to the function $@.", array, - array.getName(), decayedArray, decayedArray.getName(), f, f.getName() diff --git a/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql b/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql index d24c4d35df..086aa40ae7 100644 --- a/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql +++ b/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql @@ -15,7 +15,6 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.dataflow.DataFlow from Cast cast, VirtualBaseClass castFrom, Class castTo where diff --git a/cpp/autosar/src/rules/M5-2-6/CastNotConvertPointerToFunction.ql b/cpp/autosar/src/rules/M5-2-6/CastNotConvertPointerToFunction.ql index b6a51dc0ab..5a8df45ab1 100644 --- a/cpp/autosar/src/rules/M5-2-6/CastNotConvertPointerToFunction.ql +++ b/cpp/autosar/src/rules/M5-2-6/CastNotConvertPointerToFunction.ql @@ -15,11 +15,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.castsbetweenapointertofunctionandanyothertype.CastsBetweenAPointerToFunctionAndAnyOtherType -from Cast c -where - not isExcluded(c, PointersPackage::castNotConvertPointerToFunctionQuery()) and - not c.isImplicit() and - not c.isAffectedByMacro() and - c.getExpr().getType() instanceof FunctionPointerType -select c, "Cast converting a pointer to function." +class CastNotConvertPointerToFunctionQuery extends CastsBetweenAPointerToFunctionAndAnyOtherTypeSharedQuery +{ + CastNotConvertPointerToFunctionQuery() { + this = PointersPackage::castNotConvertPointerToFunctionQuery() + } +} diff --git a/cpp/autosar/src/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql b/cpp/autosar/src/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql index 9414e85956..e4589a364a 100644 --- a/cpp/autosar/src/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql +++ b/cpp/autosar/src/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql @@ -25,8 +25,13 @@ where ) and t = operand.getType() and not t.getUnderlyingType().getUnspecifiedType() instanceof BoolType and + // Ignore cases where the type is unknown - this will typically be in unevaluated contexts + // within uninstantiated templates. It's necessary to check for this explicitly because + // not all unevaluated contexts are considered to be `isFromUninstantiatedTemplate(_)`, + // e.g. `noexcept` specifiers + not t instanceof UnknownType and not exists(ReferenceType rt | rt = t.getUnderlyingType().getUnspecifiedType() and rt.getBaseType() instanceof BoolType ) and not operand.isFromUninstantiatedTemplate(_) -select operand, "bool operator called with a non-bool operand of type " + t.getName() + "." +select operand, "Call to bool operator with a non-bool operand of type '" + t.getName() + "'." diff --git a/cpp/autosar/src/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.ql b/cpp/autosar/src/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.ql index 0367f0aebe..7017d5e7de 100644 --- a/cpp/autosar/src/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.ql +++ b/cpp/autosar/src/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.ql @@ -14,13 +14,12 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.builtinunaryoperatorappliedtounsignedexpression.BuiltInUnaryOperatorAppliedToUnsignedExpression -from UnaryMinusExpr e, IntegralType t -where - not isExcluded(e, - OperatorsPackage::unaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsignedQuery()) and - t = e.getOperand().getExplicitlyConverted().getType().getUnderlyingType() and - t.isUnsigned() and - not e.isAffectedByMacro() -select e.getOperand(), - "The unary minus operator shall not be applied to an expression whose underlying type is unsigned." +class UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsignedQuery extends BuiltInUnaryOperatorAppliedToUnsignedExpressionSharedQuery +{ + UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsignedQuery() { + this = + OperatorsPackage::unaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsignedQuery() + } +} diff --git a/cpp/autosar/src/rules/M5-3-3/UnaryOperatorOverloaded.ql b/cpp/autosar/src/rules/M5-3-3/UnaryOperatorOverloaded.ql index c1f522de48..94f0bc6062 100644 --- a/cpp/autosar/src/rules/M5-3-3/UnaryOperatorOverloaded.ql +++ b/cpp/autosar/src/rules/M5-3-3/UnaryOperatorOverloaded.ql @@ -13,7 +13,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.addressofoperatoroverloaded.AddressOfOperatorOverloaded -from Operator o -where not isExcluded(o, OperatorsPackage::unaryOperatorOverloadedQuery()) and o.hasName("operator&") -select o, "The unary & operator overloaded." +class UnaryOperatorOverloadedQuery extends AddressOfOperatorOverloadedSharedQuery { + UnaryOperatorOverloadedQuery() { this = OperatorsPackage::unaryOperatorOverloadedQuery() } +} diff --git a/cpp/autosar/src/rules/M6-3-1/LoopCompoundCondition.ql b/cpp/autosar/src/rules/M6-3-1/LoopCompoundCondition.ql index 1c6c0b980e..b3566a1e27 100644 --- a/cpp/autosar/src/rules/M6-3-1/LoopCompoundCondition.ql +++ b/cpp/autosar/src/rules/M6-3-1/LoopCompoundCondition.ql @@ -16,9 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.loopcompoundcondition.LoopCompoundCondition -from Loop loop -where - not isExcluded(loop, ConditionalsPackage::loopCompoundConditionQuery()) and - not loop.getStmt() instanceof BlockStmt -select loop, "Loop body not enclosed within braces." +class LoopCompoundConditionQuery extends LoopCompoundConditionSharedQuery { + LoopCompoundConditionQuery() { this = ConditionalsPackage::loopCompoundConditionQuery() } +} diff --git a/cpp/autosar/src/rules/M6-3-1/SwitchCompoundCondition.ql b/cpp/autosar/src/rules/M6-3-1/SwitchCompoundCondition.ql index ee83f44ccf..f550a456dc 100644 --- a/cpp/autosar/src/rules/M6-3-1/SwitchCompoundCondition.ql +++ b/cpp/autosar/src/rules/M6-3-1/SwitchCompoundCondition.ql @@ -16,36 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.switchcompoundcondition.SwitchCompoundCondition -/** - * Class to differentiate between extractor generated blockstmt and actual blockstmt. The extractor - * will generate an artificial blockstmt when there is a single case and statement, e.g. - * ``` - * switch(x) - * case 1: - * f(); - * ``` - * This is because our AST model considers the `case` to be a statement in its own right, so the - * extractor needs an aritifical block to hold both the case and the statement. - */ -class ArtificialBlock extends BlockStmt { - ArtificialBlock() { - exists(Location block, Location firstStatement | - block = getLocation() and firstStatement = getStmt(0).getLocation() - | - // We can identify artificial blocks as those where the start of the statement is at the same - // location as the start of the first statement in the block i.e. there was no opening brace. - block.getStartLine() = firstStatement.getStartLine() and - block.getStartColumn() = firstStatement.getStartColumn() - ) - } +class SwitchCompoundConditionQuery extends SwitchCompoundConditionSharedQuery { + SwitchCompoundConditionQuery() { this = ConditionalsPackage::switchCompoundConditionQuery() } } - -from SwitchStmt switch -where - not isExcluded(switch, ConditionalsPackage::switchCompoundConditionQuery()) and - ( - switch.getStmt() instanceof ArtificialBlock or - not switch.getStmt() instanceof BlockStmt - ) -select switch, "Switch body not enclosed within braces." diff --git a/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql b/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql index 1435ed2281..e75d365461 100644 --- a/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql +++ b/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql @@ -15,10 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct -from IfStmt ifStmt, IfStmt ifElse -where - not isExcluded(ifStmt, ConditionalsPackage::ifElseTerminationConditionQuery()) and - ifStmt.getElse() = ifElse and - not ifElse.hasElse() -select ifStmt, "The $@ if statement does not terminate with an else construct.", ifElse, "if...else" +class IfElseTerminationConditionQuery extends IfElseTerminationConstructSharedQuery { + IfElseTerminationConditionQuery() { + this = ConditionalsPackage::ifElseTerminationConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql b/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql index d56bf1da0f..07953dd9f1 100644 --- a/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql +++ b/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql @@ -16,13 +16,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SwitchStatement +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition -from SwitchStmt switch, SwitchCase case -where - not isExcluded(switch, ConditionalsPackage::switchDoesNotStartWithCaseQuery()) and - case = switch.getASwitchCase() and - switchWithCaseNotFirst(switch) -select switch, - "$@ statement not well formed because the first statement in a well formed switch statement must be a case clause.", - switch, "Switch" +class SwitchDoesNotStartWithCaseQuery extends SwitchCasePositionConditionSharedQuery { + SwitchDoesNotStartWithCaseQuery() { + this = ConditionalsPackage::switchDoesNotStartWithCaseQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql b/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql index 83d4c2017f..24ac2298b5 100644 --- a/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql +++ b/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql @@ -16,13 +16,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SwitchStatement +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed -from SwitchStmt switch, SwitchCase case -where - not isExcluded(switch, ConditionalsPackage::switchStatementNotWellFormedQuery()) and - case = switch.getASwitchCase() and - switchCaseNotWellFormed(case) -select switch, - "$@ statement not well formed because this $@ block uses a statement that is not allowed.", - switch, "Switch", case, "case" +class SwitchStatementNotWellFormedQuery extends SwitchNotWellFormedSharedQuery { + SwitchStatementNotWellFormedQuery() { + this = ConditionalsPackage::switchStatementNotWellFormedQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql b/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql index 18c07b8eea..3b12bf2ef3 100644 --- a/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql +++ b/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql @@ -1,8 +1,7 @@ /** * @id cpp/autosar/nested-case-in-switch * @name M6-4-4: A switch-label shall only be used when the most closely-enclosing compound statement is the body of a switch statement - * @description By default in C++, the switch structure is weak, which may lead to switch labels - * being placed anywhere in the switch block. This can cause unspecified behaviour. + * @description Nested switch labels cause undefined behaviour. * @kind problem * @precision very-high * @problem.severity recommendation @@ -16,13 +15,8 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SwitchStatement +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch -from SwitchCase nestedCase, SwitchStmt switch -where - not isExcluded(nestedCase, ConditionalsPackage::nestedCaseInSwitchQuery()) and - switch.getASwitchCase() = nestedCase and - not nestedCase.getParentStmt() = switch.getChildStmt() -select nestedCase, - "Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement.", - switch, "switch", nestedCase, "case" +class NestedCaseInSwitchQuery extends NestedLabelInSwitchSharedQuery { + NestedCaseInSwitchQuery() { this = ConditionalsPackage::nestedCaseInSwitchQuery() } +} diff --git a/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql b/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql index 8729c948dd..8d20712021 100644 --- a/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql +++ b/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql @@ -19,7 +19,7 @@ import codingstandards.cpp.Loops from ForStmt fs, LoopControlVariable v where not isExcluded(fs, LoopsPackage::notEqualsInLoopConditionQuery()) and - isInvalidForLoopIncrementation(fs, v) + isInvalidForLoopIncrementation(fs, v, _) select fs, "For-loop counter $@ is updated by an increment larger than 1 and tested in the condition using == or !=.", v, v.getName() diff --git a/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql b/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql index 89e35990d5..bde6e8ddee 100644 --- a/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql +++ b/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql @@ -15,22 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition -from GotoStmt goto, Stmt target -where - not isExcluded(goto, ConditionalsPackage::gotoStatementJumpConditionQuery()) and - target = goto.getTarget() and - exists(Location targetLoc, Location gotoLoc | - targetLoc = target.getLocation() and - gotoLoc = goto.getLocation() and - targetLoc.getFile() = gotoLoc.getFile() - | - // Starts on a previous line - targetLoc.getStartLine() < gotoLoc.getEndLine() - or - // Starts on the same line, but an earlier column - targetLoc.getStartLine() = gotoLoc.getEndLine() and - targetLoc.getEndColumn() < gotoLoc.getStartColumn() - ) -select goto, "The goto jumps to the label $@ that is not declared later in the same function.", - target, goto.getName() +class GotoStatementJumpConditionQuery extends GotoStatementConditionSharedQuery { + GotoStatementJumpConditionQuery() { + this = ConditionalsPackage::gotoStatementJumpConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/M7-3-1/GlobalNamespaceMembershipViolation.ql b/cpp/autosar/src/rules/M7-3-1/GlobalNamespaceMembershipViolation.ql index cb714a65f2..e359880027 100644 --- a/cpp/autosar/src/rules/M7-3-1/GlobalNamespaceMembershipViolation.ql +++ b/cpp/autosar/src/rules/M7-3-1/GlobalNamespaceMembershipViolation.ql @@ -16,13 +16,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.globalnamespacedeclarations.GlobalNamespaceDeclarations -from DeclarationEntry de -where - not isExcluded(de, ScopePackage::globalNamespaceMembershipViolationQuery()) and - de.getDeclaration().getNamespace() instanceof GlobalNamespace and - de.getDeclaration().isTopLevel() and - not exists(Function f | f = de.getDeclaration() | f.hasGlobalName("main") or f.hasCLinkage()) -select de, - "Declaration " + de.getName() + - " is in the global namespace and is not a main, a namespace, or an extern \"C\" declaration." +class GlobalNamespaceMembershipViolationQuery extends GlobalNamespaceDeclarationsSharedQuery { + GlobalNamespaceMembershipViolationQuery() { + this = ScopePackage::globalNamespaceMembershipViolationQuery() + } +} diff --git a/cpp/autosar/src/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.ql b/cpp/autosar/src/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.ql index 9d86bd3637..25a01c66f8 100644 --- a/cpp/autosar/src/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.ql +++ b/cpp/autosar/src/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.ql @@ -15,11 +15,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.nonglobalfunctionmain.NonGlobalFunctionMain -from Function f -where - not isExcluded(f, - NamingPackage::identifierMainUsedForAFunctionOtherThanTheGlobalFunctionMainQuery()) and - f.hasName("main") and - not f.hasGlobalName("main") -select f, "Identifier main used for a function other than the global function main." +class IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMainQuery extends NonGlobalFunctionMainSharedQuery +{ + IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMainQuery() { + this = NamingPackage::identifierMainUsedForAFunctionOtherThanTheGlobalFunctionMainQuery() + } +} diff --git a/cpp/autosar/src/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.ql b/cpp/autosar/src/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.ql index 84b8c45a86..5a2a1e7b30 100644 --- a/cpp/autosar/src/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.ql +++ b/cpp/autosar/src/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.ql @@ -28,5 +28,13 @@ predicate isInClassScope(UsingEntry u) { exists(Class c | u.getEnclosingElement( from UsingEntry u where not isExcluded(u, BannedSyntaxPackage::usingDeclarationsUsedInHeaderFilesQuery()) and - (isInHeaderFile(u) and not isInFunctionScope(u) and not isInClassScope(u)) + isInHeaderFile(u) and + ( + u instanceof UsingDeclarationEntry + implies + ( + not isInFunctionScope(u) and + not isInClassScope(u) + ) + ) select u, "Using directive or declaration used in a header file " + u.getFile() + "." diff --git a/cpp/autosar/src/rules/M7-4-1/UsageOfAssemblerNotDocumented.ql b/cpp/autosar/src/rules/M7-4-1/UsageOfAssemblerNotDocumented.ql index 418a96ffee..f39b01b7ed 100644 --- a/cpp/autosar/src/rules/M7-4-1/UsageOfAssemblerNotDocumented.ql +++ b/cpp/autosar/src/rules/M7-4-1/UsageOfAssemblerNotDocumented.ql @@ -17,10 +17,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.usageofassemblernotdocumented.UsageOfAssemblerNotDocumented -from AsmStmt a -where - not isExcluded(a, BannedLibrariesPackage::usageOfAssemblerNotDocumentedQuery()) and - not exists(Comment c | c.getCommentedElement() = a) and - not a.isAffectedByMacro() -select a, "Use of assembler is not documented." +class UsageOfAssemblerNotDocumentedQuery extends UsageOfAssemblerNotDocumentedSharedQuery { + UsageOfAssemblerNotDocumentedQuery() { + this = BannedLibrariesPackage::usageOfAssemblerNotDocumentedQuery() + } +} diff --git a/cpp/autosar/src/rules/M7-5-1/FunctionReturnAutomaticVarCondition.ql b/cpp/autosar/src/rules/M7-5-1/FunctionReturnAutomaticVarCondition.ql index e35858f40b..cb5aa9d105 100644 --- a/cpp/autosar/src/rules/M7-5-1/FunctionReturnAutomaticVarCondition.ql +++ b/cpp/autosar/src/rules/M7-5-1/FunctionReturnAutomaticVarCondition.ql @@ -16,19 +16,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.returnreferenceorpointertoautomaticlocalvariable.ReturnReferenceOrPointerToAutomaticLocalVariable -from ReturnStmt rs, StackVariable auto, Function f, VariableAccess va, string returnType -where - f = rs.getEnclosingFunction() and - ( - f.getType() instanceof ReferenceType and va = rs.getExpr() and returnType = "reference" - or - f.getType() instanceof PointerType and - va = rs.getExpr().(AddressOfExpr).getOperand() and - returnType = "pointer" - ) and - auto = va.getTarget() and - not auto.isStatic() and - not f.isCompilerGenerated() and - not auto.getType() instanceof ReferenceType -select rs, "The $@ returns a " + returnType + "to an $@ variable", f, f.getName(), auto, "automatic" +class FunctionReturnAutomaticVarConditionQuery extends ReturnReferenceOrPointerToAutomaticLocalVariableSharedQuery +{ + FunctionReturnAutomaticVarConditionQuery() { + this = FunctionsPackage::functionReturnAutomaticVarConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/M7-5-2/AssignmentOfEscapingAutoStorage.ql b/cpp/autosar/src/rules/M7-5-2/AssignmentOfEscapingAutoStorage.ql index def439ebae..9ebdeb17c8 100644 --- a/cpp/autosar/src/rules/M7-5-2/AssignmentOfEscapingAutoStorage.ql +++ b/cpp/autosar/src/rules/M7-5-2/AssignmentOfEscapingAutoStorage.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.donotcopyaddressofautostorageobjecttootherobject.DoNotCopyAddressOfAutoStorageObjectToOtherObject -class AssignmentOfEscapingAutoStorageQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery { +class AssignmentOfEscapingAutoStorageQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery +{ AssignmentOfEscapingAutoStorageQuery() { this = FreedPackage::assignmentOfEscapingAutoStorageQuery() } diff --git a/cpp/autosar/src/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.ql b/cpp/autosar/src/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.ql index 10e365c905..c152821ab2 100644 --- a/cpp/autosar/src/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.ql +++ b/cpp/autosar/src/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.ql @@ -16,57 +16,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.multipleglobalormemberdeclarators.MultipleGlobalOrMemberDeclarators -/* - * Unfortunately, we do not have an equivalent of `DeclStmt` for non-local declarations, so we - * cannot determine whether a declaration was declared with another declaration. - * - * However, we can use location trickery to figure out if the declaration occurs close enough to - * another declaration that it _must_ have been declared within the same declaration sequence. - * - * We do this by requiring that the end location of a previous declaration is within a certain - * number of characters of the start location of the current declaration. - */ - -/** - * A `Declaration` which is not in a local scope, and is written directly by the user. - * - * These act as "candidates" for declarations that could plausibly occur in a declaration sequence - * with other candidates. - */ -class NonLocalUserDeclaration extends Declaration { - NonLocalUserDeclaration() { - not this instanceof StackVariable and - not this instanceof TemplateParameter and - not this instanceof EnumConstant and - not this instanceof TypedefType and - not any(LambdaCapture lc).getField() = this and - not this.(Function).isCompilerGenerated() and - not this.(Variable).isCompilerGenerated() and - not this.(Parameter).getFunction().isCompilerGenerated() and - not this.isInMacroExpansion() and - not exists(Struct s, TypedefType t | - s.getName() = "struct " and - t.getBaseType() = s and - this = s.getAMemberVariable() - ) +class MultipleGlobalOrMemberDeclaratorsQuery extends MultipleGlobalOrMemberDeclaratorsSharedQuery { + MultipleGlobalOrMemberDeclaratorsQuery() { + this = InitializationPackage::multipleGlobalOrMemberDeclaratorsQuery() } } - -/** - * Holds if `d1` is followed directly by `d2`. - */ -predicate isFollowingDeclaration(NonLocalUserDeclaration d1, NonLocalUserDeclaration d2) { - exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | - d1.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - d2.getLocation().hasLocationInfo(filepath, startline, endcolumn + [2 .. 3], endline, _) - ) and - not d1.(UserType).stripType() = d2.(Variable).getType().stripType() -} - -from NonLocalUserDeclaration d1 -where - not isExcluded(d1, InitializationPackage::multipleGlobalOrMemberDeclaratorsQuery()) and - isFollowingDeclaration(d1, _) and - not isFollowingDeclaration(_, d1) -select d1, "Multiple declarations after " + d1.getName() + " in this declaration sequence." diff --git a/cpp/autosar/src/rules/M8-0-1/MultipleLocalDeclarators.ql b/cpp/autosar/src/rules/M8-0-1/MultipleLocalDeclarators.ql index 3b2051c1b5..6198ab7a5a 100644 --- a/cpp/autosar/src/rules/M8-0-1/MultipleLocalDeclarators.ql +++ b/cpp/autosar/src/rules/M8-0-1/MultipleLocalDeclarators.ql @@ -16,9 +16,8 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.multiplelocaldeclarators.MultipleLocalDeclarators -from DeclStmt ds -where - not isExcluded(ds, InitializationPackage::multipleLocalDeclaratorsQuery()) and - count(ds.getADeclaration()) > 1 -select ds, "Declaration list contains more than one declaration." +class MultipleLocalDeclaratorsQuery extends MultipleLocalDeclaratorsSharedQuery { + MultipleLocalDeclaratorsQuery() { this = InitializationPackage::multipleLocalDeclaratorsQuery() } +} diff --git a/cpp/autosar/src/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.ql b/cpp/autosar/src/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.ql index 9d2b2d2006..a0ef5143e9 100644 --- a/cpp/autosar/src/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.ql +++ b/cpp/autosar/src/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.ql @@ -16,29 +16,11 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.overridingshallspecifydifferentdefaultarguments.OverridingShallSpecifyDifferentDefaultArguments -from VirtualFunction f1, VirtualFunction f2 -where - not isExcluded(f1, - VirtualFunctionsPackage::virtualFunctionParametersUseTheSameDefaultArgumentsQuery()) and - not isExcluded(f2, - VirtualFunctionsPackage::virtualFunctionParametersUseTheSameDefaultArgumentsQuery()) and - f2 = f1.getAnOverridingFunction() and - exists(Parameter p1, Parameter p2 | - p1 = f1.getAParameter() and - p2 = f2.getParameter(p1.getIndex()) - | - if p1.hasInitializer() - then - // if there is no initializer - not p2.hasInitializer() - or - // if there is one and it doesn't match - not p1.getInitializer().getExpr().getValueText() = - p2.getInitializer().getExpr().getValueText() - else - // if p1 doesn't have an initializer p2 shouldn't either - p2.hasInitializer() - ) -select f2, "$@ does not have the same default parameters as $@", f2, "overriding function", f1, - "overridden function" +class VirtualFunctionParametersUseTheSameDefaultArgumentsQuery extends OverridingShallSpecifyDifferentDefaultArgumentsSharedQuery +{ + VirtualFunctionParametersUseTheSameDefaultArgumentsQuery() { + this = VirtualFunctionsPackage::virtualFunctionParametersUseTheSameDefaultArgumentsQuery() + } +} diff --git a/cpp/autosar/src/rules/M8-5-2/UseInitBracesToMatchTypeStructure.ql b/cpp/autosar/src/rules/M8-5-2/UseInitBracesToMatchTypeStructure.ql index f9de5d549e..fae629d030 100644 --- a/cpp/autosar/src/rules/M8-5-2/UseInitBracesToMatchTypeStructure.ql +++ b/cpp/autosar/src/rules/M8-5-2/UseInitBracesToMatchTypeStructure.ql @@ -16,48 +16,11 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.enhancements.AggregateLiteralEnhancements +import codingstandards.cpp.rules.useinitializerbracestomatchaggregatetypestructure.UseInitializerBracesToMatchAggregateTypeStructure -from - InferredAggregateLiteral inferredAggregateLiteral, Type aggType, string parentDescription, - Element explanationElement, string explanationDescription -where - not isExcluded(inferredAggregateLiteral, - InitializationPackage::useInitBracesToMatchTypeStructureQuery()) and - // Not an inferred aggregate literal that acts as a "leading zero" for the root aggregate - // e.g. - // ``` - // int i[2][4] { 0 } - // ``` - // Has an inferred aggregate literal (i.e. it's `{ { 0 } }`), but we shouldn't report it - not isLeadingZeroInitialized(getRootAggregate(inferredAggregateLiteral)) and - // Provide a good message, dependending on the type of the parent - ( - // For class aggergate literal parents, report which field is being assigned to - exists(ClassAggregateLiteral cal, Field field | - cal.getFieldExpr(field) = inferredAggregateLiteral and - parentDescription = "to field $@" and - explanationElement = field - | - explanationDescription = field.getName() - ) - or - // For array aggregate literal parents, report which index is being assigned to - exists(ArrayAggregateLiteral aal, int elementIndex | - aal.getElementExpr(elementIndex) = inferredAggregateLiteral and - parentDescription = "to index " + elementIndex + " in $@" and - explanationElement = aal and - explanationDescription = "array of type " + aal.getType().getName() - ) - or - // In some cases, we seem to have missing link, so provide a basic message - not any(ArrayAggregateLiteral aal).getElementExpr(_) = inferredAggregateLiteral and - not any(ClassAggregateLiteral aal).getFieldExpr(_) = inferredAggregateLiteral and - parentDescription = "to an unnamed field of $@" and - explanationElement = inferredAggregateLiteral.getParent() and - explanationDescription = " " + explanationElement.(Expr).getType().getName() - ) -select inferredAggregateLiteral, - "Missing braces on aggregate literal of " + - getAggregateTypeDescription(inferredAggregateLiteral, aggType) + " which is assigned " + - parentDescription + ".", aggType, aggType.getName(), explanationElement, explanationDescription +class UseInitBracesToMatchTypeStructureQuery extends UseInitializerBracesToMatchAggregateTypeStructureSharedQuery +{ + UseInitBracesToMatchTypeStructureQuery() { + this = InitializationPackage::useInitBracesToMatchTypeStructureQuery() + } +} diff --git a/cpp/autosar/src/rules/M9-3-3/MemberFunctionConstIfPossible.ql b/cpp/autosar/src/rules/M9-3-3/MemberFunctionConstIfPossible.ql index 3b0ee9c058..3c8bab4d1f 100644 --- a/cpp/autosar/src/rules/M9-3-3/MemberFunctionConstIfPossible.ql +++ b/cpp/autosar/src/rules/M9-3-3/MemberFunctionConstIfPossible.ql @@ -37,6 +37,13 @@ class NonConstMemberFunction extends MemberFunction { NonConstMemberFunction() { not this.hasSpecifier("const") } } +/** + * References that are not const + */ +class NonConstReferenceType extends ReferenceType { + NonConstReferenceType() { not this.isConst() } +} + /** * `MemberFunction`s that are not const * and not `Constructor`s ect as const constructors are @@ -54,7 +61,12 @@ class ConstMemberFunctionCandidate extends NonConstMemberFunction { not this instanceof Destructor and not this instanceof Operator and //less interested in MemberFunctions with no definition - this.hasDefinition() + this.hasDefinition() and + // For uninstantiated templates we have only partial information that prevents us from determining + // if the candidate calls non-const functions. Therefore we exclude these. + not this.isFromUninstantiatedTemplate(_) and + // Cannot recommend const if it returns a non-const reference. + not this.getType() instanceof NonConstReferenceType } /** @@ -121,5 +133,6 @@ where not f.callsNonConstOwnMember() and not f.callsNonConstFromMemberVariable() and not f.isOverride() and - not f.isFinal() + not f.isFinal() and + not f.isDeleted() select f, "Member function can be declared as const." diff --git a/cpp/autosar/src/rules/M9-3-3/MemberFunctionStaticIfPossible.ql b/cpp/autosar/src/rules/M9-3-3/MemberFunctionStaticIfPossible.ql index 36c13fe5d3..69634f89c6 100644 --- a/cpp/autosar/src/rules/M9-3-3/MemberFunctionStaticIfPossible.ql +++ b/cpp/autosar/src/rules/M9-3-3/MemberFunctionStaticIfPossible.ql @@ -31,7 +31,8 @@ class NonStaticMemberFunction extends MemberFunction { not this instanceof Constructor and not this instanceof Destructor and not this instanceof Operator and - this.hasDefinition() + this.hasDefinition() and + not this.isFromUninstantiatedTemplate(_) } } @@ -39,5 +40,6 @@ from NonStaticMemberFunction nonstatic where not isExcluded(nonstatic, ConstPackage::memberFunctionStaticIfPossibleQuery()) and not exists(ThisExpr t | t.getEnclosingFunction() = nonstatic) and - not nonstatic.isVirtual() + not nonstatic.isVirtual() and + not nonstatic.isDeleted() select nonstatic, "Member function can be declared as static." diff --git a/cpp/autosar/src/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.ql b/cpp/autosar/src/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.ql index 7748f26ec1..96e434633e 100644 --- a/cpp/autosar/src/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.ql +++ b/cpp/autosar/src/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.ql @@ -22,4 +22,4 @@ where bf.getType().getUnderlyingType().(IntegralType).isSigned() and bf.getNumBits() < 2 and bf.getName() != "(unnamed bitfield)" -select bf, "A named bit-field with signed integral type should have at least 2 bits of storage " +select bf, "A named bit-field with signed integral type should have at least 2 bits of storage." diff --git a/cpp/autosar/test/codeql-pack.lock.yml b/cpp/autosar/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/autosar/test/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/cpp/autosar/test/qlpack.yml b/cpp/autosar/test/qlpack.yml index b465189500..46f06bed50 100644 --- a/cpp/autosar/test/qlpack.yml +++ b/cpp/autosar/test/qlpack.yml @@ -1,4 +1,6 @@ -name: autosar-cpp-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: autosar-cpp-coding-standards +name: codeql/autosar-cpp-coding-standards-tests +version: 2.49.0-dev extractor: cpp +license: MIT +dependencies: + codeql/autosar-cpp-coding-standards: '*' diff --git a/cpp/autosar/test/rules/A0-1-1/UselessAssignment.expected b/cpp/autosar/test/rules/A0-1-1/UselessAssignment.expected index b91ac0e0ce..a38f3afddf 100644 --- a/cpp/autosar/test/rules/A0-1-1/UselessAssignment.expected +++ b/cpp/autosar/test/rules/A0-1-1/UselessAssignment.expected @@ -1,14 +1,16 @@ -| test.cpp:22:10:22:11 | 0 | Definition of $@ is unused. | test.cpp:22:7:22:7 | y | y | -| test.cpp:25:3:25:5 | ... ++ | Definition of $@ is unused. | test.cpp:24:7:24:7 | z | z | -| test.cpp:42:3:42:7 | ... = ... | Definition of $@ is unused. | test.cpp:20:41:20:41 | p | p | -| test.cpp:44:10:44:11 | 0 | Definition of $@ is unused. | test.cpp:44:7:44:8 | l3 | l3 | -| test.cpp:45:13:45:22 | new | Definition of $@ is unused. | test.cpp:45:8:45:9 | l4 | l4 | -| test.cpp:48:8:48:8 | {...} | Definition of $@ is unused. | test.cpp:48:5:48:6 | a2 | a2 | -| test.cpp:49:11:49:15 | new | Definition of $@ is unused. | test.cpp:49:6:49:7 | a3 | a3 | -| test.cpp:50:11:50:17 | new | Definition of $@ is unused. | test.cpp:50:6:50:7 | a4 | a4 | -| test.cpp:51:11:51:17 | 0 | Definition of $@ is unused. | test.cpp:51:6:51:7 | a5 | a5 | -| test.cpp:55:3:55:10 | ... = ... | Definition of $@ is unused. | test.cpp:52:5:52:6 | a6 | a6 | -| test.cpp:60:11:60:15 | new | Definition of $@ is unused. | test.cpp:60:6:60:7 | b3 | b3 | -| test.cpp:61:11:61:17 | new | Definition of $@ is unused. | test.cpp:61:6:61:7 | b4 | b4 | -| test.cpp:62:11:62:17 | 0 | Definition of $@ is unused. | test.cpp:62:6:62:7 | b5 | b5 | -| test.cpp:70:11:70:17 | 0 | Definition of $@ is unused. | test.cpp:70:6:70:7 | c5 | c5 | +| test.cpp:55:10:55:11 | 0 | Definition of $@ is unused. | test.cpp:55:7:55:7 | y | y | +| test.cpp:58:3:58:5 | ... ++ | Definition of $@ is unused. | test.cpp:57:7:57:7 | z | z | +| test.cpp:75:3:75:7 | ... = ... | Definition of $@ is unused. | test.cpp:53:41:53:41 | p | p | +| test.cpp:77:10:77:11 | 0 | Definition of $@ is unused. | test.cpp:77:7:77:8 | l3 | l3 | +| test.cpp:78:13:78:22 | new | Definition of $@ is unused. | test.cpp:78:8:78:9 | l4 | l4 | +| test.cpp:81:8:81:8 | {...} | Definition of $@ is unused. | test.cpp:81:5:81:6 | a2 | a2 | +| test.cpp:82:11:82:15 | new | Definition of $@ is unused. | test.cpp:82:6:82:7 | a3 | a3 | +| test.cpp:83:11:83:17 | new | Definition of $@ is unused. | test.cpp:83:6:83:7 | a4 | a4 | +| test.cpp:84:11:84:17 | 0 | Definition of $@ is unused. | test.cpp:84:6:84:7 | a5 | a5 | +| test.cpp:88:3:88:10 | ... = ... | Definition of $@ is unused. | test.cpp:85:5:85:6 | a6 | a6 | +| test.cpp:93:11:93:15 | new | Definition of $@ is unused. | test.cpp:93:6:93:7 | b3 | b3 | +| test.cpp:94:11:94:17 | new | Definition of $@ is unused. | test.cpp:94:6:94:7 | b4 | b4 | +| test.cpp:95:11:95:17 | 0 | Definition of $@ is unused. | test.cpp:95:6:95:7 | b5 | b5 | +| test.cpp:103:11:103:17 | 0 | Definition of $@ is unused. | test.cpp:103:6:103:7 | c5 | c5 | +| test.cpp:132:43:132:45 | {...} | Definition of $@ is unused. | test.cpp:132:7:132:18 | unused_array | unused_array | +| test.cpp:134:29:134:31 | 0 | Definition of $@ is unused. | test.cpp:134:17:134:26 | unused_int | unused_int | diff --git a/cpp/autosar/test/rules/A0-1-1/test.cpp b/cpp/autosar/test/rules/A0-1-1/test.cpp index 966d6062bb..694396406a 100644 --- a/cpp/autosar/test/rules/A0-1-1/test.cpp +++ b/cpp/autosar/test/rules/A0-1-1/test.cpp @@ -1,9 +1,9 @@ - // A POD class struct A { int f; int f2; }; + // A non-POD class, with a trivial compiler generated constructor struct B { int f; @@ -17,6 +17,39 @@ struct C { int m; }; +void sample1(int x){}; +void sample2(int y){}; + +static void foo(B &b) noexcept { + b.g(); + B bar{}; + bar.g(); + B b2 = B(); + auto b3 = &b2; + b3->g(); + B &b4 = b; + b4.g(); + auto &b5 = *new B(); + b5.g(); + /* Below causes a compile error (non-const reference when initialized should + * hold an lvalue) + */ + // auto &b6 = new B(); + // b6.g(); +} + +template void test() { + T t; + t.g(); +} + +template void call_test() { + // call it with type parameter B to trigger indexing + test(); +} + +void call_call_test() { call_test(); } + int test_useless_assignment(int &x, int p) { x = 0; // COMPLIANT - x is a reference parameter, so is visible by the caller int y = 0; // NON_COMPLIANT - never used @@ -49,7 +82,7 @@ int test_useless_assignment(int &x, int p) { A *a3 = new A; // NON_COMPLIANT - POD class, no constructor/destructor A *a4 = new A(); // NON_COMPLIANT - POD class, no constructor/destructor A *a5 = nullptr; // NON_COMPLIANT - null never read - A a6{}; // COMPLIANT - `m` assigned below + A a6{}; // COMPLIANT - `f` assigned below a6.f = 2; // COMPLIANT - we don't track the fields here, but we do track `a6`, // so we'd consider this used by the assignment below a6.f = 1; // NON_COMPLIANT - assignment into `f`, but `a6` is not used @@ -69,5 +102,37 @@ int test_useless_assignment(int &x, int p) { C *c4 = new C(); // COMPLIANT - this will call a constructor?? C *c5 = nullptr; // NON_COMPLIANT - null never read + A a7{1, 2}; // COMPLIANT - used in the `sample1` call below + sample1(a7.f + a7.f2); // COMPLIANT - object access is a valid use + + // A *a8; // COMPLIANT - value not given at declaration + // a8 = &a7; + // sample2(a8->f); // COMPLIANT - object access is a valid use + return y; } + +int main() { return 0; } + +#include +template void test_range_based_for_loop_template() { + std::vector values_; + for (auto &elem : values_) { // COMPLIANT - should not report either elem or + // the compiler generated (__range) + // variable in the uninstantiated + // template + elem; + } +} + +#include + +std::int32_t test_constexpr_array_size() { + constexpr int constexpr_array_size = 7; // COMPLIANT + int unused_array[constexpr_array_size] = {}; // NON_COMPLIANT + + constexpr int unused_int = {}; // NON_COMPLIANT + + std::int32_t used_array[] = {-1, 0, 1}; // COMPLIANT + return used_array[1]; +} diff --git a/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected b/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected index d24d36e070..480b6d75a3 100644 --- a/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected +++ b/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected @@ -1 +1 @@ -| test.cpp:10:3:10:3 | call to f | Return value from call to $@ is unused. | test.cpp:1:5:1:5 | f | f | +| test.cpp:12:3:12:3 | call to f | Return value from call to $@ is unused. | test.cpp:3:5:3:5 | f | f | \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-2/test.cpp b/cpp/autosar/test/rules/A0-1-2/test.cpp index 9990687bf4..2be7122128 100644 --- a/cpp/autosar/test/rules/A0-1-2/test.cpp +++ b/cpp/autosar/test/rules/A0-1-2/test.cpp @@ -1,3 +1,5 @@ +#include + int f(); void g(int x); @@ -8,7 +10,8 @@ class A { void test_return_val() { f(); // NON_COMPLIANT - return value never read - static_cast(f()); // COMPLIANT + static_cast(f()); // COMPLIANT - explicitly ignoring the return value by + // static_cast to void. int x = f(); // COMPLIANT - according to the rule, even though it's not in // practice used because the unused assignment would be flagged // by A0-1-1 @@ -17,4 +20,9 @@ void test_return_val() { A a2; a1 + a2; // COMPLIANT - `+` is a call to operator+, but is permitted by the // rule -} \ No newline at end of file + + (void)f(); // COMPLIANT - explicitly ignoring the return value by C-style cast + // to void. + std::ignore = f(); // COMPLIANT - explicitly ignoring the return value by + // assigning to std::ignore. +} diff --git a/cpp/autosar/test/rules/A0-1-3/test.cpp b/cpp/autosar/test/rules/A0-1-3/test.cpp index a23fbb9545..4ec8af884a 100644 --- a/cpp/autosar/test/rules/A0-1-3/test.cpp +++ b/cpp/autosar/test/rules/A0-1-3/test.cpp @@ -85,4 +85,55 @@ namespace bar { void h3() {} // NON_COMPLIANT } // namespace bar } // namespace foo -} // namespace \ No newline at end of file +} // namespace + +static int unevaluatedContextFn(int x) { + x++; + return x; +} // COMPLIANT - called in an unevaluated context. +#include +static int unevalContextCaller() // COMPLIANT - address taken +{ + + typeid(unevaluatedContextFn(0)); + sizeof(unevaluatedContextFn(1)); + noexcept(unevaluatedContextFn(2)); + decltype(unevaluatedContextFn(2)) n = 42; + return 0; +} +int (*ptr_unevalContextCaller)(void) = unevalContextCaller; + +class X { +private: + [[maybe_unused]] void maybeUnused(); + void deleted() = delete; // COMPLIANT - Deleted Function +}; + +void X::maybeUnused() {} // COMPLIANT - [[maybe_unused]] + +static int overload1(int c) // COMPLIANT - called +{ + return ++c; +} + +static int overload1(int c, int d) // COMPLIANT - overload1(int) is called. +{ + return c + d; +} + +int overload = overload1(5); + +class classWithOverloads { +public: + int caller(int x) { return overloadMember(x, 0); } + +private: + int overloadMember(int c) // COMPLIANT - overloadMember(int, int) is called. + { + return ++c; + } + int overloadMember(int c, int d) // COMPLIANT - called. + { + return c + d; + } +}; diff --git a/cpp/autosar/test/rules/A0-1-4/UnusedParameter.expected b/cpp/autosar/test/rules/A0-1-4/UnusedParameter.expected deleted file mode 100644 index b4d1fe97fa..0000000000 --- a/cpp/autosar/test/rules/A0-1-4/UnusedParameter.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:3:22:3:22 | x | Unused parameter 'x' for function $@. | test.cpp:3:6:3:16 | test_unused | test_unused | -| test.cpp:11:14:11:14 | x | Unused parameter 'x' for function $@. | test.cpp:11:8:11:8 | b | A::b | diff --git a/cpp/autosar/test/rules/A0-1-4/UnusedParameter.qlref b/cpp/autosar/test/rules/A0-1-4/UnusedParameter.qlref deleted file mode 100644 index 9090eacb41..0000000000 --- a/cpp/autosar/test/rules/A0-1-4/UnusedParameter.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A0-1-4/UnusedParameter.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-4/UnusedParameter.testref b/cpp/autosar/test/rules/A0-1-4/UnusedParameter.testref new file mode 100644 index 0000000000..c51267a65f --- /dev/null +++ b/cpp/autosar/test/rules/A0-1-4/UnusedParameter.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unusedparameter/UnusedParameter.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-4/test.cpp b/cpp/autosar/test/rules/A0-1-4/test.cpp deleted file mode 100644 index ef50059107..0000000000 --- a/cpp/autosar/test/rules/A0-1-4/test.cpp +++ /dev/null @@ -1,16 +0,0 @@ -int test_used(int x) { return x; } // COMPLIANT - -void test_unused(int x) {} // NON_COMPLIANT - -void test_unnamed( - int) { // COMPLIANT - unnamed parameters are allowed to be unused -} - -class A { - int a(int x) { return x; } // COMPLIANT - void b(int x) {} // NON_COMPLIANT - void c(int) {} // COMPLIANT - virtual void d(int x, int y) {} // virtual, not covered by this rule -}; - -void test_no_def(int x); // COMPLIANT - no definition, so cannot be "unused" \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.expected b/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.expected deleted file mode 100644 index 4141698a95..0000000000 --- a/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.expected +++ /dev/null @@ -1,6 +0,0 @@ -| test.cpp:1:7:1:7 | A | Type declaration A is not used. | -| test.cpp:3:7:3:7 | B | Type declaration B is not used. | -| test.cpp:10:11:10:11 | D | Type declaration D is not used. | -| test.cpp:74:11:74:11 | R | Type declaration R is not used. | -| test.cpp:87:12:87:12 | struct | Type declaration struct is not used. | -| test.cpp:108:29:108:30 | AA | Type declaration AA is not used. | diff --git a/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.qlref b/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.qlref deleted file mode 100644 index 8ae6ae9563..0000000000 --- a/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A0-1-6/UnusedTypeDeclarations.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.testref b/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.testref new file mode 100644 index 0000000000..b6b5c63301 --- /dev/null +++ b/cpp/autosar/test/rules/A0-1-6/UnusedTypeDeclarations.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-6/test.cpp b/cpp/autosar/test/rules/A0-1-6/test.cpp deleted file mode 100644 index c14c0359d8..0000000000 --- a/cpp/autosar/test/rules/A0-1-6/test.cpp +++ /dev/null @@ -1,110 +0,0 @@ -class A {}; // NON_COMPLIANT - unused - -class B { // NON_COMPLIANT - only used within itself -public: - B f(); - void g(B b); -}; - -class C {}; // COMPLIANT - used in the type def -typedef C D; // NON_COMPLIANT - typedef itself not used - -template // `T` is a `TemplateParameter` and shouldn't be flagged by - // the query -class E { // COMPLIANT - used in test_template_class below -private: - T m_t; - -public: - E(T t) : m_t(t) {} -}; - -void test_template_class() { E e(0); } - -class F {}; // COMPLIANT - used as a global function return type - -F test_return_value() { - F f; - return f; -} - -class G {}; // COMPLIANT - used as a global function parameter type - -void test_return_value(G g) {} - -class H {}; // COMPLIANT, used with template function - -template T get() { - T t; - return t; -} - -void test_template_function() { get(); } - -template class I : T::Base {}; - -class J { -public: - class Base {}; // COMPLIANT - I has J::Base as a base class -}; - -void test_template_base() { I i{}; } - -template class K { // COMPLIANT - used in function below, statically -public: - static T f(); -}; - -class L {}; // COMPLIANT - used in function below as type argument - -void test_static_call() { L l = K::f(); } - -enum M { C1, C2, C3 }; // COMPLIANT - used in an enum type access below -enum class N { C4, C5, C6 }; // COMPLIANT - used in an enum type access below - -void test_enum_access() { - int i = C1; - N::C4; -} - -class O {}; // COMPLIANT - used in typedef below - -typedef O P; // COMPLIANT - used in typedef below -typedef P Q; // COMPLIANT - used in function below -typedef Q R; // NON_COMPLIANT - never used - -Q test_type_def() {} - -struct { // COMPLIANT - used in type definition - union { // COMPLIANT - f1 and f3 is accessed - struct { // COMPLIANT - f1 is accessed - int f1; - }; - struct { // COMPLIANT - f3 is accessed - float f2; - float f3; - }; - struct { // NON_COMPLIANT - f4 is never accessed - long f4; - }; - }; - int f5; -} s; - -void test_nested_struct() { - s.f1; - s.f3; - s.f5; -} - -template class X { // COMPLIANT - template class never instantiated - using custom_type = E; // COMPLIANT - template class never instantiated -}; - -template class Y {}; // COMPLIANT - used in the test case below - -// Alias templates -template using Z = Y; // COMPLIANT - used below -template using AA = Y; // NON_COMPLIANT - never instantiated - -void test_alias_template() { Z v; } diff --git a/cpp/autosar/test/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.expected.clang b/cpp/autosar/test/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.expected.gcc b/cpp/autosar/test/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.expected.qcc b/cpp/autosar/test/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.expected.qcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A0-4-1/test.cpp.clang b/cpp/autosar/test/rules/A0-4-1/test.cpp.clang new file mode 100644 index 0000000000..9dea3a0743 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-1/test.cpp.clang @@ -0,0 +1,8 @@ +#include + +void test() { + float f; // COMPLIANT + + double d; // COMPLIANT + long double ld; // COMPLIANT +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-1/test.cpp.gcc b/cpp/autosar/test/rules/A0-4-1/test.cpp.gcc new file mode 100644 index 0000000000..9dea3a0743 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-1/test.cpp.gcc @@ -0,0 +1,8 @@ +#include + +void test() { + float f; // COMPLIANT + + double d; // COMPLIANT + long double ld; // COMPLIANT +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-1/test.cpp.qcc b/cpp/autosar/test/rules/A0-4-1/test.cpp.qcc new file mode 100644 index 0000000000..9dea3a0743 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-1/test.cpp.qcc @@ -0,0 +1,8 @@ +#include + +void test() { + float f; // COMPLIANT + + double d; // COMPLIANT + long double ld; // COMPLIANT +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-3/options.clang b/cpp/autosar/test/rules/A0-4-3/options.clang new file mode 100644 index 0000000000..0fd3902c30 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-3/options.clang @@ -0,0 +1 @@ +-std=gnu++14 \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-3/options.gcc b/cpp/autosar/test/rules/A0-4-3/options.gcc new file mode 100644 index 0000000000..0fd3902c30 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-3/options.gcc @@ -0,0 +1 @@ +-std=gnu++14 \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-3/options.qcc b/cpp/autosar/test/rules/A0-4-3/options.qcc new file mode 100644 index 0000000000..0fd3902c30 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-3/options.qcc @@ -0,0 +1 @@ +-std=gnu++14 \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.expected b/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.expected deleted file mode 100644 index 3e99c4cd56..0000000000 --- a/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.expected +++ /dev/null @@ -1,18 +0,0 @@ -| test.cpp:4:3:4:6 | call to acos | Domain error in call to acos: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | -| test.cpp:8:3:8:6 | call to acos | Domain error in call to acos: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | -| test.cpp:9:3:9:6 | call to asin | Domain error in call to asin: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | -| test.cpp:13:3:13:6 | call to asin | Domain error in call to asin: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | -| test.cpp:14:3:14:7 | call to atanh | Domain error in call to atanh: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | -| test.cpp:18:3:18:7 | call to atanh | Domain error in call to atanh: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | -| test.cpp:19:3:19:7 | call to atan2 | Domain error in call to atan2: both arguments are equal to zero. | -| test.cpp:23:3:23:5 | call to pow | Domain error in call to pow: both arguments are equal to zero. | -| test.cpp:27:3:27:5 | call to pow | Domain error in call to pow: both arguments are less than zero. | -| test.cpp:33:3:33:7 | call to acosh | Domain error in call to acosh: argument is less than 1. | -| test.cpp:34:3:34:7 | call to ilogb | Domain error in call to ilogb: argument is equal to zero. | -| test.cpp:37:3:37:5 | call to log | Domain error in call to log: argument is negative. | -| test.cpp:40:3:40:7 | call to log10 | Domain error in call to log10: argument is negative. | -| test.cpp:43:3:43:6 | call to log2 | Domain error in call to log2: argument is negative. | -| test.cpp:46:3:46:6 | call to sqrt | Domain error in call to sqrt: argument is negative. | -| test.cpp:49:3:49:7 | call to log1p | Domain error in call to log1p: argument is less than 1. | -| test.cpp:52:3:52:6 | call to logb | Domain error in call to logb: argument is equal to zero. | -| test.cpp:55:3:55:8 | call to tgamma | Domain error in call to tgamma: argument is equal to zero. | diff --git a/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.qlref b/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.qlref deleted file mode 100644 index 0c39ff65ab..0000000000 --- a/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A0-4-4/UncheckedRangeDomainPoleErrors.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.testref b/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.testref new file mode 100644 index 0000000000..060c94cfa1 --- /dev/null +++ b/cpp/autosar/test/rules/A0-4-4/UncheckedRangeDomainPoleErrors.testref @@ -0,0 +1 @@ +cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-4-4/test.cpp b/cpp/autosar/test/rules/A0-4-4/test.cpp deleted file mode 100644 index 63f53b8a41..0000000000 --- a/cpp/autosar/test/rules/A0-4-4/test.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "math.h" - -void test() { - acos(-1.1f); // NON_COMPLIANT - acos(-1.0f); // COMPLIANT - acos(0.0f); // COMPLIANT - acos(1.0f); // COMPLIANT - acos(1.1f); // NON_COMPLIANT - asin(-1.1f); // NON_COMPLIANT - asin(-1.0f); // COMPLIANT - asin(0.0f); // COMPLIANT - asin(1.0f); // COMPLIANT - asin(1.1f); // NON_COMPLIANT - atanh(-1.1f); // NON_COMPLIANT - atanh(-1.0f); // COMPLIANT - atanh(0.0f); // COMPLIANT - atanh(1.0f); // COMPLIANT - atanh(1.1f); // NON_COMPLIANT - atan2(0.0f, 0.0f); // NON_COMPLIANT - atan2(1.0f, 0.0f); // COMPLIANT - atan2(0.0f, 1.0f); // COMPLIANT - atan2(1.0f, 1.0f); // COMPLIANT - pow(0.0f, 0.0f); // NON_COMPLIANT - pow(1.0f, 0.0f); // COMPLIANT - pow(0.0f, 1.0f); // COMPLIANT - pow(1.0f, 1.0f); // COMPLIANT - pow(-1.0f, -1.0f); // NON_COMPLIANT - pow(-1.0f, 0.0f); // COMPLIANT - pow(0.0f, -1.0f); // COMPLIANT - pow(1.0f, -1.0f); // COMPLIANT - pow(-1.0f, 1.0f); // COMPLIANT - acosh(1.0f); // COMPLIANT - acosh(0.9f); // NON_COMPLIANT - ilogb(0.0f); // NON_COMPLIANT - ilogb(1.0f); // COMPLIANT - ilogb(-1.0f); // COMPLIANT - log(-1.0f); // NON_COMPLIANT - log(0.0f); // COMPLIANT - log(1.0f); // COMPLIANT - log10(-1.0f); // NON_COMPLIANT - log10(0.0f); // COMPLIANT - log10(1.0f); // COMPLIANT - log2(-1.0f); // NON_COMPLIANT - log2(0.0f); // COMPLIANT - log2(1.0f); // COMPLIANT - sqrt(-1.0f); // NON_COMPLIANT - sqrt(0.0f); // COMPLIANT - sqrt(1.0f); // COMPLIANT - log1p(-2.0f); // NON_COMPLIANT - log1p(-1.0f); // COMPLIANT - log1p(0.0f); // COMPLIANT - logb(0.0f); // NON_COMPLIANT - logb(1.0f); // COMPLIANT - logb(-1.0f); // COMPLIANT - tgamma(0.0f); // NON_COMPLIANT - tgamma(1.0f); // COMPLIANT - tgamma(-1.1f); // COMPLIANT -} diff --git a/cpp/autosar/test/rules/A1-1-1/CStandardLibraryHeadersAreDeprecated.cpp b/cpp/autosar/test/rules/A1-1-1/CStandardLibraryHeadersAreDeprecated.cpp index b984336e54..752508e334 100644 --- a/cpp/autosar/test/rules/A1-1-1/CStandardLibraryHeadersAreDeprecated.cpp +++ b/cpp/autosar/test/rules/A1-1-1/CStandardLibraryHeadersAreDeprecated.cpp @@ -1,5 +1,5 @@ #include // NON_COMPLIANT -#include // NON_COMPLAINT +#include // NON_COMPLIANT #include // NON_COMPLIANT #include // NON_COMPLIANT #include // NON_COMPLIANT @@ -24,5 +24,5 @@ #include // NON_COMPLIANT #include // NON_COMPLIANT -#include // COMPLIANT -#include // COMPLIANT \ No newline at end of file +#include // COMPLIANT +#include // COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-1/StrstreamTypesAreDeprecated.expected b/cpp/autosar/test/rules/A1-1-1/StrstreamTypesAreDeprecated.expected index d78de102ed..a8faadd3e8 100644 --- a/cpp/autosar/test/rules/A1-1-1/StrstreamTypesAreDeprecated.expected +++ b/cpp/autosar/test/rules/A1-1-1/StrstreamTypesAreDeprecated.expected @@ -1,14 +1,11 @@ -| StrstreamTypesAreDeprecated.cpp:4:21:4:21 | x | Use of class 'std::strstreambuf' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:7:20:7:21 | f1 | Use of class 'std::strstreambuf' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:12:19:12:20 | i1 | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:12:22:12:27 | call to istrstream | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:14:19:14:20 | i2 | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:14:22:14:23 | call to istrstream | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:16:19:16:20 | i3 | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:16:22:16:37 | call to istrstream | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:19:18:19:19 | f2 | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:20:10:20:31 | call to istrstream | Use of class 'std::istrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:24:19:24:19 | call to ostrstream | Use of class 'std::ostrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:24:19:24:19 | o | Use of class 'std::ostrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:27:18:27:19 | f3 | Use of class 'std::ostrstream' is deprecated. | -| StrstreamTypesAreDeprecated.cpp:28:10:28:30 | call to ostrstream | Use of class 'std::ostrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:4:8:4:19 | type mention | Use of class 'strstreambuf' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:7:6:7:17 | type mention | Use of class 'strstreambuf' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:8:19:8:30 | type mention | Use of class 'strstreambuf' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:12:8:12:17 | type mention | Use of class 'istrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:14:8:14:17 | type mention | Use of class 'istrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:16:8:16:17 | type mention | Use of class 'istrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:19:6:19:15 | type mention | Use of class 'istrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:20:19:20:28 | type mention | Use of class 'istrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:24:8:24:17 | type mention | Use of class 'ostrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:27:6:27:15 | type mention | Use of class 'ostrstream' is deprecated. | +| StrstreamTypesAreDeprecated.cpp:28:19:28:28 | type mention | Use of class 'ostrstream' is deprecated. | diff --git a/cpp/autosar/test/rules/A1-1-2.1/CompilerWarningLevelNotInCompliance.expected b/cpp/autosar/test/rules/A1-1-2.1/CompilerWarningLevelNotInCompliance.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2.1/CompilerWarningLevelNotInCompliance.qlref b/cpp/autosar/test/rules/A1-1-2.1/CompilerWarningLevelNotInCompliance.qlref new file mode 100644 index 0000000000..30fb98b639 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.1/CompilerWarningLevelNotInCompliance.qlref @@ -0,0 +1 @@ +rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/foo b/cpp/autosar/test/rules/A1-1-2.1/foo similarity index 100% rename from cpp/autosar/test/rules/A1-1-2/foo rename to cpp/autosar/test/rules/A1-1-2.1/foo diff --git a/cpp/autosar/test/rules/A1-1-2.1/options.clang b/cpp/autosar/test/rules/A1-1-2.1/options.clang new file mode 100644 index 0000000000..d88b8ee03d --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.1/options.clang @@ -0,0 +1 @@ +@foo \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.1/options.gcc b/cpp/autosar/test/rules/A1-1-2.1/options.gcc new file mode 100644 index 0000000000..d88b8ee03d --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.1/options.gcc @@ -0,0 +1 @@ +@foo \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.1/options.qcc b/cpp/autosar/test/rules/A1-1-2.1/options.qcc new file mode 100644 index 0000000000..d88b8ee03d --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.1/options.qcc @@ -0,0 +1 @@ +@foo \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/responsefile.cpp b/cpp/autosar/test/rules/A1-1-2.1/responsefile.cpp similarity index 100% rename from cpp/autosar/test/rules/A1-1-2/responsefile.cpp rename to cpp/autosar/test/rules/A1-1-2.1/responsefile.cpp diff --git a/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected new file mode 100644 index 0000000000..81a5c4327e --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected @@ -0,0 +1 @@ +| Wcast-function-type.cpp:0:0:0:0 | Wcast-function-type.cpp | No warning-level options were used in the compilation of 'Wcast-function-type.cpp'. | diff --git a/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected.clang b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected.gcc b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected.qcc b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.expected.qcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.qlref b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.qlref new file mode 100644 index 0000000000..30fb98b639 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.2/CompilerWarningLevelNotInCompliance.qlref @@ -0,0 +1 @@ +rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.2/Wcast-function-type.cpp b/cpp/autosar/test/rules/A1-1-2.2/Wcast-function-type.cpp new file mode 100644 index 0000000000..bc48268931 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.2/Wcast-function-type.cpp @@ -0,0 +1,14 @@ +// semmle-extractor-options: --clang -std=c++14 -Wcast-function-type +// COMPLIANT + +// NOTE: When tested with `codeql test run`, the test extractor provides `-w` +// which overrides `-Wcast-function-type` and causes this test case to be +// non-compliant. +// +// However, when tested with our compiler matrix tests, this test db is built +// via `codeql database create --command="..."`, and the `-w` flag will NOT be +// used. This means the `-Wcast-function-type` flag is active and the test case +// is compliant. +// +// Therefore, the .expected file for this test expects non-compliance, and the +// .expected.gcc and .expected.clang files expect this test to be compliant. diff --git a/cpp/autosar/test/rules/A1-1-2.2/options.clang b/cpp/autosar/test/rules/A1-1-2.2/options.clang new file mode 100644 index 0000000000..7c5542f288 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.2/options.clang @@ -0,0 +1 @@ +-Wcast-function-type \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.2/options.gcc b/cpp/autosar/test/rules/A1-1-2.2/options.gcc new file mode 100644 index 0000000000..7c5542f288 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.2/options.gcc @@ -0,0 +1 @@ +-Wcast-function-type \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.2/options.qcc b/cpp/autosar/test/rules/A1-1-2.2/options.qcc new file mode 100644 index 0000000000..7c5542f288 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.2/options.qcc @@ -0,0 +1 @@ +-Wcast-function-type \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.3/CompilerWarningLevelNotInCompliance.expected b/cpp/autosar/test/rules/A1-1-2.3/CompilerWarningLevelNotInCompliance.expected new file mode 100644 index 0000000000..cf114e0569 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.3/CompilerWarningLevelNotInCompliance.expected @@ -0,0 +1 @@ +| test.cpp:0:0:0:0 | test.cpp | No warning-level options were used in the compilation of 'test.cpp'. | diff --git a/cpp/autosar/test/rules/A1-1-2.3/CompilerWarningLevelNotInCompliance.qlref b/cpp/autosar/test/rules/A1-1-2.3/CompilerWarningLevelNotInCompliance.qlref new file mode 100644 index 0000000000..30fb98b639 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.3/CompilerWarningLevelNotInCompliance.qlref @@ -0,0 +1 @@ +rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.3/options.clang b/cpp/autosar/test/rules/A1-1-2.3/options.clang new file mode 100644 index 0000000000..b45da65784 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.3/options.clang @@ -0,0 +1 @@ +-w \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.3/options.gcc b/cpp/autosar/test/rules/A1-1-2.3/options.gcc new file mode 100644 index 0000000000..b45da65784 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.3/options.gcc @@ -0,0 +1 @@ +-w \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.3/options.qcc b/cpp/autosar/test/rules/A1-1-2.3/options.qcc new file mode 100644 index 0000000000..b45da65784 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.3/options.qcc @@ -0,0 +1 @@ +-w \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/test.cpp b/cpp/autosar/test/rules/A1-1-2.3/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A1-1-2/test.cpp rename to cpp/autosar/test/rules/A1-1-2.3/test.cpp diff --git a/cpp/autosar/test/rules/A1-1-2.4/CompilerWarningLevelNotInCompliance.expected b/cpp/autosar/test/rules/A1-1-2.4/CompilerWarningLevelNotInCompliance.expected new file mode 100644 index 0000000000..dd7f320be2 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.4/CompilerWarningLevelNotInCompliance.expected @@ -0,0 +1 @@ +| Wformat=0-Wno-format-security.cpp:0:0:0:0 | Wformat=0-Wno-format-security.cpp | No warning-level options were used in the compilation of 'Wformat=0-Wno-format-security.cpp'. | diff --git a/cpp/autosar/test/rules/A1-1-2.4/CompilerWarningLevelNotInCompliance.qlref b/cpp/autosar/test/rules/A1-1-2.4/CompilerWarningLevelNotInCompliance.qlref new file mode 100644 index 0000000000..30fb98b639 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.4/CompilerWarningLevelNotInCompliance.qlref @@ -0,0 +1 @@ +rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.4/Wformat=0-Wno-format-security.cpp b/cpp/autosar/test/rules/A1-1-2.4/Wformat=0-Wno-format-security.cpp new file mode 100644 index 0000000000..29523ad24e --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.4/Wformat=0-Wno-format-security.cpp @@ -0,0 +1,2 @@ +// semmle-extractor-options: --clang -std=c++14 -Wformat=0 -Wno-format-security +// NON_COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.4/options.clang b/cpp/autosar/test/rules/A1-1-2.4/options.clang new file mode 100644 index 0000000000..4544f91ecb --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.4/options.clang @@ -0,0 +1 @@ +-Wformat=0 -Wno-format-security \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.4/options.gcc b/cpp/autosar/test/rules/A1-1-2.4/options.gcc new file mode 100644 index 0000000000..4544f91ecb --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.4/options.gcc @@ -0,0 +1 @@ +-Wformat=0 -Wno-format-security \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.4/options.qcc b/cpp/autosar/test/rules/A1-1-2.4/options.qcc new file mode 100644 index 0000000000..e28a2c3ac5 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.4/options.qcc @@ -0,0 +1 @@ +-Wno-format -Wno-format-security \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected new file mode 100644 index 0000000000..df69d21d5a --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected @@ -0,0 +1 @@ +| Wall-Wno-format.cpp:0:0:0:0 | Wall-Wno-format.cpp | No warning-level options were used in the compilation of 'Wall-Wno-format.cpp'. | \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.clang b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.gcc b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.qcc b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.qcc new file mode 100644 index 0000000000..c6354c2475 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.expected.qcc @@ -0,0 +1 @@ +| Wall-Wno-format.cpp:0:0:0:0 | Wall-Wno-format.cpp | No warning-level options were used in the compilation of 'Wall-Wno-format.cpp'. | diff --git a/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.qlref b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.qlref new file mode 100644 index 0000000000..30fb98b639 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/CompilerWarningLevelNotInCompliance.qlref @@ -0,0 +1 @@ +rules/A1-1-2/CompilerWarningLevelNotInCompliance.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.5/Wall-Wno-format.cpp b/cpp/autosar/test/rules/A1-1-2.5/Wall-Wno-format.cpp new file mode 100644 index 0000000000..93c4b98248 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/Wall-Wno-format.cpp @@ -0,0 +1,14 @@ +// semmle-extractor-options: --clang -std=c++14 -Wall -Wno-format +// COMPLIANT + +// NOTE: When tested with `codeql test run`, the test extractor provides `-w` +// which overrides `-Wcast-function-type` and causes this test case to be +// non-compliant. +// +// However, when tested with our compiler matrix tests, this test db is built +// via `codeql database create --command="..."`, and the `-w` flag will NOT be +// used. This means the `-Wcast-function-type` flag is active and the test case +// is compliant. +// +// Therefore, the .expected file for this test expects non-compliance, and the +// .expected.gcc and .expected.clang files expect this test to be compliant. diff --git a/cpp/autosar/test/rules/A1-1-2.5/options.clang b/cpp/autosar/test/rules/A1-1-2.5/options.clang new file mode 100644 index 0000000000..735817b680 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/options.clang @@ -0,0 +1 @@ +-Wall -Wno-format \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.5/options.gcc b/cpp/autosar/test/rules/A1-1-2.5/options.gcc new file mode 100644 index 0000000000..735817b680 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/options.gcc @@ -0,0 +1 @@ +-Wall -Wno-format \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2.5/options.qcc b/cpp/autosar/test/rules/A1-1-2.5/options.qcc new file mode 100644 index 0000000000..735817b680 --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2.5/options.qcc @@ -0,0 +1 @@ +-Wall -Wno-format \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected b/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected index cf114e0569..ddc4e03f62 100644 --- a/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected +++ b/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected @@ -1 +1 @@ -| test.cpp:0:0:0:0 | test.cpp | No warning-level options were used in the compilation of 'test.cpp'. | +| Wall.cpp:0:0:0:0 | Wall.cpp | No warning-level options were used in the compilation of 'Wall.cpp'. | diff --git a/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected.clang b/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected.gcc b/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected.qcc b/cpp/autosar/test/rules/A1-1-2/CompilerWarningLevelNotInCompliance.expected.qcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A1-1-2/Wall.cpp b/cpp/autosar/test/rules/A1-1-2/Wall.cpp index cb21e0601e..b42189a8d1 100644 --- a/cpp/autosar/test/rules/A1-1-2/Wall.cpp +++ b/cpp/autosar/test/rules/A1-1-2/Wall.cpp @@ -1,2 +1,12 @@ // semmle-extractor-options: --clang -std=c++14 -Wall -// COMPLIANT \ No newline at end of file +// COMPLIANT + +// NOTE: When tested with `codeql test run`, the test extractor provides `-w` +// which overrides `-Wall` and causes this test case to be non-compliant. +// +// However, when tested with our compiler matrix tests, this test db is built +// via `codeql database create --command="..."`, and the `-w` flag will NOT be +// used. This means the `-Wall` flag is active and the test case is compliant. +// +// Therefore, the .expected file for this test expects non-compliance, and the +// .expected.gcc and .expected.clang files expect this test to be compliant. \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/Wcast-function-type.cpp b/cpp/autosar/test/rules/A1-1-2/Wcast-function-type.cpp deleted file mode 100644 index f405349bbb..0000000000 --- a/cpp/autosar/test/rules/A1-1-2/Wcast-function-type.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// semmle-extractor-options: --clang -std=c++14 -Wcast-function-type -// COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/Wextra.cpp b/cpp/autosar/test/rules/A1-1-2/Wextra.cpp deleted file mode 100644 index fed8af5f45..0000000000 --- a/cpp/autosar/test/rules/A1-1-2/Wextra.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// semmle-extractor-options: --clang -std=c++14 -Wextra -// COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/options.clang b/cpp/autosar/test/rules/A1-1-2/options.clang new file mode 100644 index 0000000000..16bcca5afe --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2/options.clang @@ -0,0 +1 @@ +-Wall \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/options.gcc b/cpp/autosar/test/rules/A1-1-2/options.gcc new file mode 100644 index 0000000000..16bcca5afe --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2/options.gcc @@ -0,0 +1 @@ +-Wall \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-2/options.qcc b/cpp/autosar/test/rules/A1-1-2/options.qcc new file mode 100644 index 0000000000..16bcca5afe --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-2/options.qcc @@ -0,0 +1 @@ +-Wall \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected index f07f2d298c..0105f717e6 100644 --- a/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected +++ b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected @@ -3,7 +3,7 @@ | test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffinite-math-only'. | | test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffloat-store'. | | test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fgnu-keywords'. | -| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fno-signed-zeroes'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fno-signed-zeros'. | | test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-menable-no-infs'. | | test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-menable-no-nans'. | | test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-menable-unsafe-fp-math'. | diff --git a/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.clang b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.clang new file mode 100644 index 0000000000..c3fea7cbba --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.clang @@ -0,0 +1,6 @@ +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-Ofast'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffast-math'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffinite-math-only'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffloat-store'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fgnu-keywords'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fno-signed-zeros'. | diff --git a/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.gcc b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.gcc new file mode 100644 index 0000000000..c3fea7cbba --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.gcc @@ -0,0 +1,6 @@ +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-Ofast'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffast-math'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffinite-math-only'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffloat-store'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fgnu-keywords'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fno-signed-zeros'. | diff --git a/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.qcc b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.qcc new file mode 100644 index 0000000000..c3fea7cbba --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-3/UncompliantOptimizationOptionMustBeDisabledInCompiler.expected.qcc @@ -0,0 +1,6 @@ +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-Ofast'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffast-math'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffinite-math-only'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-ffloat-store'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fgnu-keywords'. | +| test.cpp:0:0:0:0 | test.cpp | File compiled with uncompliant optimization flag '-fno-signed-zeros'. | diff --git a/cpp/autosar/test/rules/A1-1-3/options.clang b/cpp/autosar/test/rules/A1-1-3/options.clang new file mode 100644 index 0000000000..6ca75105ce --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-3/options.clang @@ -0,0 +1 @@ +-Ofast -ffast-math -fgnu-keywords -fno-signed-zeros -ffinite-math-only -ffloat-store \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-3/options.gcc b/cpp/autosar/test/rules/A1-1-3/options.gcc new file mode 100644 index 0000000000..6ca75105ce --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-3/options.gcc @@ -0,0 +1 @@ +-Ofast -ffast-math -fgnu-keywords -fno-signed-zeros -ffinite-math-only -ffloat-store \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-3/options.qcc b/cpp/autosar/test/rules/A1-1-3/options.qcc new file mode 100644 index 0000000000..6ca75105ce --- /dev/null +++ b/cpp/autosar/test/rules/A1-1-3/options.qcc @@ -0,0 +1 @@ +-Ofast -ffast-math -fgnu-keywords -fno-signed-zeros -ffinite-math-only -ffloat-store \ No newline at end of file diff --git a/cpp/autosar/test/rules/A1-1-3/test.cpp b/cpp/autosar/test/rules/A1-1-3/test.cpp index 1b30f476e9..eff0b1272f 100644 --- a/cpp/autosar/test/rules/A1-1-3/test.cpp +++ b/cpp/autosar/test/rules/A1-1-3/test.cpp @@ -1,3 +1,3 @@ // clang-format off -// semmle-extractor-options:-Ofast -ffast-math -fgnu-keywords -fno-signed-zeroes -menable-unsafe-fp-math -menable-no-nans -menable-no-infs -menable-unsafe-fp-math -ffinite-math-only -ffloat-store +// semmle-extractor-options:-Ofast -ffast-math -fgnu-keywords -fno-signed-zeros -menable-unsafe-fp-math -menable-no-nans -menable-no-infs -menable-unsafe-fp-math -ffinite-math-only -ffloat-store // NON_COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-0-1/MissingSpecialMemberFunction.expected b/cpp/autosar/test/rules/A12-0-1/MissingSpecialMemberFunction.expected index 9e1cd591c6..ced97cced2 100644 --- a/cpp/autosar/test/rules/A12-0-1/MissingSpecialMemberFunction.expected +++ b/cpp/autosar/test/rules/A12-0-1/MissingSpecialMemberFunction.expected @@ -1,2 +1,8 @@ | test.cpp:12:7:12:8 | C3 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:12:7:12:8 | C3 | C3 | | test.cpp:28:7:28:8 | C5 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:28:7:28:8 | C5 | C5 | +| test.cpp:51:7:51:9 | C10 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:51:7:51:9 | C10 | C10 | +| test.cpp:55:7:55:9 | C11 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:55:7:55:9 | C11 | C11 | +| test.cpp:59:7:59:9 | C12 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:59:7:59:9 | C12 | C12 | +| test.cpp:63:7:63:9 | C13 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:63:7:63:9 | C13 | C13 | +| test.cpp:67:7:67:9 | C14 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:67:7:67:9 | C14 | C14 | +| test.cpp:71:7:71:9 | C15 | Class $@ has provided at least one user-defined special member function but is missing definitions for all five special member functions. | test.cpp:71:7:71:9 | C15 | C15 | diff --git a/cpp/autosar/test/rules/A12-0-1/test.cpp b/cpp/autosar/test/rules/A12-0-1/test.cpp index 71652633b4..9a38204641 100644 --- a/cpp/autosar/test/rules/A12-0-1/test.cpp +++ b/cpp/autosar/test/rules/A12-0-1/test.cpp @@ -46,4 +46,63 @@ struct C7::C8 { // COMPLIANT struct C9 { // COMPLIANT C9() {} C9(int x) {} +}; + +class C10 { + ~C10() = default; // NON_COMPLIANT +}; + +class C11 { + ~C11() = delete; // NON_COMPLIANT +}; + +class C12 { + C12(C12 const &); // NON_COMPLIANT +}; + +class C13 { + C13(C13 const &) = default; // NON_COMPLIANT +}; + +class C14 { + C14(C14 const &) = delete; // NON_COMPLIANT +}; + +class C15 { + C15 &operator=(C15 const &); // NON_COMPLIANT +}; + +template class C16 { // COMPLIANT + C16() = default; +}; + +template class C17 { // COMPLIANT + C17() = default; + C17(C17 const &) = default; + C17(C17 &&) = default; + virtual ~C17() = default; + C17 &operator=(C17 const &) = default; + C17 &operator=(C17 &&) = default; +}; + +template class C18 { // COMPLIANT + C18() = default; + C18(C18 const &) = delete; + C18(C18 &&) = delete; + virtual ~C18() = default; + C18 &operator=(C18 const &) = delete; + C18 &operator=(C18 &&) = delete; +}; + +template class C19 { // COMPLIANT +public: + explicit C19(T i) : i(i) {} + C19(C19 const &) = delete; + C19(C19 &&) = delete; + virtual ~C19() = default; + C19 &operator=(C19 const &) = delete; + C19 &operator=(C19 &&) = delete; + +private: + T i; }; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-0-2/test.cpp b/cpp/autosar/test/rules/A12-0-2/test.cpp index 50dfe4a101..6e640e62d4 100644 --- a/cpp/autosar/test/rules/A12-0-2/test.cpp +++ b/cpp/autosar/test/rules/A12-0-2/test.cpp @@ -1,5 +1,5 @@ #include -#include +#include class A { public: diff --git a/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.qlref b/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.qlref deleted file mode 100644 index 9d356add77..0000000000 --- a/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A12-1-1/ExplicitConstructorBaseClassInitialization.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.testref b/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.testref new file mode 100644 index 0000000000..ac8c5e1a83 --- /dev/null +++ b/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.testref @@ -0,0 +1 @@ +cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-1-1/test.cpp b/cpp/autosar/test/rules/A12-1-1/test.cpp deleted file mode 100644 index 7721da8b01..0000000000 --- a/cpp/autosar/test/rules/A12-1-1/test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -class Base {}; -class Derived : public Base { -public: - Derived() {} // NON_COMPLIANT - does not call Base() - Derived(int i) : Base() {} // COMPLIANT - Derived(int i, int j); // IGNORED - not defined, so we don't know -}; - -class Derived1 : public Base { -public: - Derived1() = default; // COMPLIANT - `default` does not have explicit - // initializers -}; - -class VirtualBase {}; - -class Derived2 : virtual public VirtualBase {}; - -class Derived3 : virtual public VirtualBase {}; - -class Derived4 : public Derived2, public Derived3 { -public: - Derived4() {} // NON_COMPLIANT - does not call Derived2(), Derived3() or - // VirtualBase() - Derived4(int i) // NON_COMPLIANT - does not call VirtualBase() - : Derived2(), Derived3() {} - Derived4(int i, int j) // COMPLIANT - calls VirtualBase() - : Derived2(), Derived3(), VirtualBase() {} -}; - -class NonTrivialBase { -public: - NonTrivialBase() = default; - NonTrivialBase(int i){}; - NonTrivialBase(int i, int j){}; -}; - -class Derived5 : public NonTrivialBase { -public: - Derived5() {} // NON_COMPLIANT - does not call NonTrivialBase() - Derived5(int i) : NonTrivialBase(i) {} // COMPLIANT - Derived5(int i, int j) : NonTrivialBase(i, j) {} // COMPLIANT -}; - -class MultipleInheritenceBase {}; - -class Child1 : public MultipleInheritenceBase {}; - -class Child2 : public MultipleInheritenceBase {}; - -class GrandChild : public Child1, public Child2 { - // no need to initialize MultipleInheritenceBase - GrandChild() : Child1(), Child2() {} // COMPLIANT -}; - -class Base2 {}; - -class Derived6 : public Base2 { -public: - Derived6() : b() {} // NON_COMPLIANT - -private: - Base2 b; -}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.expected b/cpp/autosar/test/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.expected index f8900da92f..dbe7f89585 100644 --- a/cpp/autosar/test/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.expected +++ b/cpp/autosar/test/rules/A12-4-1/DestructorOfABaseClassNotPublicVirtual.expected @@ -1,2 +1,2 @@ -| test.cpp:4:3:4:4 | definition of ~A | Destructor of base class A is not declared as public virtual, public override, or protected non-virtual. | -| test.cpp:30:3:30:4 | definition of ~E | Destructor of base class E is not declared as public virtual, public override, or protected non-virtual. | +| test.cpp:4:3:4:4 | definition of ~A | Destructor of base class 'A' is not declared as public virtual, public override, or protected non-virtual. | +| test.cpp:30:3:30:4 | definition of ~E | Destructor of base class 'E' is not declared as public virtual, public override, or protected non-virtual. | diff --git a/cpp/autosar/test/rules/A12-8-1/test.cpp b/cpp/autosar/test/rules/A12-8-1/test.cpp index 481435096a..ad7ae7ec0c 100644 --- a/cpp/autosar/test/rules/A12-8-1/test.cpp +++ b/cpp/autosar/test/rules/A12-8-1/test.cpp @@ -44,8 +44,8 @@ class C4 { C4() : m1(0) {} C4(C4 const &p1) : m1(p1.m1) { std::cout << "Copying class C4" - << std::endl; // NON_COMPLIANT - peformance overhead affecting the - // copying of th object? + << std::endl; // NON_COMPLIANT - performance overhead affecting + // the copying of the object? } private: diff --git a/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.qlref b/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.qlref deleted file mode 100644 index 686462e15f..0000000000 --- a/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.testref b/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.testref new file mode 100644 index 0000000000..65fc614121 --- /dev/null +++ b/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.testref @@ -0,0 +1 @@ +cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.expected b/cpp/autosar/test/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.expected index 39b98e0500..74ed472a52 100644 --- a/cpp/autosar/test/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.expected +++ b/cpp/autosar/test/rules/A12-8-6/CopyAndMoveNotDeclaredProtected.expected @@ -1,14 +1,22 @@ -| test.cpp:4:3:4:12 | declaration of BaseClass1 | Copy constructor for base class BaseClass1 (a derived class exists) is not declared protected or deleted. | -| test.cpp:5:3:5:12 | declaration of BaseClass1 | Move constructor for base class BaseClass1 (a derived class exists) is not declared protected or deleted. | -| test.cpp:6:15:6:23 | declaration of operator= | Copy assignment operator for base class BaseClass1 (a derived class exists) is not declared protected or deleted. | -| test.cpp:7:15:7:23 | declaration of operator= | Move assignment operator for base class BaseClass1 (a derived class exists) is not declared protected or deleted. | -| test.cpp:15:7:15:7 | declaration of operator= | Copy assignment operator for base class BaseClass2 (a derived class exists) is not declared protected or deleted. | -| test.cpp:15:7:15:7 | declaration of operator= | Move assignment operator for base class BaseClass2 (a derived class exists) is not declared protected or deleted. | -| test.cpp:55:3:55:12 | declaration of BaseClass5 | Copy constructor for base class BaseClass5 (a derived class exists) is not declared protected or deleted. | -| test.cpp:56:3:56:12 | declaration of BaseClass5 | Move constructor for base class BaseClass5 (a derived class exists) is not declared protected or deleted. | -| test.cpp:57:15:57:23 | declaration of operator= | Copy assignment operator for base class BaseClass5 (a derived class exists) is not declared protected or deleted. | -| test.cpp:58:15:58:23 | declaration of operator= | Move assignment operator for base class BaseClass5 (a derived class exists) is not declared protected or deleted. | -| test.cpp:75:3:75:12 | declaration of BaseClass6 | Copy constructor for base class BaseClass6 (the class is abstract) is not declared protected or deleted. | -| test.cpp:76:3:76:12 | declaration of BaseClass6 | Move constructor for base class BaseClass6 (the class is abstract) is not declared protected or deleted. | -| test.cpp:77:15:77:23 | declaration of operator= | Copy assignment operator for base class BaseClass6 (the class is abstract) is not declared protected or deleted. | -| test.cpp:78:15:78:23 | declaration of operator= | Move assignment operator for base class BaseClass6 (the class is abstract) is not declared protected or deleted. | +| test.cpp:4:3:4:12 | declaration of BaseClass1 | Copy constructor for base class 'BaseClass1' is not declared protected. | +| test.cpp:5:3:5:12 | declaration of BaseClass1 | Move constructor for base class 'BaseClass1' is not declared protected. | +| test.cpp:6:15:6:23 | declaration of operator= | Copy assignment operator for base class 'BaseClass1' is not declared protected. | +| test.cpp:7:15:7:23 | declaration of operator= | Move assignment operator for base class 'BaseClass1' is not declared protected. | +| test.cpp:15:7:15:7 | declaration of operator= | Implicit copy assignment operator for base class 'BaseClass2' is not declared deleted. | +| test.cpp:15:7:15:7 | declaration of operator= | Implicit move assignment operator for base class 'BaseClass2' is not declared deleted. | +| test.cpp:55:3:55:12 | declaration of BaseClass5 | Copy constructor for base class 'BaseClass5' is not declared protected. | +| test.cpp:56:3:56:12 | declaration of BaseClass5 | Move constructor for base class 'BaseClass5' is not declared protected. | +| test.cpp:57:15:57:23 | declaration of operator= | Copy assignment operator for base class 'BaseClass5' is not declared protected. | +| test.cpp:58:15:58:23 | declaration of operator= | Move assignment operator for base class 'BaseClass5' is not declared protected. | +| test.cpp:75:3:75:12 | declaration of BaseClass6 | Copy constructor for base class 'BaseClass6' is not declared protected. | +| test.cpp:76:3:76:12 | declaration of BaseClass6 | Move constructor for base class 'BaseClass6' is not declared protected. | +| test.cpp:77:15:77:23 | declaration of operator= | Copy assignment operator for base class 'BaseClass6' is not declared protected. | +| test.cpp:78:15:78:23 | declaration of operator= | Move assignment operator for base class 'BaseClass6' is not declared protected. | +| test.cpp:85:3:85:12 | declaration of BaseClass7 | Copy constructor for base class 'BaseClass7' is not declared protected. | +| test.cpp:86:3:86:12 | declaration of BaseClass7 | Move constructor for base class 'BaseClass7' is not declared protected. | +| test.cpp:87:15:87:23 | declaration of operator= | Copy assignment operator for base class 'BaseClass7' is not declared protected. | +| test.cpp:88:15:88:23 | declaration of operator= | Move assignment operator for base class 'BaseClass7' is not declared protected. | +| test.cpp:108:3:108:12 | declaration of BaseClass8 | Copy constructor for base class 'BaseClass8' is not declared protected. | +| test.cpp:109:3:109:12 | declaration of BaseClass8 | Move constructor for base class 'BaseClass8' is not declared protected. | +| test.cpp:110:15:110:23 | declaration of operator= | Copy assignment operator for base class 'BaseClass8' is not declared protected. | +| test.cpp:111:15:111:23 | declaration of operator= | Move assignment operator for base class 'BaseClass8' is not declared protected. | diff --git a/cpp/autosar/test/rules/A12-8-6/test.cpp b/cpp/autosar/test/rules/A12-8-6/test.cpp index 2dc5425706..d197fc18fb 100644 --- a/cpp/autosar/test/rules/A12-8-6/test.cpp +++ b/cpp/autosar/test/rules/A12-8-6/test.cpp @@ -12,8 +12,8 @@ class DerivedClass1 // COMPLIANT - not a base class itself // Base class with compiler generated move/copy is not compliant, because they // are public by default -class BaseClass2 {}; // NON_COMPLIANT - compiler generated move and assignment - // are in contravention +class BaseClass2 {}; // NON_COMPLIANT[FALSE_NEGATIVE] - compiler generated move + // and assignment are in contravention class DerivedClass2 // COMPLIANT - not a base class itself : public BaseClass2 {}; @@ -77,4 +77,67 @@ class BaseClass6 { BaseClass6 &operator=(BaseClass6 const &) = default; // NON_COMPLIANT BaseClass6 &operator=(BaseClass6 &&) = default; // NON_COMPLIANT virtual void test() = 0; // pure virtual function, making this abstract -}; \ No newline at end of file +}; + +template class BaseClass7 { +public: + BaseClass7() {} + BaseClass7(BaseClass7 const &) = default; // NON_COMPLIANT + BaseClass7(BaseClass7 &&) = default; // NON_COMPLIANT + BaseClass7 &operator=(BaseClass7 const &) = default; // NON_COMPLIANT + BaseClass7 &operator=(BaseClass7 &&) = default; // NON_COMPLIANT + int operator=(int i); // COMPLIANT - not an assignment operator +}; + +template +class DerivedClass7 // COMPLIANT - not a base class itself + : public BaseClass7 { +public: + DerivedClass7() {} +}; + +class DerivedClass8 // COMPLIANT - not a base class itself + : public BaseClass7 { +public: + DerivedClass8() {} +}; + +class BaseClass8 { +public: + BaseClass8() {} + BaseClass8(BaseClass8 const &) = default; // NON_COMPLIANT + BaseClass8(BaseClass8 &&) = default; // NON_COMPLIANT + BaseClass8 &operator=(BaseClass8 const &) = default; // NON_COMPLIANT + BaseClass8 &operator=(BaseClass8 &&) = default; // NON_COMPLIANT +}; + +template +class DerivedClass9 // COMPLIANT - not a base class itself + : public BaseClass8 { +public: + DerivedClass9() {} + +private: + T t; +}; + +template class BaseClass9 { // NON_COMPLIANT[FALSE_NEGATIVE] + +public: + BaseClass9() {} +}; + +template +class DerivedClass10 // COMPLIANT - not a base class itself + : public BaseClass9 { +public: + DerivedClass10() {} +}; + +void test() { + BaseClass7 b; + DerivedClass7 d; + DerivedClass9 e; + BaseClass9 f; + DerivedClass10 g; +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.expected b/cpp/autosar/test/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.expected index 53dc884023..5d1d6022b5 100644 --- a/cpp/autosar/test/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.expected +++ b/cpp/autosar/test/rules/A13-1-3/UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.expected @@ -1 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql:27,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql:28,5-13) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UserDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters.ql:27,7-20) | test.cpp:47:8:47:23 | operator ""_uds5 | User defined literal operator returns $@, which is not converted from a passed parameter | test.cpp:48:10:48:12 | 0.0 | expression | diff --git a/cpp/autosar/test/rules/A13-2-1/AssignmentOperatorReturnThis.expected b/cpp/autosar/test/rules/A13-2-1/AssignmentOperatorReturnThis.expected index e9929173b0..9c0d50ca86 100644 --- a/cpp/autosar/test/rules/A13-2-1/AssignmentOperatorReturnThis.expected +++ b/cpp/autosar/test/rules/A13-2-1/AssignmentOperatorReturnThis.expected @@ -1,3 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (AssignmentOperatorReturnThis.ql:25,5-13) | test.cpp:10:12:10:20 | operator= | User-defined assignment operator $@ does not return *this | test.cpp:10:12:10:20 | operator= | user defined assignment operator | | test.cpp:17:11:17:19 | operator= | User-defined assignment operator $@ does not return *this | test.cpp:17:11:17:19 | operator= | user defined assignment operator | | test.cpp:24:12:24:20 | operator= | User-defined assignment operator $@ does not return *this | test.cpp:24:12:24:20 | operator= | user defined assignment operator | diff --git a/cpp/autosar/test/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.expected b/cpp/autosar/test/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.expected index 45d74bda6f..3ebbfa09cf 100644 --- a/cpp/autosar/test/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.expected +++ b/cpp/autosar/test/rules/A13-2-2/BinaryOperatorAndBitwiseOperatorReturnAPrvalue.expected @@ -1,4 +1,4 @@ -| test.cpp:16:9:16:17 | operator- | User-defined bitwise or arithmetic operator operator-(const A &, int) -> const A does not return a prvalue. | -| test.cpp:20:4:20:12 | operator\| | User-defined bitwise or arithmetic operator operator\|(const A &, const A &) -> A * does not return a prvalue. | -| test.cpp:24:9:24:18 | operator<< | User-defined bitwise or arithmetic operator operator<<(const A &, const A &) -> const A does not return a prvalue. | -| test.cpp:34:6:34:14 | operator+ | User-defined bitwise or arithmetic operator NS_C::operator+(const C &, const C &) -> int & does not return a prvalue. | +| test.cpp:16:9:16:17 | operator- | User-defined bitwise or arithmetic operator operator- does not return a prvalue. | +| test.cpp:20:4:20:12 | operator\| | User-defined bitwise or arithmetic operator operator\| does not return a prvalue. | +| test.cpp:24:9:24:18 | operator<< | User-defined bitwise or arithmetic operator operator<< does not return a prvalue. | +| test.cpp:34:6:34:14 | operator+ | User-defined bitwise or arithmetic operator operator+ does not return a prvalue. | diff --git a/cpp/autosar/test/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.expected b/cpp/autosar/test/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.expected index 590f891ead..cb71b56b51 100644 --- a/cpp/autosar/test/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.expected +++ b/cpp/autosar/test/rules/A13-3-1/FunctionThatContainsForwardingReferenceAsItsArgumentOverloaded.expected @@ -1,5 +1,3 @@ -| test.cpp:24:6:24:7 | F1 | Function overloads a $@ with a forwarding reference parameter. | test.cpp:27:25:27:26 | F1 | function | -| test.cpp:49:3:49:3 | A | Function overloads a $@ with a forwarding reference parameter. | test.cpp:47:3:47:3 | A | function | -| test.cpp:50:3:50:3 | A | Function overloads a $@ with a forwarding reference parameter. | test.cpp:47:3:47:3 | A | function | -| test.cpp:63:8:63:8 | B | Function overloads a $@ with a forwarding reference parameter. | test.cpp:66:3:66:3 | B | function | -| test.cpp:63:8:63:8 | B | Function overloads a $@ with a forwarding reference parameter. | test.cpp:66:3:66:3 | B | function | +| test.cpp:24:6:24:7 | F1 | Function overloads a $@. | test.cpp:27:25:27:26 | F1 | function with a forwarding reference parameter | +| test.cpp:50:3:50:3 | A | Function overloads a $@. | test.cpp:48:3:48:3 | A | function with a forwarding reference parameter | +| test.cpp:51:3:51:3 | A | Function overloads a $@. | test.cpp:48:3:48:3 | A | function with a forwarding reference parameter | diff --git a/cpp/autosar/test/rules/A13-3-1/test.cpp b/cpp/autosar/test/rules/A13-3-1/test.cpp index 4a706b53e2..8ed4e4d609 100644 --- a/cpp/autosar/test/rules/A13-3-1/test.cpp +++ b/cpp/autosar/test/rules/A13-3-1/test.cpp @@ -39,7 +39,8 @@ template void F1(T &&x) {} // class A { public: - // COMPLIANT by exception, constrained to not match copy/move ctors + // COMPLIANT[FALSE_POSITIVE] - by exception, constrained to not match + // explicit copy/move ctors template < typename T, std::enable_if_t::value> * = nullptr> - B(T &&value) {} + template < + typename T, + std::enable_if_t>, A>::value> * = nullptr> + B(T &&value) {} // COMPLIANT - by exception }; -int main() {} \ No newline at end of file +int main() {} + +class C { +public: + C() {} + template + C(T &&) {} // COMPLIANT - ignore overloads of implicit copy/move ctors +}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A13-5-2/test.cpp b/cpp/autosar/test/rules/A13-5-2/test.cpp index 0f9f7a3b3d..37bc0d0fdb 100644 --- a/cpp/autosar/test/rules/A13-5-2/test.cpp +++ b/cpp/autosar/test/rules/A13-5-2/test.cpp @@ -8,4 +8,15 @@ class A { operator int() const { return d; } // NON_COMPLIANT private: float d; -}; \ No newline at end of file +}; + +void test_compiler_generated() { + int x = 0; + + auto capture = [x]() -> int { return x; }; + + auto no_capture = []() -> int { + int x = 1; + return x; + }; +} diff --git a/cpp/autosar/test/rules/A13-5-3/UserDefinedConversionOperatorsShouldNotBeUsed.expected b/cpp/autosar/test/rules/A13-5-3/UserDefinedConversionOperatorsShouldNotBeUsed.expected index e757cdf984..14e68ab4a9 100644 --- a/cpp/autosar/test/rules/A13-5-3/UserDefinedConversionOperatorsShouldNotBeUsed.expected +++ b/cpp/autosar/test/rules/A13-5-3/UserDefinedConversionOperatorsShouldNotBeUsed.expected @@ -1,4 +1,4 @@ | test.cpp:33:7:33:7 | call to operator A | User-defined conversion operators should not be used. | | test.cpp:35:24:35:24 | call to operator A * | User-defined conversion operators should not be used. | -| test.cpp:37:15:37:15 | call to operator B::array_A * | User-defined conversion operators should not be used. | +| test.cpp:37:15:37:15 | call to operator A (*)[3] | User-defined conversion operators should not be used. | | test.cpp:41:7:41:7 | call to operator A * | User-defined conversion operators should not be used. | diff --git a/cpp/autosar/test/rules/A13-5-3/test.cpp b/cpp/autosar/test/rules/A13-5-3/test.cpp index 8eaaef6581..c50578b280 100644 --- a/cpp/autosar/test/rules/A13-5-3/test.cpp +++ b/cpp/autosar/test/rules/A13-5-3/test.cpp @@ -35,7 +35,7 @@ int main() { bar(static_cast(b)); // NON_COMPLIANT--explicit A(*pa)[3] = b; // NON_COMPLIANT - converting B to array of A (size 3)- - // decalartion of variable name pa, pointer to array of A , array size is 3 + // declaration of variable name pa, pointer to array of A , array size is 3 C c; bar(c); // NON_COMPLIANT - one pointer with bar diff --git a/cpp/autosar/test/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.expected b/cpp/autosar/test/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.expected index d45a3c6871..f0c78e2af1 100644 --- a/cpp/autosar/test/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.expected +++ b/cpp/autosar/test/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.expected @@ -1,4 +1,3 @@ -| test.cpp:10:9:10:10 | T1 | Member T1 template class does not use any of template arguments of its $@. | test.cpp:6:29:6:30 | C1 | declaring type | | test.cpp:11:9:11:10 | T2 | Member T2 template class does not use any of template arguments of its $@. | test.cpp:6:29:6:30 | C1 | declaring type | | test.cpp:28:31:28:33 | C12 | Member C12 template class does not use any of template arguments of its $@. | test.cpp:6:29:6:30 | C1 | declaring type | | test.cpp:45:7:45:8 | a1 | Member a1 template class does not use any of template arguments of its $@. | test.cpp:37:31:37:33 | C22 | declaring type | diff --git a/cpp/autosar/test/rules/A14-5-2/test.cpp b/cpp/autosar/test/rules/A14-5-2/test.cpp index e60a955c68..260ff5b4b2 100644 --- a/cpp/autosar/test/rules/A14-5-2/test.cpp +++ b/cpp/autosar/test/rules/A14-5-2/test.cpp @@ -7,7 +7,7 @@ template class C1 { public: enum E1 : T { e1, e2 }; // COMPLIANT - using T1 = typename template_base::type; // COMPLIANT[FALSE_POSITIVE] + using T1 = typename template_base::type; // COMPLIANT using T2 = typename template_base::type; // NON_COMPLIANT class C11 { // COMPLIANT @@ -156,4 +156,4 @@ template class V { void f4() { V v; v.type(); -} \ No newline at end of file +} diff --git a/cpp/autosar/test/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.expected b/cpp/autosar/test/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.expected index 1ec607ca49..e697056580 100644 --- a/cpp/autosar/test/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.expected +++ b/cpp/autosar/test/rules/A14-7-2/TemplateSpecializationNotDeclaredInTheSameFile.expected @@ -1,3 +1,3 @@ -| test.cpp:5:20:5:25 | s | Specialization found in file $@ where primary template is outside that file. | test.cpp:0:0:0:0 | test.cpp | rules/A14-7-2/test.cpp | -| test.cpp:7:18:7:24 | f | Specialization found in file $@ where primary template is outside that file. | test.cpp:0:0:0:0 | test.cpp | rules/A14-7-2/test.cpp | -| test.cpp:13:19:13:28 | vector> | Specialization found in file $@ where primary template is outside that file. | test.cpp:0:0:0:0 | test.cpp | rules/A14-7-2/test.cpp | +| test.cpp:5:20:5:25 | s | Specialization found in file $@ where primary template is outside that file. | test.cpp:0:0:0:0 | test.cpp | test.cpp | +| test.cpp:7:18:7:24 | f | Specialization found in file $@ where primary template is outside that file. | test.cpp:0:0:0:0 | test.cpp | test.cpp | +| test.cpp:13:19:13:28 | vector> | Specialization found in file $@ where primary template is outside that file. | test.cpp:0:0:0:0 | test.cpp | test.cpp | diff --git a/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.qlref b/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.qlref deleted file mode 100644 index b2f19b3af3..0000000000 --- a/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.testref b/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.testref new file mode 100644 index 0000000000..6a284e2cbb --- /dev/null +++ b/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.qlref b/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.qlref deleted file mode 100644 index 68c8e7af9a..0000000000 --- a/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A15-1-2/PointerExceptionObject.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.testref b/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.testref new file mode 100644 index 0000000000..24d4229225 --- /dev/null +++ b/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.testref @@ -0,0 +1 @@ +cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-1-3/ThrownExceptionsShouldBeUnique.expected b/cpp/autosar/test/rules/A15-1-3/ThrownExceptionsShouldBeUnique.expected index b085736659..5db0f83985 100644 --- a/cpp/autosar/test/rules/A15-1-3/ThrownExceptionsShouldBeUnique.expected +++ b/cpp/autosar/test/rules/A15-1-3/ThrownExceptionsShouldBeUnique.expected @@ -1,3 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ThrownExceptionsShouldBeUnique.ql:24,3-11) | test.cpp:6:5:6:26 | throw ... | The $@ thrown here is a possible duplicate of the $@ thrown $@. | test.cpp:6:5:6:26 | call to exception | std::exception exception | test.cpp:14:5:14:26 | call to exception | exception | test.cpp:14:5:14:26 | throw ... | here | | test.cpp:8:5:8:53 | throw ... | The $@ thrown here is a possible duplicate of the $@ thrown $@. | test.cpp:8:5:8:53 | call to runtime_error | std::runtime_error exception | test.cpp:16:5:16:53 | call to runtime_error | exception | test.cpp:16:5:16:53 | throw ... | here | | test.cpp:14:5:14:26 | throw ... | The $@ thrown here is a possible duplicate of the $@ thrown $@. | test.cpp:14:5:14:26 | call to exception | std::exception exception | test.cpp:6:5:6:26 | call to exception | exception | test.cpp:6:5:6:26 | throw ... | here | diff --git a/cpp/autosar/test/rules/A15-1-5/ExceptionsThrownAcrossExecutionBoundaries.expected.clang b/cpp/autosar/test/rules/A15-1-5/ExceptionsThrownAcrossExecutionBoundaries.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A15-1-5/ExceptionsThrownAcrossExecutionBoundaries.expected.gcc b/cpp/autosar/test/rules/A15-1-5/ExceptionsThrownAcrossExecutionBoundaries.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A15-1-5/ExceptionsThrownAcrossExecutionBoundaries.expected.qcc b/cpp/autosar/test/rules/A15-1-5/ExceptionsThrownAcrossExecutionBoundaries.expected.qcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.expected b/cpp/autosar/test/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.expected index b077cc93bb..529a7ccf99 100644 --- a/cpp/autosar/test/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.expected +++ b/cpp/autosar/test/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.expected @@ -1,62 +1,51 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:47,12-20) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:48,30-38) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:48,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:74,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:74,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:75,7-15) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:130,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:130,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstructorErrorLeavesObjectInInvalidState.ql:130,54-62) edges | test.cpp:12:16:12:27 | new [bad_alloc] | test.cpp:14:33:16:5 | { ... } [bad_alloc] | | test.cpp:13:7:13:28 | throw ... [exception] | test.cpp:14:33:16:5 | { ... } [exception] | | test.cpp:14:33:16:5 | { ... } [bad_alloc] | test.cpp:15:7:15:11 | re-throw exception [bad_alloc] | | test.cpp:14:33:16:5 | { ... } [exception] | test.cpp:15:7:15:11 | re-throw exception [exception] | +| test.cpp:15:7:15:11 | re-throw exception [bad_alloc] | test.cpp:9:3:9:8 | ClassA [bad_alloc] | +| test.cpp:15:7:15:11 | re-throw exception [exception] | test.cpp:9:3:9:8 | ClassA [exception] | | test.cpp:25:16:25:27 | new [bad_alloc] | test.cpp:27:33:30:5 | { ... } [bad_alloc] | | test.cpp:26:7:26:28 | throw ... [exception] | test.cpp:27:33:30:5 | { ... } [exception] | | test.cpp:27:33:30:5 | { ... } [bad_alloc] | test.cpp:29:7:29:11 | re-throw exception [bad_alloc] | | test.cpp:27:33:30:5 | { ... } [exception] | test.cpp:29:7:29:11 | re-throw exception [exception] | +| test.cpp:29:7:29:11 | re-throw exception [bad_alloc] | test.cpp:23:3:23:8 | ClassB [bad_alloc] | +| test.cpp:29:7:29:11 | re-throw exception [exception] | test.cpp:23:3:23:8 | ClassB [exception] | | test.cpp:44:16:44:27 | call to CreateMember [bad_alloc] | test.cpp:46:33:48:5 | { ... } [bad_alloc] | | test.cpp:45:7:45:28 | throw ... [exception] | test.cpp:46:33:48:5 | { ... } [exception] | | test.cpp:46:33:48:5 | { ... } [bad_alloc] | test.cpp:47:7:47:11 | re-throw exception [bad_alloc] | | test.cpp:46:33:48:5 | { ... } [exception] | test.cpp:47:7:47:11 | re-throw exception [exception] | +| test.cpp:47:7:47:11 | re-throw exception [bad_alloc] | test.cpp:41:3:41:8 | ClassC [bad_alloc] | +| test.cpp:47:7:47:11 | re-throw exception [exception] | test.cpp:41:3:41:8 | ClassC [exception] | | test.cpp:58:16:58:27 | call to CreateMember [bad_alloc] | test.cpp:60:33:63:5 | { ... } [bad_alloc] | | test.cpp:59:7:59:28 | throw ... [exception] | test.cpp:60:33:63:5 | { ... } [exception] | | test.cpp:60:33:63:5 | { ... } [bad_alloc] | test.cpp:62:7:62:11 | re-throw exception [bad_alloc] | | test.cpp:60:33:63:5 | { ... } [exception] | test.cpp:62:7:62:11 | re-throw exception [exception] | +| test.cpp:62:7:62:11 | re-throw exception [bad_alloc] | test.cpp:55:3:55:8 | ClassD [bad_alloc] | +| test.cpp:62:7:62:11 | re-throw exception [exception] | test.cpp:55:3:55:8 | ClassD [exception] | +| test.cpp:77:11:77:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | +| test.cpp:78:11:78:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | | test.cpp:80:13:80:22 | new [bad_alloc] | test.cpp:82:33:86:5 | { ... } [bad_alloc] | | test.cpp:81:13:81:22 | new [bad_alloc] | test.cpp:82:33:86:5 | { ... } [bad_alloc] | | test.cpp:82:33:86:5 | { ... } [bad_alloc] | test.cpp:85:7:85:11 | re-throw exception [bad_alloc] | -nodes -| test.cpp:12:16:12:27 | new [bad_alloc] | -| test.cpp:13:7:13:28 | throw ... [exception] | -| test.cpp:14:33:16:5 | { ... } [bad_alloc] | -| test.cpp:14:33:16:5 | { ... } [exception] | -| test.cpp:15:7:15:11 | re-throw exception [bad_alloc] | -| test.cpp:15:7:15:11 | re-throw exception [exception] | -| test.cpp:25:16:25:27 | new [bad_alloc] | -| test.cpp:26:7:26:28 | throw ... [exception] | -| test.cpp:27:33:30:5 | { ... } [bad_alloc] | -| test.cpp:27:33:30:5 | { ... } [exception] | -| test.cpp:29:7:29:11 | re-throw exception [bad_alloc] | -| test.cpp:29:7:29:11 | re-throw exception [exception] | -| test.cpp:44:16:44:27 | call to CreateMember [bad_alloc] | -| test.cpp:45:7:45:28 | throw ... [exception] | -| test.cpp:46:33:48:5 | { ... } [bad_alloc] | -| test.cpp:46:33:48:5 | { ... } [exception] | -| test.cpp:47:7:47:11 | re-throw exception [bad_alloc] | -| test.cpp:47:7:47:11 | re-throw exception [exception] | -| test.cpp:58:16:58:27 | call to CreateMember [bad_alloc] | -| test.cpp:59:7:59:28 | throw ... [exception] | -| test.cpp:60:33:63:5 | { ... } [bad_alloc] | -| test.cpp:60:33:63:5 | { ... } [exception] | -| test.cpp:62:7:62:11 | re-throw exception [bad_alloc] | -| test.cpp:62:7:62:11 | re-throw exception [exception] | -| test.cpp:77:11:77:20 | new [bad_alloc] | -| test.cpp:78:11:78:20 | new [bad_alloc] | -| test.cpp:80:13:80:22 | new [bad_alloc] | -| test.cpp:81:13:81:22 | new [bad_alloc] | -| test.cpp:82:33:86:5 | { ... } [bad_alloc] | -| test.cpp:85:7:85:11 | re-throw exception [bad_alloc] | -| test.cpp:87:11:87:20 | new [bad_alloc] | +| test.cpp:85:7:85:11 | re-throw exception [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | +| test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | #select | test.cpp:9:3:9:8 | ClassA | test.cpp:13:7:13:28 | throw ... [exception] | test.cpp:15:7:15:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:15:7:15:11 | re-throw exception | std::bad_alloc | test.cpp:12:16:12:27 | new | alloc | | test.cpp:9:3:9:8 | ClassA | test.cpp:13:7:13:28 | throw ... [exception] | test.cpp:15:7:15:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:15:7:15:11 | re-throw exception | std::exception | test.cpp:12:16:12:27 | new | alloc | | test.cpp:41:3:41:8 | ClassC | test.cpp:45:7:45:28 | throw ... [exception] | test.cpp:47:7:47:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:47:7:47:11 | re-throw exception | std::bad_alloc | test.cpp:44:16:44:27 | call to CreateMember | alloc | | test.cpp:41:3:41:8 | ClassC | test.cpp:45:7:45:28 | throw ... [exception] | test.cpp:47:7:47:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:47:7:47:11 | re-throw exception | std::exception | test.cpp:44:16:44:27 | call to CreateMember | alloc | -| test.cpp:75:3:75:8 | ClassE | test.cpp:78:11:78:20 | new [bad_alloc] | test.cpp:78:11:78:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:78:11:78:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc | -| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc | -| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:78:11:78:20 | new | alloc | -| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:80:13:80:22 | new | alloc | -| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:81:13:81:22 | new | alloc | +| test.cpp:75:3:75:8 | ClassE | test.cpp:78:11:78:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:78:11:78:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc | +| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc | +| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:78:11:78:20 | new | alloc | +| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:80:13:80:22 | new | alloc | +| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:81:13:81:22 | new | alloc | diff --git a/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.expected b/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.expected deleted file mode 100644 index 2a0726c356..0000000000 --- a/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.expected +++ /dev/null @@ -1,11 +0,0 @@ -edges -| test.cpp:5:3:5:20 | throw ... [ExceptionA] | test.cpp:4:6:4:15 | test_throw [ExceptionA] | -| test.cpp:8:6:8:11 | throwA [ExceptionA] | test.cpp:11:3:11:8 | call to throwA [ExceptionA] | -| test.cpp:8:6:8:11 | throwA [ExceptionA] | test.cpp:15:3:15:8 | call to throwA [ExceptionA] | -| test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:8:6:8:11 | throwA [ExceptionA] | -| test.cpp:11:3:11:8 | call to throwA [ExceptionA] | test.cpp:10:6:10:24 | test_indirect_throw [ExceptionA] | -| test.cpp:15:3:15:8 | call to throwA [ExceptionA] | test.cpp:14:6:14:26 | test_indirect_throw_2 [ExceptionA] | -#select -| test.cpp:4:6:4:15 | test_throw | test.cpp:5:3:5:20 | throw ... [ExceptionA] | test.cpp:4:6:4:15 | test_throw [ExceptionA] | Function test_throw is declared noexcept(true) but can throw exceptions of type ExceptionA. | -| test.cpp:10:6:10:24 | test_indirect_throw | test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:10:6:10:24 | test_indirect_throw [ExceptionA] | Function test_indirect_throw is declared noexcept(true) but can throw exceptions of type ExceptionA. | -| test.cpp:14:6:14:26 | test_indirect_throw_2 | test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:14:6:14:26 | test_indirect_throw_2 [ExceptionA] | Function test_indirect_throw_2 is declared noexcept(true) but can throw exceptions of type ExceptionA. | diff --git a/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.qlref b/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.qlref deleted file mode 100644 index 80fbc7365c..0000000000 --- a/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A15-4-2/NoExceptFunctionThrows.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.testref b/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.testref new file mode 100644 index 0000000000..76dc55827f --- /dev/null +++ b/cpp/autosar/test/rules/A15-4-2/NoExceptFunctionThrows.testref @@ -0,0 +1 @@ +cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-4-2/test.cpp b/cpp/autosar/test/rules/A15-4-2/test.cpp deleted file mode 100644 index afa46e5ae6..0000000000 --- a/cpp/autosar/test/rules/A15-4-2/test.cpp +++ /dev/null @@ -1,24 +0,0 @@ - -class ExceptionA {}; - -void test_throw() noexcept(true) { - throw ExceptionA(); // NON_COMPLIANT - function marked as noexcept(true) -} - -void throwA() { throw ExceptionA(); } - -void test_indirect_throw() noexcept(true) { - throwA(); // NON_COMPLIANT - function marked as noexcept(true) -} - -void test_indirect_throw_2() noexcept { - throwA(); // NON_COMPLIANT - function marked as noexcept(true) -} - -void test_indirect_throw_3() noexcept(false) { - throwA(); // COMPLIANT - function marked as noexcept(false) -} - -void test_indirect_throw_4() { - throwA(); // COMPLIANT - function marked as noexcept(false) -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-4-4/coding-standards.xml b/cpp/autosar/test/rules/A15-4-4/coding-standards.xml new file mode 100644 index 0000000000..6ae9c299fa --- /dev/null +++ b/cpp/autosar/test/rules/A15-4-4/coding-standards.xml @@ -0,0 +1,11 @@ + + + + + + A15-4-4 + Suppress entry. + a-15-4-4-deviation + + + diff --git a/cpp/autosar/test/rules/A15-4-4/test.cpp b/cpp/autosar/test/rules/A15-4-4/test.cpp index 6834c7a8ce..faae76ca8e 100644 --- a/cpp/autosar/test/rules/A15-4-4/test.cpp +++ b/cpp/autosar/test/rules/A15-4-4/test.cpp @@ -24,4 +24,44 @@ void test_indirect_throw() { // COMPLIANT - throws an exception indirectly class A { public: A() = delete; // COMPLIANT - deleted functions imply `noexcept(true)`. -}; \ No newline at end of file +}; + +/* Added for testing FP of embedded operator inside lambdas being reported */ +void lambda_example() noexcept { + auto with_capture = [=]() {}; + auto empty_capture = []() {}; +} + +#include +template +void swap_wrapper(TypeA lhs, + TypeB rhs) noexcept(noexcept(std::swap(*lhs, *rhs))) { + std::swap(*lhs, *rhs); +} + +void test_swap_wrapper() noexcept { + int a = 0; + int b = 1; + swap_wrapper(&a, &b); +} + +#include +#include + +std::string test_fp_reported_in_424( + const std::string &s1, + const std::string &s2) { // COMPLIANT - `reserve` and `append` may throw. + std::string s3; + s3.reserve(s1.size() + s2.size()); + s3.append(s1.c_str(), s1.size()); + s3.append(s2.c_str(), s2.size()); + return s3; +} + +void test_no_except_deviated_decl(); // a-15-4-4-deviation + +void test_no_except_deviated_decl() {} + +void test_no_except_deviated_defn(); + +void test_no_except_deviated_defn() {} // a-15-4-4-deviation \ No newline at end of file diff --git a/cpp/autosar/test/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.expected b/cpp/autosar/test/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.expected index 9e698e41ae..4c6a3ae4f7 100644 --- a/cpp/autosar/test/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.expected +++ b/cpp/autosar/test/rules/A15-5-1/SpecialFunctionMissingNoExceptSpecification.expected @@ -1,11 +1,11 @@ -| test.cpp:5:3:5:9 | ~ClassA | ClassA::~ClassA should not be noexcept(false). | -| test.cpp:9:3:9:9 | ~ClassB | ClassB::~ClassB should not be noexcept(false). | -| test.cpp:38:6:38:20 | operator delete | operator delete is implicitly noexcept(false) and might throw. | -| test.cpp:43:6:43:20 | operator delete | operator delete is implicitly noexcept(false) and might throw. | -| test.cpp:53:11:53:19 | operator= | ClassF::operator= should not be noexcept(false). | -| test.cpp:63:3:63:8 | ClassH | ClassH::ClassH should not be noexcept(false). | -| test.cpp:68:6:68:9 | swap | swap is implicitly noexcept(false) and might throw. | -| test.cpp:72:6:72:9 | swap | swap should not be noexcept(false). | -| test.cpp:77:8:77:11 | swap | ClassI::swap is implicitly noexcept(false) and might throw. | -| test.cpp:82:8:82:11 | swap | ClassJ::swap is implicitly noexcept(false) and might throw. | -| test.cpp:88:6:88:6 | swap | swap is implicitly noexcept(false) and might throw. | +| test.cpp:5:3:5:9 | ~ClassA | Special function ClassA::~ClassA has a noexcept(false) specification that permits exceptions. | +| test.cpp:9:3:9:9 | ~ClassB | Special function ClassB::~ClassB has a noexcept(false) specification that permits exceptions. | +| test.cpp:38:6:38:20 | operator delete | operator delete has an implicit noexcept(true) specification but should make that explicit. | +| test.cpp:43:6:43:20 | operator delete | operator delete has an implicit noexcept(true) specification but should make that explicit. | +| test.cpp:53:11:53:19 | operator= | Special function ClassF::operator= has a noexcept(false) specification that permits exceptions. | +| test.cpp:63:3:63:8 | ClassH | Special function ClassH::ClassH has a noexcept(false) specification that permits exceptions. | +| test.cpp:68:6:68:9 | swap | Special function swap has an implicit noexcept(false) specification that permits exceptions. | +| test.cpp:72:6:72:9 | swap | Special function swap has a noexcept(false) specification that permits exceptions. | +| test.cpp:77:8:77:11 | swap | Special function ClassI::swap has an implicit noexcept(false) specification that permits exceptions. | +| test.cpp:82:8:82:11 | swap | Special function ClassJ::swap has an implicit noexcept(false) specification that permits exceptions. | +| test.cpp:88:6:88:6 | swap | Special function swap has an implicit noexcept(false) specification that permits exceptions. | diff --git a/cpp/autosar/test/rules/A15-5-1/test.cpp b/cpp/autosar/test/rules/A15-5-1/test.cpp index adc45dd36c..353f4f62d7 100644 --- a/cpp/autosar/test/rules/A15-5-1/test.cpp +++ b/cpp/autosar/test/rules/A15-5-1/test.cpp @@ -1,6 +1,6 @@ #include "stddef.h" +#include #include - class ClassA { ~ClassA() noexcept(false) { throw std::exception(); } // NON_COMPLIANT }; @@ -36,12 +36,12 @@ class ClassD { }; void operator delete(void *ptr) { // NON_COMPLIANT - // NOTE: cannot be declared noexcept(false) + // NOTE: defaults to noexcept(true) throw std::exception(); } void operator delete(void *ptr, size_t size) { // NON_COMPLIANT - // NOTE: cannot be declared noexcept(false) + // NOTE: defaults to noexcept(true) throw std::exception(); } diff --git a/cpp/autosar/test/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.expected b/cpp/autosar/test/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.expected index b8a83801b4..44dd686b23 100644 --- a/cpp/autosar/test/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.expected +++ b/cpp/autosar/test/rules/A16-0-1/PreProcessorShallOnlyBeUsedForCertainDirectivesPatterns.expected @@ -1,5 +1,8 @@ -| test.cpp:3:1:3:25 | #pragma gcc testingpragma | Preprocessor directive used for conditional compilation. | | test.cpp:5:1:5:18 | #ifndef TESTHEADER | Preprocessor directive used for conditional compilation. | | test.cpp:9:1:9:26 | #define OBJECTLIKE_MACRO 1 | Preprocessor directive used for conditional compilation. | | test.cpp:10:1:10:35 | #define FUNCTIONLIKE_MACRO(X) X + 1 | Preprocessor directive used for conditional compilation. | | test.cpp:11:1:11:37 | #define FUNCTIONLIKE_MACROTWO() 1 + 1 | Preprocessor directive used for conditional compilation. | +| test.cpp:31:1:31:26 | #elif OBJECTLIKE_MACRO > 0 | Preprocessor directive used for conditional compilation. | +| test.cpp:37:1:37:26 | #elif OBJECTLIKE_MACRO > 0 | Preprocessor directive used for conditional compilation. | +| test.cpp:41:1:41:23 | #ifdef OBJECTLIKE_MACRO | Preprocessor directive used for conditional compilation. | +| test.cpp:58:1:58:27 | #elif MACRO_ENABLED_OTHER_1 | Preprocessor directive used for conditional compilation. | diff --git a/cpp/autosar/test/rules/A16-0-1/options b/cpp/autosar/test/rules/A16-0-1/options new file mode 100644 index 0000000000..9c0e6cf7b5 --- /dev/null +++ b/cpp/autosar/test/rules/A16-0-1/options @@ -0,0 +1 @@ +semmle-extractor-options:--clang -std=c++14 --edg --diag_error=implicit_func_decl -nostdinc -I../../../../../cpp/common/test/includes/standard-library -D MACRO_ENABLED_NON_1 -D MACRO_ENABLED_OTHER_1 diff --git a/cpp/autosar/test/rules/A16-0-1/test.cpp b/cpp/autosar/test/rules/A16-0-1/test.cpp index a855cca169..a8b83e40ec 100644 --- a/cpp/autosar/test/rules/A16-0-1/test.cpp +++ b/cpp/autosar/test/rules/A16-0-1/test.cpp @@ -1,6 +1,6 @@ #include //COMPLIANT -#pragma gcc testingpragma // NON_COMPLIANT +#pragma gcc testingpragma // COMPLIANT - exception - already reported by A16-7-1 #ifndef TESTHEADER // NON_COMPLIANT int g; @@ -17,3 +17,59 @@ int g; #ifndef TESTHEADER // COMPLIANT #include //COMPLIANT #endif // COMPLIANT + +#ifdef MACRO_ENABLED // COMPLIANT +#include // COMPLIANT +#else // COMPLIANT +#include // COMPLIANT +#endif // COMPLIANT + +#ifdef MACRO_ENABLED_NON // COMPLIANT +#include // COMPLIANT +#elif MACRO_ENABLED_OTHER // COMPLIANT +#include // COMPLIANT +#elif OBJECTLIKE_MACRO > 0 // NON_COMPLIANT +int x00 = 1; // present +#endif // COMPLIANT + +#ifdef OBJECTLIKE_MACRO_NO // COMPLIANT +int x0 = 0; // not present +#elif OBJECTLIKE_MACRO > 0 // NON_COMPLIANT +int x0 = 1; // present +#endif // COMPLIANT + +#ifdef OBJECTLIKE_MACRO // NON_COMPLIANT +int x1 = 0; // present +#elif OBJECTLIKE_MACRO > -1 // NON_COMPLIANT[FALSE_NEGATIVE] - known due to + // database not containing elements +int x1 = 1; // not present +#endif // COMPLIANT + +// case 1 - first present only +#ifdef MACRO_ENABLED_NON_1 // COMPLIANT +#include //present +#elif MACRO_ENABLED_OTHER // NON_COMPLIANT[FALSE_NEGATIVE] +int x = 1; // not present +#endif + +// case 2 - second present only +#ifdef MACRO_ENABLED_NON // COMPLIANT +#include //not present +#elif MACRO_ENABLED_OTHER_1 // NON_COMPLIANT +int x = 1; // present +#endif + +// case 3 - neither present +#ifdef MACRO_ENABLED_NON // COMPLIANT +#include //not present +#elif MACRO_ENABLED_OTHER // NON_COMPLIANT[FALSE_NEGATIVE] +int x = 1; // not present +#endif + +// case 4 - both look present but the second still not bc the condition is not +// required to be evaluated +#ifdef MACRO_ENABLED_NON_1 // COMPLIANT +#include //present +#elif MACRO_ENABLED_OTHER_1 // NON_COMPLIANT[FALSE_NEGATIVE] +int x = 1; // not present +#endif \ No newline at end of file diff --git a/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected index 6e141d4fbe..631736202f 100644 --- a/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected +++ b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected @@ -1,2 +1,2 @@ | test.cpp:1:1:1:19 | #include "test.hpp" | Nothing in this file uses anything from "test.hpp" | -| test.cpp:3:1:3:17 | #include | Nothing in this file uses anything from | +| test.cpp:2:1:2:20 | #include | Nothing in this file uses anything from | diff --git a/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.clang b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.clang new file mode 100644 index 0000000000..153bf8fa0f --- /dev/null +++ b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.clang @@ -0,0 +1 @@ +| test.cpp:1:1:1:19 | #include "test.hpp" | Nothing in this file uses anything from "test.hpp" | diff --git a/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.gcc b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.gcc new file mode 100644 index 0000000000..153bf8fa0f --- /dev/null +++ b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.gcc @@ -0,0 +1 @@ +| test.cpp:1:1:1:19 | #include "test.hpp" | Nothing in this file uses anything from "test.hpp" | diff --git a/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.qcc b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.qcc new file mode 100644 index 0000000000..153bf8fa0f --- /dev/null +++ b/cpp/autosar/test/rules/A16-2-2/UnusedIncludeDirectives.expected.qcc @@ -0,0 +1 @@ +| test.cpp:1:1:1:19 | #include "test.hpp" | Nothing in this file uses anything from "test.hpp" | diff --git a/cpp/autosar/test/rules/A16-2-2/internal.h b/cpp/autosar/test/rules/A16-2-2/internal.h new file mode 100644 index 0000000000..6eb06a9a27 --- /dev/null +++ b/cpp/autosar/test/rules/A16-2-2/internal.h @@ -0,0 +1 @@ +void f(); \ No newline at end of file diff --git a/cpp/autosar/test/rules/A16-2-2/test.cpp b/cpp/autosar/test/rules/A16-2-2/test.cpp index 1e3c536057..571675ab18 100644 --- a/cpp/autosar/test/rules/A16-2-2/test.cpp +++ b/cpp/autosar/test/rules/A16-2-2/test.cpp @@ -1,5 +1,5 @@ #include "test.hpp" //NON_COMPLIANT -#include //COMPLIANT -#include //NON_COMPLIANT +#include //NON_COMPLIANT - redundant but not useless on real compilers +#include //COMPLIANT -std::string s = "A string"; \ No newline at end of file +std::vector v; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A16-2-2/test.hpp b/cpp/autosar/test/rules/A16-2-2/test.hpp index 6eb06a9a27..a6c63f5413 100644 --- a/cpp/autosar/test/rules/A16-2-2/test.hpp +++ b/cpp/autosar/test/rules/A16-2-2/test.hpp @@ -1 +1,3 @@ -void f(); \ No newline at end of file +#include "z.h" + +void g() { f(); } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A16-2-2/test2.cpp b/cpp/autosar/test/rules/A16-2-2/test2.cpp new file mode 100644 index 0000000000..6a4e01987d --- /dev/null +++ b/cpp/autosar/test/rules/A16-2-2/test2.cpp @@ -0,0 +1,7 @@ +#include "test.hpp" // COMPLIANT +#include "z.h" // COMPLIANT + +void test() { + f(); + g(); +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A16-2-2/z.h b/cpp/autosar/test/rules/A16-2-2/z.h new file mode 100644 index 0000000000..22080b7884 --- /dev/null +++ b/cpp/autosar/test/rules/A16-2-2/z.h @@ -0,0 +1 @@ +#include "internal.h" \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.expected b/cpp/autosar/test/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.expected index 3952555595..ff53ffd841 100644 --- a/cpp/autosar/test/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.expected +++ b/cpp/autosar/test/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.expected @@ -19,3 +19,4 @@ | test.cpp:19:1:19:18 | #include | C library "uchar.h" is included instead of the corresponding C++ library . | | test.cpp:20:1:20:18 | #include | C library "wchar.h" is included instead of the corresponding C++ library . | | test.cpp:21:1:21:19 | #include | C library "wctype.h" is included instead of the corresponding C++ library . | +| test.cpp:45:1:45:17 | #include "time.h" | C library "time.h" is included instead of the corresponding C++ library . | diff --git a/cpp/autosar/test/rules/A18-0-1/lib/example.h b/cpp/autosar/test/rules/A18-0-1/lib/example.h new file mode 100644 index 0000000000..001980b02f --- /dev/null +++ b/cpp/autosar/test/rules/A18-0-1/lib/example.h @@ -0,0 +1,4 @@ +#ifndef LIB_EXAMPLE_H_ +#define LIB_EXAMPLE_H_ + +#endif \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-0-1/test.cpp b/cpp/autosar/test/rules/A18-0-1/test.cpp index b095017685..579842ddab 100644 --- a/cpp/autosar/test/rules/A18-0-1/test.cpp +++ b/cpp/autosar/test/rules/A18-0-1/test.cpp @@ -39,4 +39,7 @@ #include // COMPLIANT #include // COMPLIANT #include // COMPLIANT -#include // COMPLIANT \ No newline at end of file +#include // COMPLIANT + +#include "lib/example.h" // COMPLIANT +#include "time.h" // NON_COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-0-1/test.cpp.qcc b/cpp/autosar/test/rules/A18-0-1/test.cpp.qcc new file mode 100644 index 0000000000..ef665a3df5 --- /dev/null +++ b/cpp/autosar/test/rules/A18-0-1/test.cpp.qcc @@ -0,0 +1,41 @@ +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT +#include // NON_COMPLIANT + +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT +#include // COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-0-1/time.h b/cpp/autosar/test/rules/A18-0-1/time.h new file mode 100644 index 0000000000..ba58b95bbc --- /dev/null +++ b/cpp/autosar/test/rules/A18-0-1/time.h @@ -0,0 +1,4 @@ +#ifndef LIB_TIME_EXAMPLE_H_ +#define LIB_TIME_EXAMPLE_H_ +// may be a user lib or a std lib checked into a project +#endif \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-0-3/LocaleFunctionsUsed.expected b/cpp/autosar/test/rules/A18-0-3/LocaleFunctionsUsed.expected index f6bfa5f4d9..1521b1a9f4 100644 --- a/cpp/autosar/test/rules/A18-0-3/LocaleFunctionsUsed.expected +++ b/cpp/autosar/test/rules/A18-0-3/LocaleFunctionsUsed.expected @@ -1,10 +1,10 @@ -| test.cpp:4:3:4:16 | call to setlocale | Use of function 'std::setlocale'. | -| test.cpp:5:3:5:16 | call to setlocale | Use of function 'std::setlocale'. | -| test.cpp:6:3:6:16 | call to setlocale | Use of function 'std::setlocale'. | -| test.cpp:7:3:7:16 | call to setlocale | Use of function 'std::setlocale'. | -| test.cpp:8:3:8:16 | call to setlocale | Use of function 'std::setlocale'. | -| test.cpp:9:3:9:16 | call to setlocale | Use of function 'std::setlocale'. | -| test.cpp:10:20:10:34 | call to localeconv | Use of function 'std::localeconv'. | +| test.cpp:4:3:4:16 | call to setlocale | Use of function 'setlocale'. | +| test.cpp:5:3:5:16 | call to setlocale | Use of function 'setlocale'. | +| test.cpp:6:3:6:16 | call to setlocale | Use of function 'setlocale'. | +| test.cpp:7:3:7:16 | call to setlocale | Use of function 'setlocale'. | +| test.cpp:8:3:8:16 | call to setlocale | Use of function 'setlocale'. | +| test.cpp:9:3:9:16 | call to setlocale | Use of function 'setlocale'. | +| test.cpp:10:20:10:34 | call to localeconv | Use of function 'localeconv'. | | test.cpp:12:3:12:11 | call to setlocale | Use of function 'setlocale'. | | test.cpp:13:3:13:11 | call to setlocale | Use of function 'setlocale'. | | test.cpp:14:3:14:11 | call to setlocale | Use of function 'setlocale'. | diff --git a/cpp/autosar/test/rules/A18-0-3/LocaleTypeLConvUsed.expected b/cpp/autosar/test/rules/A18-0-3/LocaleTypeLConvUsed.expected index 4e93c94f9b..c83be2cb1c 100644 --- a/cpp/autosar/test/rules/A18-0-3/LocaleTypeLConvUsed.expected +++ b/cpp/autosar/test/rules/A18-0-3/LocaleTypeLConvUsed.expected @@ -1,2 +1,2 @@ -| test.cpp:10:8:10:12 | type mention | Use of type 'std::lconv'. | +| test.cpp:10:8:10:12 | type mention | Use of type 'lconv'. | | test.cpp:18:3:18:7 | type mention | Use of type 'lconv'. | diff --git a/cpp/autosar/test/rules/A18-1-1/test.cpp b/cpp/autosar/test/rules/A18-1-1/test.cpp index 21eb783717..0e9bffa3d7 100644 --- a/cpp/autosar/test/rules/A18-1-1/test.cpp +++ b/cpp/autosar/test/rules/A18-1-1/test.cpp @@ -10,5 +10,7 @@ int test_c_arrays() { int x[100]; // NON_COMPLIANT constexpr int a[]{0, 1, 2}; // NON_COMPLIANT + + __func__; // COMPLIANT return 0; -} \ No newline at end of file +} diff --git a/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.qlref b/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.qlref deleted file mode 100644 index 9f78cda4c6..0000000000 --- a/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A18-1-2/VectorboolSpecializationUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.testref b/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.testref new file mode 100644 index 0000000000..a934690acb --- /dev/null +++ b/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.expected b/cpp/autosar/test/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.expected index a96c3fb64f..bd46224da6 100644 --- a/cpp/autosar/test/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.expected +++ b/cpp/autosar/test/rules/A18-1-4/PointerToAnElementOfAnArrayPassedToASmartPointer.expected @@ -1,12 +1,22 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:26,67-75) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:27,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:39,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:50,34-42) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:50,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:58,25-33) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:70,3-16) edges -| test.cpp:3:36:3:45 | new[] | test.cpp:19:27:19:44 | call to allocate_int_array | -| test.cpp:3:36:3:45 | new[] | test.cpp:23:12:23:29 | call to allocate_int_array | -| test.cpp:3:36:3:45 | new[] | test.cpp:27:20:27:37 | call to allocate_int_array | -| test.cpp:11:29:11:41 | call to unique_ptr | test.cpp:12:30:12:36 | call to release | -| test.cpp:27:20:27:37 | call to allocate_int_array | test.cpp:32:12:32:20 | int_array | +| test.cpp:3:36:3:45 | new[] | test.cpp:19:27:19:44 | call to allocate_int_array | provenance | | +| test.cpp:3:36:3:45 | new[] | test.cpp:23:12:23:29 | call to allocate_int_array | provenance | | +| test.cpp:3:36:3:45 | new[] | test.cpp:27:20:27:37 | call to allocate_int_array | provenance | | +| test.cpp:11:29:11:41 | call to unique_ptr | test.cpp:12:27:12:28 | v2 | provenance | | +| test.cpp:12:27:12:28 | v2 | test.cpp:12:30:12:36 | call to release | provenance | | +| test.cpp:12:27:12:28 | v2 | test.cpp:12:30:12:36 | call to release | provenance | Config | +| test.cpp:27:20:27:37 | call to allocate_int_array | test.cpp:32:12:32:20 | int_array | provenance | | nodes | test.cpp:3:36:3:45 | new[] | semmle.label | new[] | | test.cpp:11:29:11:41 | call to unique_ptr | semmle.label | call to unique_ptr | +| test.cpp:12:27:12:28 | v2 | semmle.label | v2 | | test.cpp:12:30:12:36 | call to release | semmle.label | call to release | | test.cpp:19:27:19:44 | call to allocate_int_array | semmle.label | call to allocate_int_array | | test.cpp:23:12:23:29 | call to allocate_int_array | semmle.label | call to allocate_int_array | diff --git a/cpp/autosar/test/rules/A18-5-3/test.cpp b/cpp/autosar/test/rules/A18-5-3/test.cpp index 85fbbd8db8..9245681435 100644 --- a/cpp/autosar/test/rules/A18-5-3/test.cpp +++ b/cpp/autosar/test/rules/A18-5-3/test.cpp @@ -41,7 +41,7 @@ void test_single_array_item(bool do_array_delete) { c_ptr_array[5] = new char; if (do_array_delete) { - delete[] c_ptr_array[5]; // NON_COMPLIANT [FALSE_NEGATIVE] + delete[] c_ptr_array[5]; // NON_COMPLIANT[FALSE_NEGATIVE] } else { delete c_ptr_array[5]; // COMPLIANT } diff --git a/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.qlref b/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.qlref deleted file mode 100644 index 1f1e8258e4..0000000000 --- a/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.testref b/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.testref new file mode 100644 index 0000000000..4d1e21d4cb --- /dev/null +++ b/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.testref @@ -0,0 +1 @@ +cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.qlref b/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.qlref deleted file mode 100644 index 04cc5622dd..0000000000 --- a/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.testref b/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.testref new file mode 100644 index 0000000000..f2fcc2eded --- /dev/null +++ b/cpp/autosar/test/rules/A18-5-4/GlobalUnsizedOperatorDeleteNotDefined.testref @@ -0,0 +1 @@ +cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-5-5/test.cpp b/cpp/autosar/test/rules/A18-5-5/test.cpp index 73244c15a6..b125f2201a 100644 --- a/cpp/autosar/test/rules/A18-5-5/test.cpp +++ b/cpp/autosar/test/rules/A18-5-5/test.cpp @@ -11,7 +11,7 @@ void *malloc1(int b) { // NON_COMPLIANT - recursion return malloc1(b - 1); } -void *malloc2(int b) __attribute__((no_caller_saved_registers, __malloc__)); +void *malloc2(int b) __attribute__((__malloc__)); void *malloc2(int b) { // NON_COMPLIANT - execution doesn't depend on b for (int i = 0; i < 10; i++) { diff --git a/cpp/autosar/test/rules/A18-5-6/DynamicMemoryManagementFailureMode.expected b/cpp/autosar/test/rules/A18-5-6/DynamicMemoryManagementFailureMode.expected index 1a651c3632..3e2ddd3b39 100644 --- a/cpp/autosar/test/rules/A18-5-6/DynamicMemoryManagementFailureMode.expected +++ b/cpp/autosar/test/rules/A18-5-6/DynamicMemoryManagementFailureMode.expected @@ -1,4 +1,3 @@ | test.cpp:5:7:5:13 | malloc1 | (Audit) Function may be a memory allocation function and an analysis should be performed to analyze the failure modes of dynamic memory management. | | test.cpp:8:7:8:13 | malloc3 | (Audit) Function may be a memory allocation function and an analysis should be performed to analyze the failure modes of dynamic memory management. | | test.cpp:10:6:10:7 | h1 | (Audit) Function may be a memory allocation function and an analysis should be performed to analyze the failure modes of dynamic memory management. | -| test_msvc.cpp:3:29:3:35 | malloc5 | (Audit) Function may be a memory allocation function and an analysis should be performed to analyze the failure modes of dynamic memory management. | diff --git a/cpp/autosar/test/rules/A18-5-6/test.cpp b/cpp/autosar/test/rules/A18-5-6/test.cpp index 576dbc02fa..a9434ed1b5 100644 --- a/cpp/autosar/test/rules/A18-5-6/test.cpp +++ b/cpp/autosar/test/rules/A18-5-6/test.cpp @@ -4,7 +4,7 @@ void *malloc1(int b) __attribute__((malloc)); void *malloc1(int b) { return nullptr; } // NON_COMPLIANT -void *malloc3(int b) __attribute__((no_caller_saved_registers, __malloc__)); +void *malloc3(int b) __attribute__((__malloc__)); void *malloc3(int b) { return nullptr; } // NON_COMPLIANT void h1() {} // NON_COMPLIANT diff --git a/cpp/autosar/test/rules/A18-5-6/test_msvc.cpp b/cpp/autosar/test/rules/A18-5-6/test_msvc.cpp deleted file mode 100644 index ff8314a955..0000000000 --- a/cpp/autosar/test/rules/A18-5-6/test_msvc.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// semmle-extractor-options: --microsoft - -__declspec(allocator) void *malloc5(int b) { // NON_COMPLIANT - return nullptr; -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.expected b/cpp/autosar/test/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.expected index d9dd02c054..68cab835fa 100644 --- a/cpp/autosar/test/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.expected +++ b/cpp/autosar/test/rules/A18-5-8/UnnecessaryUseOfDynamicStorage.expected @@ -1,3 +1,8 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnnecessaryUseOfDynamicStorage.ql:55,34-42) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnnecessaryUseOfDynamicStorage.ql:58,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnnecessaryUseOfDynamicStorage.ql:60,26-34) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UnnecessaryUseOfDynamicStorage.ql:74,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (UnnecessaryUseOfDynamicStorage.ql:79,41-54) | test.cpp:17:17:17:29 | new | StructA object of size 8 bytes does not appear to outlive the function, but is created on the heap instead of the stack. | | test.cpp:21:17:21:32 | new[] | StructA[] object of size 800 bytes does not appear to outlive the function, but is created on the heap instead of the stack. | | test.cpp:35:20:35:44 | call to make_shared | StructA object of size 8 bytes does not appear to outlive the function, but is created on the heap instead of the stack. | diff --git a/cpp/autosar/test/rules/A18-5-8/test.cpp b/cpp/autosar/test/rules/A18-5-8/test.cpp index fcaf482777..3183810942 100644 --- a/cpp/autosar/test/rules/A18-5-8/test.cpp +++ b/cpp/autosar/test/rules/A18-5-8/test.cpp @@ -68,4 +68,13 @@ StructA *test_failure() { a = nullptr; } return a; +} + +#include +std::unique_ptr +test_for_fp_reported_in_20(const std::string &s) noexcept { + // make_unique performs heap allocation + // but this outlives the function due to copy elision + // (specifically NRVO) + return std::make_unique(s); // COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-9-1/BindUsed.expected b/cpp/autosar/test/rules/A18-9-1/BindUsed.expected index 7f936853ae..2db33a6fed 100644 --- a/cpp/autosar/test/rules/A18-9-1/BindUsed.expected +++ b/cpp/autosar/test/rules/A18-9-1/BindUsed.expected @@ -1,3 +1,3 @@ -| test.cpp:9:13:9:21 | call to bind | Prefer lambdas to using std::bind. | -| test.cpp:10:13:10:24 | call to bind1st | Prefer lambdas to using std::bind1st. | -| test.cpp:11:13:11:24 | call to bind2nd | Prefer lambdas to using std::bind2nd. | +| test.cpp:9:13:9:21 | call to bind | Prefer lambdas to using `bind`. | +| test.cpp:10:13:10:24 | call to bind1st | Prefer lambdas to using `bind1st`. | +| test.cpp:11:13:11:24 | call to bind2nd | Prefer lambdas to using `bind2nd`. | diff --git a/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.qlref b/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.qlref deleted file mode 100644 index 05bcab607a..0000000000 --- a/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A18-9-2/ForwardingValuesToOtherFunctions.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.testref b/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.testref new file mode 100644 index 0000000000..d56acb8415 --- /dev/null +++ b/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.testref @@ -0,0 +1 @@ +cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A18-9-4/ArgumentToForwardSubsequentlyUsed.expected b/cpp/autosar/test/rules/A18-9-4/ArgumentToForwardSubsequentlyUsed.expected index 1c72dd7bf3..9e1cf41d3d 100644 --- a/cpp/autosar/test/rules/A18-9-4/ArgumentToForwardSubsequentlyUsed.expected +++ b/cpp/autosar/test/rules/A18-9-4/ArgumentToForwardSubsequentlyUsed.expected @@ -1 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArgumentToForwardSubsequentlyUsed.ql:22,10-18) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArgumentToForwardSubsequentlyUsed.ql:24,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ArgumentToForwardSubsequentlyUsed.ql:24,30-38) | test.cpp:8:5:8:6 | t2 | The argument $@ of `std::forward` may be indeterminate when accessed at this location. | test.cpp:7:45:7:46 | t2 | t2 | diff --git a/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.expected b/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.expected index e69de29bb2..180e52c6f1 100644 --- a/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.expected +++ b/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticFunctionReusedInNamespace.expected @@ -0,0 +1,2 @@ +| test1a.cpp:13:13:13:14 | f1 | Static function $@ reuses identifier of $@ | test1a.cpp:13:13:13:14 | f1 | f1 | test1b.cpp:6:13:6:14 | f1 | f1 | +| test1b.cpp:6:13:6:14 | f1 | Static function $@ reuses identifier of $@ | test1b.cpp:6:13:6:14 | f1 | f1 | test1a.cpp:13:13:13:14 | f1 | f1 | diff --git a/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.expected b/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.expected index e69de29bb2..9eef8da1b5 100644 --- a/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.expected +++ b/cpp/autosar/test/rules/A2-10-4/IdentifierNameOfStaticNonMemberObjectReusedInNamespace.expected @@ -0,0 +1,2 @@ +| test1a.cpp:2:12:2:13 | v1 | Non-member static object $@ reuses identifier name of non-member static object $@ | test1a.cpp:2:12:2:13 | v1 | v1 | test1b.cpp:2:12:2:13 | v1 | v1 | +| test1b.cpp:2:12:2:13 | v1 | Non-member static object $@ reuses identifier name of non-member static object $@ | test1b.cpp:2:12:2:13 | v1 | v1 | test1a.cpp:2:12:2:13 | v1 | v1 | diff --git a/cpp/autosar/test/rules/A2-10-4/test1a.cpp b/cpp/autosar/test/rules/A2-10-4/test1a.cpp index 4c6ec898ed..8511fffa92 100644 --- a/cpp/autosar/test/rules/A2-10-4/test1a.cpp +++ b/cpp/autosar/test/rules/A2-10-4/test1a.cpp @@ -13,4 +13,10 @@ namespace ns3 { static void f1() {} void f2() {} + +// Variable templates can cause false positives +template static int number_one = 0; // COMPLIANT + +template <> static int number_one<1> = 1; // COMPLIANT +template <> static int number_one<2> = 2; // COMPLIANT } // namespace ns3 \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-10-4/test1b.cpp b/cpp/autosar/test/rules/A2-10-4/test1b.cpp index 49a01226c3..c8a0e8a4b5 100644 --- a/cpp/autosar/test/rules/A2-10-4/test1b.cpp +++ b/cpp/autosar/test/rules/A2-10-4/test1b.cpp @@ -3,7 +3,6 @@ static int v1 = 3; // NON_COMPLIANT } // namespace ns1 namespace ns3 { -static void f1() {} // NON_COMPLIANT - Not accepted by Clang linker and - // therefore not alerted upon. +static void f1() {} // NON_COMPLIANT - Not accepted by Clang linker void f2() {} // COMPLIANT - Not accepted by Clang linker -} // namespace ns3 \ No newline at end of file +} // namespace ns3 diff --git a/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfANonMemberObjectWithExternalOrInternalLinkageIsReused.expected b/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfANonMemberObjectWithExternalOrInternalLinkageIsReused.expected index d06e645044..d6f496a3c6 100644 --- a/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfANonMemberObjectWithExternalOrInternalLinkageIsReused.expected +++ b/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfANonMemberObjectWithExternalOrInternalLinkageIsReused.expected @@ -1,4 +1,6 @@ -| test1a.cpp:6:12:6:13 | g3 | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1a.cpp:6:12:6:13 | g3 | g3 | test1b.cpp:7:12:7:13 | g3 | g3 | -| test1a.cpp:17:43:17:43 | number_two | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1a.cpp:17:43:17:43 | number_two | number_two | test1b.cpp:12:43:12:43 | number_two | number_two | -| test1b.cpp:7:12:7:13 | g3 | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1b.cpp:7:12:7:13 | g3 | g3 | test1a.cpp:6:12:6:13 | g3 | g3 | -| test1b.cpp:12:43:12:43 | number_two | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1b.cpp:12:43:12:43 | number_two | number_two | test1a.cpp:17:43:17:43 | number_two | number_two | +| test1a.cpp:2:12:2:13 | g1 | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1a.cpp:2:12:2:13 | g1 | g1 | test1b.cpp:2:12:2:13 | g1 | g1 | +| test1a.cpp:6:12:6:13 | g3 | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1a.cpp:6:12:6:13 | g3 | g3 | test1b.cpp:6:12:6:13 | g3 | g3 | +| test1a.cpp:17:50:17:50 | number_two | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1a.cpp:17:50:17:50 | number_two | number_two | test1b.cpp:11:50:11:50 | number_two | number_two | +| test1b.cpp:2:12:2:13 | g1 | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1b.cpp:2:12:2:13 | g1 | g1 | test1a.cpp:2:12:2:13 | g1 | g1 | +| test1b.cpp:6:12:6:13 | g3 | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1b.cpp:6:12:6:13 | g3 | g3 | test1a.cpp:6:12:6:13 | g3 | g3 | +| test1b.cpp:11:50:11:50 | number_two | Identifier name of non-member object $@ reuses the identifier name of non-member object $@. | test1b.cpp:11:50:11:50 | number_two | number_two | test1a.cpp:17:50:17:50 | number_two | number_two | diff --git a/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfAStaticFunctionIsReused.expected b/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfAStaticFunctionIsReused.expected index d84cdee2b0..c9eea3450b 100644 --- a/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfAStaticFunctionIsReused.expected +++ b/cpp/autosar/test/rules/A2-10-5/IdentifierNameOfAStaticFunctionIsReused.expected @@ -1,2 +1,2 @@ -| test1a.cpp:7:13:7:14 | f1 | Identifier name of static function $@ reuses identifier name of static function $@ | test1a.cpp:7:13:7:14 | f1 | f1 | test1b.cpp:10:13:10:14 | f1 | f1 | -| test1b.cpp:10:13:10:14 | f1 | Identifier name of static function $@ reuses identifier name of static function $@ | test1b.cpp:10:13:10:14 | f1 | f1 | test1a.cpp:7:13:7:14 | f1 | f1 | +| test1a.cpp:7:13:7:14 | f1 | Identifier name of static function $@ reuses identifier name of static function $@ | test1a.cpp:7:13:7:14 | f1 | f1 | test1b.cpp:9:13:9:14 | f1 | f1 | +| test1b.cpp:9:13:9:14 | f1 | Identifier name of static function $@ reuses identifier name of static function $@ | test1b.cpp:9:13:9:14 | f1 | f1 | test1a.cpp:7:13:7:14 | f1 | f1 | diff --git a/cpp/autosar/test/rules/A2-10-5/test1a.cpp b/cpp/autosar/test/rules/A2-10-5/test1a.cpp index d8e6634da7..749ad38b0f 100644 --- a/cpp/autosar/test/rules/A2-10-5/test1a.cpp +++ b/cpp/autosar/test/rules/A2-10-5/test1a.cpp @@ -1,5 +1,5 @@ namespace n1 { -static int g1 = 0; +static int g1 = 0; // NON_COMPLIANT } static int g2; // COMPLIANT @@ -14,7 +14,7 @@ int test() { return number_one; } long test2() { return number_one; } -template constexpr T number_two = T(1); // NON_COMPLIANT +template static constexpr T number_two = T(1); // NON_COMPLIANT int test3() { return number_two; } diff --git a/cpp/autosar/test/rules/A2-10-5/test1b.cpp b/cpp/autosar/test/rules/A2-10-5/test1b.cpp index f292164478..342d739c4d 100644 --- a/cpp/autosar/test/rules/A2-10-5/test1b.cpp +++ b/cpp/autosar/test/rules/A2-10-5/test1b.cpp @@ -1,6 +1,5 @@ namespace n1 { -static int g1 = 1; // NON_COMPLIANT[FALSE_NEGATIVE], considered the same as - // n1::g1 in test1a.cpp. +static int g1 = 1; // NON_COMPLIANT } namespace n2 { @@ -9,8 +8,8 @@ static int g3 = 0; // NON_COMPLIANT static void f1() {} // NON_COMPLIANT -template constexpr T number_two = T(1); // NON_COMPLIANT +template static constexpr T number_two = T(1); // NON_COMPLIANT -int test3() { return number_two; } +int test5() { return number_two; } -long test4() { return number_two; } \ No newline at end of file +long test6() { return number_two; } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.qlref b/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.qlref deleted file mode 100644 index ce6347c955..0000000000 --- a/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A2-13-1/EscapeSequenceOutsideISO.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.testref b/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.testref new file mode 100644 index 0000000000..924122e38e --- /dev/null +++ b/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.testref @@ -0,0 +1 @@ +cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-3-1/CharacterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode.expected.gcc b/cpp/autosar/test/rules/A2-3-1/CharacterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode.expected.gcc new file mode 100644 index 0000000000..fd5bf9d0e2 --- /dev/null +++ b/cpp/autosar/test/rules/A2-3-1/CharacterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode.expected.gcc @@ -0,0 +1 @@ +| test.cpp:6:5:6:13 | l1_\\u00a8 | Declaration uses the character '\\u00a8' that is outside the language basic character set. | diff --git a/cpp/autosar/test/rules/A2-3-1/CharacterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode.expected.qcc b/cpp/autosar/test/rules/A2-3-1/CharacterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode.expected.qcc new file mode 100644 index 0000000000..fd5bf9d0e2 --- /dev/null +++ b/cpp/autosar/test/rules/A2-3-1/CharacterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode.expected.qcc @@ -0,0 +1 @@ +| test.cpp:6:5:6:13 | l1_\\u00a8 | Declaration uses the character '\\u00a8' that is outside the language basic character set. | diff --git a/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInComment.expected b/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInComment.expected index b5fd4c77cc..4df213e5c2 100644 --- a/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInComment.expected +++ b/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInComment.expected @@ -1,2 +1,2 @@ | test.cpp:3:1:3:37 | // Invalid character \u00ce\u00b1 NON_COMPLIANT | Comment uses the character '\u00ce\u00b1' that is outside the language basic character set. | -| test.cpp:10:1:12:2 | /*\nInvalid character \u00e2\u0086\u00a6 NON_COMPLIANT\n*/ | Comment uses the character '\u00e2\u0086\u00a6' that is outside the language basic character set. | +| test.cpp:12:1:14:2 | /*\nInvalid character \u00e2\u0086\u00a6 NON_COMPLIANT\n*/ | Comment uses the character '\u00e2\u0086\u00a6' that is outside the language basic character set. | diff --git a/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInStringLiteral.expected b/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInStringLiteral.expected index 3ad38685ba..fe21bce430 100644 --- a/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInStringLiteral.expected +++ b/cpp/autosar/test/rules/A2-3-1/InvalidCharacterInStringLiteral.expected @@ -1 +1 @@ -| test.cpp:7:20:7:22 | \u00ce\u00b1 | String literal uses the character '\u03b1' that is outside the language basic character set. | +| test.cpp:7:21:7:23 | \u00ce\u00b1 | String literal uses the character '\u03b1' that is outside the language basic character set. | diff --git a/cpp/autosar/test/rules/A2-3-1/test.cpp b/cpp/autosar/test/rules/A2-3-1/test.cpp index 5d1550f292..cc8b1b53ac 100644 --- a/cpp/autosar/test/rules/A2-3-1/test.cpp +++ b/cpp/autosar/test/rules/A2-3-1/test.cpp @@ -4,9 +4,15 @@ double α = 2.; // NON_COMPLIANT; U+03b1 void *to_𐆅_and_beyond = nullptr; // NON_COMPLIANT; U+10185 int l1_\u00A8; // COMPLIANT[FALSE_POSITIVE] -const char *euro = "α"; // NON_COMPLIANT +const char *euro1 = "α"; // NON_COMPLIANT +const wchar_t *euro2 = L"α"; // COMPLIANT +const char *euro3 = u8"α"; // COMPLIANT int valid; /* Invalid character ↦ NON_COMPLIANT +*/ + +/* +Valid character @ in comments COMPLIANT */ \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-3-1/test.cpp.gcc b/cpp/autosar/test/rules/A2-3-1/test.cpp.gcc new file mode 100644 index 0000000000..5cc8740b6f --- /dev/null +++ b/cpp/autosar/test/rules/A2-3-1/test.cpp.gcc @@ -0,0 +1,12 @@ +// It is valid to use @ in comments COMPLIANT + +// Invalid character α NON_COMPLIANT +// U+03b1 - this is compiler checked +// U+10185 - this is compiler checked +int l1_\u00A8; // COMPLIANT[FALSE_POSITIVE] +const char *euro = "α"; // NON_COMPLIANT + +int valid; +/* +Invalid character ↦ NON_COMPLIANT +*/ \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-3-1/test.cpp.qcc b/cpp/autosar/test/rules/A2-3-1/test.cpp.qcc new file mode 100644 index 0000000000..5cc8740b6f --- /dev/null +++ b/cpp/autosar/test/rules/A2-3-1/test.cpp.qcc @@ -0,0 +1,12 @@ +// It is valid to use @ in comments COMPLIANT + +// Invalid character α NON_COMPLIANT +// U+03b1 - this is compiler checked +// U+10185 - this is compiler checked +int l1_\u00A8; // COMPLIANT[FALSE_POSITIVE] +const char *euro = "α"; // NON_COMPLIANT + +int valid; +/* +Invalid character ↦ NON_COMPLIANT +*/ \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.qlref b/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.qlref deleted file mode 100644 index 876f24be61..0000000000 --- a/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A2-7-1/SingleLineCommentEndsWithSlash.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.testref b/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.testref new file mode 100644 index 0000000000..7874a476a0 --- /dev/null +++ b/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.testref @@ -0,0 +1 @@ +cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A2-7-3/UndocumentedUserDefinedType.expected b/cpp/autosar/test/rules/A2-7-3/UndocumentedUserDefinedType.expected index 77ede66305..d14f6e21f7 100644 --- a/cpp/autosar/test/rules/A2-7-3/UndocumentedUserDefinedType.expected +++ b/cpp/autosar/test/rules/A2-7-3/UndocumentedUserDefinedType.expected @@ -1,10 +1,12 @@ -| test.cpp:70:7:70:12 | definition of ClassD | Declaration entry for user-defined type ClassD is missing documentation. | -| test.cpp:72:7:72:7 | definition of a | Declaration entry for member variable a is missing documentation. | -| test.cpp:73:14:73:14 | declaration of b | Declaration entry for member variable b is missing documentation. | -| test.cpp:74:8:74:8 | declaration of f | Declaration entry for function f is missing documentation. | -| test.cpp:76:7:76:7 | definition of c | Declaration entry for member variable c is missing documentation. | -| test.cpp:78:6:78:6 | declaration of d | Declaration entry for function d is missing documentation. | -| test.cpp:81:6:81:6 | definition of e | Declaration entry for function e is missing documentation. | -| test.cpp:88:1:88:30 | definition of message_to_string_undocumented | Declaration entry for function message_to_string_undocumented is missing documentation. | -| test.cpp:113:14:113:14 | definition of operator() | Declaration entry for function operator() is missing documentation. | -| test.cpp:160:21:160:24 | definition of kBar | Declaration entry for member variable kBar is missing documentation. | +| test.cpp:74:8:74:8 | declaration of j | Declaration entry for function j is missing documentation. | +| test.cpp:75:8:75:8 | declaration of k | Declaration entry for function k is missing documentation. | +| test.cpp:90:7:90:12 | definition of ClassD | Declaration entry for user-defined type ClassD is missing documentation. | +| test.cpp:92:7:92:7 | definition of a | Declaration entry for member variable a is missing documentation. | +| test.cpp:93:14:93:14 | declaration of b | Declaration entry for member variable b is missing documentation. | +| test.cpp:94:8:94:8 | declaration of f | Declaration entry for function f is missing documentation. | +| test.cpp:96:7:96:7 | definition of c | Declaration entry for member variable c is missing documentation. | +| test.cpp:98:6:98:6 | declaration of d | Declaration entry for function d is missing documentation. | +| test.cpp:101:6:101:6 | definition of e | Declaration entry for function e is missing documentation. | +| test.cpp:108:1:108:30 | definition of message_to_string_undocumented | Declaration entry for function message_to_string_undocumented is missing documentation. | +| test.cpp:180:21:180:24 | definition of kBar | Declaration entry for member variable kBar is missing documentation. | +| test.cpp:227:14:227:17 | definition of foo3 | Declaration entry for function foo3 is missing documentation. | diff --git a/cpp/autosar/test/rules/A2-7-3/test.cpp b/cpp/autosar/test/rules/A2-7-3/test.cpp index b467e05c80..c062da4ee9 100644 --- a/cpp/autosar/test/rules/A2-7-3/test.cpp +++ b/cpp/autosar/test/rules/A2-7-3/test.cpp @@ -60,10 +60,30 @@ class ClassC { // COMPLIANT /// @param i an integer. /// @throw std::runtime_error void f(int i); // COMPLIANT + + /** Same documentation for all members + * This is a multiline comment. + */ + ///@{ + void g(); // COMPLIANT + void h(); // COMPLIANT + void i(); // COMPLIANT + ///@} + + ///@{ + void j(); // NON_COMPLIANT + void k(); // NON_COMPLIANT + /** Member-specific documentation */ + void l(); // COMPLIANT + ///@} + private: /// @brief A Doxygen comment. int c; // COMPLIANT }; +void ClassC::i() { // not flagged, as we will only flag the non-definition + // declaration +} /// A Doxygen comment. void c(); // COMPLIANT @@ -110,7 +130,7 @@ std::string template_function_test() { // COMPLIANT /// @brief function assigned_lambda_test. int assigned_lambda_test() { - auto l = [](int x, int y) { return x + y; }; // NON_COMPLIANT + auto l = [](int x, int y) { return x + y; }; // COMPLIANT: We exclude lambdas. return l(2, 3); } @@ -161,3 +181,58 @@ template class A2_7_3 final { }; /// @brief This is the instantiateA2_7_3 documentation void instantiateA2_7_3() { A2_7_3 instance; } + +/// Test documentation +void testFunctionScope() { + using my_float = float; + class ClassF { // COMPLIANT - in function scope + public: + int m_x; // COMPLIANT - in function scope + void fTest(); // COMPLIANT - in function scope + class ClassFNested { + public: + int m_nested_x; // COMPLIANT - in function scope + void fNestedTest(); // COMPLIANT - in function scope + }; + }; +} + +/// Test documentation +template class ClassG { // COMPLIANT +private: + /// Test documentation + int x; // COMPLIANT + +public: + /// Test documentation + friend int foo(ClassG g) { return g.x; } // COMPLIANT +}; + +/// Test documentation +void test() { // COMPLIANT + ClassG g; + foo(g); +} + +/// Test documentation +class ClassG2 { // COMPLIANT +public: + /// Test documentation + friend int foo2() { return 1; } // COMPLIANT +}; + +/// Test documentation +class ClassG3 { // COMPLIANT +public: + friend int foo3() { return 1; } // NON_COMPLIANT +}; + +/// @brief A Doxygen comment. +class ClassH { // COMPLIANT +public: + /// @brief Group with comment at the end. + ///@{ + void m(); // COMPLIANT + void n(); // COMPLIANT + ///@} End of group +}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A20-8-4/SharedPointerUsedWithNoOwnershipSharing.expected b/cpp/autosar/test/rules/A20-8-4/SharedPointerUsedWithNoOwnershipSharing.expected index f15f142b3b..5b770a1925 100644 --- a/cpp/autosar/test/rules/A20-8-4/SharedPointerUsedWithNoOwnershipSharing.expected +++ b/cpp/autosar/test/rules/A20-8-4/SharedPointerUsedWithNoOwnershipSharing.expected @@ -1,3 +1,4 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (SharedPointerUsedWithNoOwnershipSharing.ql:47,7-15) | test.cpp:14:24:14:26 | sp3 | The ownership of shared_ptr $@ is not shared within or passed out of the local scope of function $@. | test.cpp:14:24:14:26 | sp3 | sp3 | test.cpp:11:22:11:23 | f1 | f1 | | test.cpp:16:24:16:26 | sp5 | The ownership of shared_ptr $@ is not shared within or passed out of the local scope of function $@. | test.cpp:16:24:16:26 | sp5 | sp5 | test.cpp:11:22:11:23 | f1 | f1 | | test.cpp:17:24:17:26 | sp6 | The ownership of shared_ptr $@ is not shared within or passed out of the local scope of function $@. | test.cpp:17:24:17:26 | sp6 | sp6 | test.cpp:11:22:11:23 | f1 | f1 | diff --git a/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.clang b/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.clang new file mode 100644 index 0000000000..3d9afbbdae --- /dev/null +++ b/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.clang @@ -0,0 +1,14 @@ +| test.cpp:10:39:10:48 | call to __normal_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:12:38:12:43 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:13:38:13:42 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:15:44:15:49 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:16:44:16:48 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:19:41:19:50 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:22:47:22:57 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:25:8:25:16 | call to __normal_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:26:10:26:15 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:27:10:27:14 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:28:11:28:16 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:29:11:29:15 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:31:8:31:16 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:33:9:33:18 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | diff --git a/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.gcc b/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.gcc new file mode 100644 index 0000000000..3d9afbbdae --- /dev/null +++ b/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.gcc @@ -0,0 +1,14 @@ +| test.cpp:10:39:10:48 | call to __normal_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:12:38:12:43 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:13:38:13:42 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:15:44:15:49 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:16:44:16:48 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:19:41:19:50 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:22:47:22:57 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:25:8:25:16 | call to __normal_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:26:10:26:15 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:27:10:27:14 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:28:11:28:16 | call to cbegin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:29:11:29:15 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:31:8:31:16 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:33:9:33:18 | call to _Rb_tree_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | diff --git a/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.qcc b/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.qcc new file mode 100644 index 0000000000..39dd8073e7 --- /dev/null +++ b/cpp/autosar/test/rules/A23-0-1/IteratorImplicitlyConvertedToConstIterator.expected.qcc @@ -0,0 +1,10 @@ +| test.cpp:10:39:10:48 | call to __wrap_iter | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:13:38:13:42 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:16:44:16:48 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:19:41:19:50 | call to __map_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:22:47:22:57 | call to __map_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:25:8:25:16 | call to __wrap_iter | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:27:10:27:14 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:29:11:29:15 | call to begin | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:31:8:31:16 | call to __map_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | +| test.cpp:33:9:33:18 | call to __map_const_iterator | Non-const version of container call immediately converted to a `const_iterator`. | diff --git a/cpp/autosar/test/rules/A27-0-4/CStyleStringsUsed.expected b/cpp/autosar/test/rules/A27-0-4/CStyleStringsUsed.expected index 6184aad74e..555cb412b8 100644 --- a/cpp/autosar/test/rules/A27-0-4/CStyleStringsUsed.expected +++ b/cpp/autosar/test/rules/A27-0-4/CStyleStringsUsed.expected @@ -1,3 +1,6 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CStyleStringsUsed.ql:39,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CStyleStringsUsed.ql:39,23-31) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (CStyleStringsUsed.ql:39,47-55) | test.cpp:7:20:7:27 | CodeQL | Usage of C-style string in $@. | test.cpp:7:20:7:27 | CodeQL | expression | | test.cpp:7:20:7:27 | CodeQL | Usage of C-style string in $@. | test.cpp:16:16:16:17 | a1 | expression | | test.cpp:8:22:8:26 | call to c_str | Usage of C-style string in $@. | test.cpp:8:22:8:26 | call to c_str | expression | diff --git a/cpp/autosar/test/rules/A27-0-4/test.cpp b/cpp/autosar/test/rules/A27-0-4/test.cpp index 10ee885979..e73c37ea64 100644 --- a/cpp/autosar/test/rules/A27-0-4/test.cpp +++ b/cpp/autosar/test/rules/A27-0-4/test.cpp @@ -26,4 +26,5 @@ void f2() { f1(a1); f1(a2); f1(s.c_str()); // NON_COMPLIANT + __func__; } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.expected b/cpp/autosar/test/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.expected index 6611f6003f..d6c00ac4b5 100644 --- a/cpp/autosar/test/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.expected +++ b/cpp/autosar/test/rules/A3-1-5/NonTrivialNonTemplateFunctionDefinedInsideClassDefinition.expected @@ -1,2 +1 @@ -| test.cpp:12:7:12:13 | trivial | Non-Trivial or non-template function trivial is defined in the class body of $@. | test.cpp:2:7:2:7 | A | A | | test.cpp:26:7:26:9 | gcd | Non-Trivial or non-template function gcd is defined in the class body of $@. | test.cpp:2:7:2:7 | A | A | diff --git a/cpp/autosar/test/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.expected b/cpp/autosar/test/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.expected deleted file mode 100644 index af8a1d4588..0000000000 --- a/cpp/autosar/test/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.expected +++ /dev/null @@ -1,7 +0,0 @@ -| test.cpp:58:5:58:11 | getB | The trivial member function getB is not defined in the class body of $@. | test.cpp:2:7:2:7 | A | A | -| test.cpp:60:25:60:28 | d | The template member function d is not defined in the class body of $@. | test.cpp:2:7:2:7 | A | A | -| test.cpp:62:5:62:8 | b | The trivial member function b is not defined in the class body of $@. | test.cpp:2:7:2:7 | A | A | -| test.cpp:81:34:81:57 | complexCalculation | The template member function complexCalculation is not defined in the class body of $@. | test.cpp:64:29:64:29 | B | B | -| test.cpp:97:47:97:53 | d | The template member function d is not defined in the class body of $@. | test.cpp:64:29:64:29 | B | B | -| test.cpp:101:27:101:33 | b | The template member function b is not defined in the class body of $@. | test.cpp:64:29:64:29 | B | B | -| test.cpp:106:27:106:36 | getB | The template member function getB is not defined in the class body of $@. | test.cpp:64:29:64:29 | B | B | diff --git a/cpp/autosar/test/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.qlref b/cpp/autosar/test/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.qlref deleted file mode 100644 index c644147bb4..0000000000 --- a/cpp/autosar/test/rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A3-1-5/TrivialOrTemplateFunctionDefinedOutsideClassDefinition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A3-1-5/test.cpp b/cpp/autosar/test/rules/A3-1-5/test.cpp index eb5bc9edb7..e6db0d1190 100644 --- a/cpp/autosar/test/rules/A3-1-5/test.cpp +++ b/cpp/autosar/test/rules/A3-1-5/test.cpp @@ -9,7 +9,7 @@ class A { int getABar() { return 9; } - int trivial() { // NON_COMPLIANT + int not_trivial() { // COMPLIANT - with threshold of 10 loc ; ; ; @@ -23,11 +23,18 @@ class A { int complexCalculation(); - int gcd(int a, int b) { + int gcd(int a, int b) { // NON_COMPLIANT if (b == 0) return a; int result = gcd(b, (a % b)); ; + ; + ; + ; + ; + ; + ; + ; return result; } @@ -55,11 +62,11 @@ inline int A::complexCalculation() { // COMPLIANT return 1; } -int A::getB() { return 1; } // NON_COMPLIANT +int A::getB() { return 1; } // COMPLIANT -template T A::d(T t) { return t; } // NON_COMPLIANT +template T A::d(T t) { return t; } // COMPLIANT -int A::b() { return 3; } // NON_COMPLIANT +int A::b() { return 3; } // COMPLIANT template class B { public: @@ -76,9 +83,30 @@ template class B { template T d(T t); int complexCalculation(); + + int complexCalculation2() { // COMPLIANT - template + ; + ; + ; + ; + ; + ; + ; + ; + ; + ; + ; + ; + return 1; + } }; -template inline int B::complexCalculation() { // NON_COMPLIANT +void test_B() { + B b; + b.complexCalculation2(); +} + +template inline int B::complexCalculation() { // COMPLIANT ; ; ; @@ -94,16 +122,16 @@ template inline int B::complexCalculation() { // NON_COMPLIANT return 1; } -template template T B::d(T t) { // NON_COMPLIANT +template template T B::d(T t) { // COMPLIANT return t; } -template int B::b() { // NON_COMPLIANT +template int B::b() { // COMPLIANT C c; return 3; } -template int B::getB() { return 3; } // NON_COMPLIANT +template int B::getB() { return 3; } // COMPLIANT template class Foo { public: @@ -121,8 +149,29 @@ class FooBar { public: ~FooBar(); int f1(int a, int b); + + template int complexCalculation() { // COMPLIANT - template + ; + ; + ; + ; + ; + ; + ; + ; + ; + ; + ; + ; + return 1; + } }; +void test_FooBar() { + FooBar foobar; + foobar.complexCalculation(); +} + FooBar::~FooBar() {} // COMPLIANT want to ignore pImpl uses of destructors int FooBar::f1(int a, int b) { // COMPLIANT not a trivial function @@ -131,5 +180,12 @@ int FooBar::f1(int a, int b) { // COMPLIANT not a trivial function return a; int result = FooBar::f1(b, (a % b)); ; + ; + ; + ; + ; + ; + ; + ; } -} +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.expected b/cpp/autosar/test/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.expected index 8ec684e14c..e5657e9eec 100644 --- a/cpp/autosar/test/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.expected +++ b/cpp/autosar/test/rules/A3-3-1/ExternalLinkageNotDeclaredInHeaderFile.expected @@ -1,6 +1,6 @@ -| test.cpp:3:5:3:6 | definition of g1 | Externally linked object g1 not declared in header file. | -| test.cpp:4:12:4:13 | declaration of g2 | Externally linked object g2 not declared in header file. | -| test.cpp:10:5:10:6 | definition of l1 | Externally linked object l1 not declared in header file. | -| test.cpp:11:6:11:7 | definition of f1 | Externally linked function f1 not declared in header file. | -| test.cpp:22:5:22:5 | definition of f | Externally linked function f not declared in header file. | -| test.cpp:25:5:25:6 | declaration of f1 | Externally linked function f1 not declared in header file. | +| test.cpp:3:5:3:6 | definition of g1 | Externally linked object 'g1' not declared in header file. | +| test.cpp:4:12:4:13 | declaration of g2 | Externally linked object 'g2' not declared in header file. | +| test.cpp:10:5:10:6 | definition of l1 | Externally linked object 'l1' not declared in header file. | +| test.cpp:11:6:11:7 | definition of f1 | Externally linked function 'f1' not declared in header file. | +| test.cpp:22:5:22:5 | definition of f | Externally linked function 'f' not declared in header file. | +| test.cpp:25:5:25:6 | declaration of f1 | Externally linked function 'f1' not declared in header file. | diff --git a/cpp/autosar/test/rules/A3-3-1/test.cpp b/cpp/autosar/test/rules/A3-3-1/test.cpp index 194b06329c..992399f4c6 100644 --- a/cpp/autosar/test/rules/A3-3-1/test.cpp +++ b/cpp/autosar/test/rules/A3-3-1/test.cpp @@ -35,4 +35,8 @@ namespace n { void f5() { // COMPLIANT int i = 0; } -} // namespace n \ No newline at end of file +} // namespace n + +const int c = 1; // COMPLIANT - internal linkage +const char *const str2 = "foo"; // COMPLIANT - internal linkage +constexpr int k = 1; // COMPLIANT - internal linkage \ No newline at end of file diff --git a/cpp/autosar/test/rules/A3-9-1/VariableWidthIntegerTypesUsed.expected b/cpp/autosar/test/rules/A3-9-1/VariableWidthIntegerTypesUsed.expected index 4f748125d9..7609c76101 100644 --- a/cpp/autosar/test/rules/A3-9-1/VariableWidthIntegerTypesUsed.expected +++ b/cpp/autosar/test/rules/A3-9-1/VariableWidthIntegerTypesUsed.expected @@ -1,4 +1,3 @@ -| test.cpp:4:8:4:8 | c | Variable 'c' has variable-width type. | | test.cpp:5:17:5:18 | uc | Variable 'uc' has variable-width type. | | test.cpp:6:15:6:16 | sc | Variable 'sc' has variable-width type. | | test.cpp:8:7:8:7 | i | Variable 'i' has variable-width type. | @@ -12,3 +11,29 @@ | test.cpp:18:8:18:8 | l | Variable 'l' has variable-width type. | | test.cpp:19:17:19:18 | ul | Variable 'ul' has variable-width type. | | test.cpp:20:15:20:16 | sl | Variable 'sl' has variable-width type. | +| test.cpp:39:23:39:25 | uc1 | Variable 'uc1' has variable-width type. | +| test.cpp:40:21:40:23 | sc1 | Variable 'sc1' has variable-width type. | +| test.cpp:42:13:42:14 | i1 | Variable 'i1' has variable-width type. | +| test.cpp:43:22:43:24 | ui1 | Variable 'ui1' has variable-width type. | +| test.cpp:44:18:44:19 | u1 | Variable 'u1' has variable-width type. | +| test.cpp:45:20:45:22 | si1 | Variable 'si1' has variable-width type. | +| test.cpp:46:16:46:17 | s1 | Variable 's1' has variable-width type. | +| test.cpp:48:15:48:17 | sh1 | Variable 'sh1' has variable-width type. | +| test.cpp:49:24:49:27 | ush1 | Variable 'ush1' has variable-width type. | +| test.cpp:50:22:50:25 | ssh1 | Variable 'ssh1' has variable-width type. | +| test.cpp:52:14:52:15 | l1 | Variable 'l1' has variable-width type. | +| test.cpp:53:23:53:25 | ul1 | Variable 'ul1' has variable-width type. | +| test.cpp:54:21:54:23 | sl1 | Variable 'sl1' has variable-width type. | +| test.cpp:57:26:57:28 | uc2 | Variable 'uc2' has variable-width type. | +| test.cpp:58:24:58:26 | sc2 | Variable 'sc2' has variable-width type. | +| test.cpp:60:16:60:17 | i2 | Variable 'i2' has variable-width type. | +| test.cpp:61:25:61:27 | ui2 | Variable 'ui2' has variable-width type. | +| test.cpp:62:21:62:22 | u2 | Variable 'u2' has variable-width type. | +| test.cpp:63:23:63:25 | si2 | Variable 'si2' has variable-width type. | +| test.cpp:64:19:64:20 | s2 | Variable 's2' has variable-width type. | +| test.cpp:66:18:66:20 | sh2 | Variable 'sh2' has variable-width type. | +| test.cpp:67:27:67:30 | ush2 | Variable 'ush2' has variable-width type. | +| test.cpp:68:25:68:28 | ssh2 | Variable 'ssh2' has variable-width type. | +| test.cpp:70:17:70:18 | l2 | Variable 'l2' has variable-width type. | +| test.cpp:71:26:71:28 | ul2 | Variable 'ul2' has variable-width type. | +| test.cpp:72:24:72:26 | sl2 | Variable 'sl2' has variable-width type. | diff --git a/cpp/autosar/test/rules/A3-9-1/VariableWidthPlainCharTypeUsed.expected b/cpp/autosar/test/rules/A3-9-1/VariableWidthPlainCharTypeUsed.expected new file mode 100644 index 0000000000..6631606cbf --- /dev/null +++ b/cpp/autosar/test/rules/A3-9-1/VariableWidthPlainCharTypeUsed.expected @@ -0,0 +1,3 @@ +| test.cpp:4:8:4:8 | c | Variable 'c' has variable-width char type. | +| test.cpp:38:14:38:15 | c1 | Variable 'c1' has variable-width char type. | +| test.cpp:56:17:56:18 | c2 | Variable 'c2' has variable-width char type. | diff --git a/cpp/autosar/test/rules/A3-9-1/VariableWidthPlainCharTypeUsed.qlref b/cpp/autosar/test/rules/A3-9-1/VariableWidthPlainCharTypeUsed.qlref new file mode 100644 index 0000000000..b76c61f4c7 --- /dev/null +++ b/cpp/autosar/test/rules/A3-9-1/VariableWidthPlainCharTypeUsed.qlref @@ -0,0 +1 @@ +rules/A3-9-1/VariableWidthPlainCharTypeUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A3-9-1/test.cpp b/cpp/autosar/test/rules/A3-9-1/test.cpp index 8d2ef48523..7ffb87ca39 100644 --- a/cpp/autosar/test/rules/A3-9-1/test.cpp +++ b/cpp/autosar/test/rules/A3-9-1/test.cpp @@ -3,7 +3,7 @@ void test_variable_width_type_variables() { char c; // NON_COMPLIANT unsigned char uc; // NON_COMPLIANT - signed char sc; // NON_COMPLIANt + signed char sc; // NON_COMPLIANT int i; // NON_COMPLIANT unsigned int ui; // NON_COMPLIANT @@ -32,4 +32,58 @@ void test_variable_width_type_variables() { int main(int argc, char *argv[]) { // COMPLIANT // main as an exception -} \ No newline at end of file +} + +void test_variable_width_type_qualified_variables() { + const char c1 = 0; // NON_COMPLIANT + const unsigned char uc1 = 0; // NON_COMPLIANT + const signed char sc1 = 0; // NON_COMPLIANt + + const int i1 = 0; // NON_COMPLIANT + const unsigned int ui1 = 0; // NON_COMPLIANT + const unsigned u1 = 0; // NON_COMPLIANT + const signed int si1 = 0; // NON_COMPLIANT + const signed s1 = 0; // NON_COMPLIANT + + const short sh1 = 0; // NON_COMPLIANT + const unsigned short ush1 = 0; // NON_COMPLIANT + const signed short ssh1 = 0; // NON_COMPLIANT + + const long l1 = 0; // NON_COMPLIANT + const unsigned long ul1 = 0; // NON_COMPLIANT + const signed long sl1 = 0; // NON_COMPLIANT + + volatile char c2; // NON_COMPLIANT + volatile unsigned char uc2; // NON_COMPLIANT + volatile signed char sc2; // NON_COMPLIANt + + volatile int i2; // NON_COMPLIANT + volatile unsigned int ui2; // NON_COMPLIANT + volatile unsigned u2; // NON_COMPLIANT + volatile signed int si2; // NON_COMPLIANT + volatile signed s2; // NON_COMPLIANT + + volatile short sh2; // NON_COMPLIANT + volatile unsigned short ush2; // NON_COMPLIANT + volatile signed short ssh2; // NON_COMPLIANT + + volatile long l2; // NON_COMPLIANT + volatile unsigned long ul2; // NON_COMPLIANT + volatile signed long sl2; // NON_COMPLIANT +} + +struct test_fix_fp_614 { + test_fix_fp_614 operator++(int); // COMPLIANT + test_fix_fp_614 operator--(int); // COMPLIANT +}; + +// COMPLIANT - instantiated with Fixed Width Types. +template constexpr void test_fix_fp_540(MyType value) { + value++; +} + +int call_test_fix_fp_540() { + test_fix_fp_540(19); + test_fix_fp_540(20); + return 0; +} diff --git a/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.qlref b/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.qlref deleted file mode 100644 index d836b834b3..0000000000 --- a/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A4-10-1/NullPointerConstantNotNullptr.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.testref b/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.testref new file mode 100644 index 0000000000..aeb655a341 --- /dev/null +++ b/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected b/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected index a813b23223..8ece6b3dd1 100644 --- a/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected +++ b/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected @@ -1,96 +1,104 @@ -| enum.cpp:21:3:21:16 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:21:3:21:8 | Avenue | expression | -| enum.cpp:21:3:21:16 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:21:12:21:16 | Place | expression | -| enum.cpp:22:3:22:15 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:22:3:22:7 | Place | expression | -| enum.cpp:22:3:22:15 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:22:11:22:15 | Place | expression | -| enum.cpp:23:3:23:9 | - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:23:4:23:9 | Avenue | expression | -| enum.cpp:24:3:24:10 | ... % ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:24:3:24:6 | Road | expression | -| enum.cpp:25:3:25:12 | ... / ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:25:3:25:8 | Avenue | expression | -| enum.cpp:26:3:26:15 | ... * ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:26:3:26:11 | Boulevard | expression | -| enum.cpp:29:3:29:13 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:29:3:29:6 | Lane | expression | -| enum.cpp:29:3:29:13 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:29:10:29:13 | Road | expression | -| enum.cpp:30:3:30:15 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:30:3:30:7 | Place | expression | -| enum.cpp:30:3:30:15 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:30:12:30:15 | Lane | expression | -| enum.cpp:31:3:31:7 | ! ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:31:4:31:7 | Road | expression | -| enum.cpp:34:3:34:23 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:34:3:34:11 | Boulevard | expression | -| enum.cpp:34:3:34:23 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:34:15:34:23 | Boulevard | expression | -| enum.cpp:35:3:35:7 | ~ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:35:4:35:7 | Lane | expression | -| enum.cpp:36:3:36:14 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:36:3:36:7 | Place | expression | -| enum.cpp:36:3:36:14 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:36:11:36:14 | Road | expression | -| enum.cpp:37:3:37:11 | ... >> ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:37:3:37:6 | Road | expression | -| enum.cpp:38:3:38:11 | ... << ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:38:3:38:6 | Road | expression | -| enum.cpp:39:10:39:10 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:39:3:39:8 | Avenue | expression | -| enum.cpp:39:10:39:10 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:39:13:39:17 | Place | expression | -| enum.cpp:40:8:40:8 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:40:3:40:6 | Road | expression | -| enum.cpp:40:8:40:8 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:40:11:40:14 | Road | expression | -| enum.cpp:41:8:41:8 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:41:3:41:6 | Road | expression | -| enum.cpp:41:8:41:8 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:41:12:41:15 | Road | expression | -| enum.cpp:42:8:42:8 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:42:3:42:6 | Road | expression | -| enum.cpp:42:8:42:8 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:42:12:42:15 | Road | expression | -| enum.cpp:57:3:57:7 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:57:3:57:3 | a | expression | -| enum.cpp:57:3:57:7 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:57:7:57:7 | a | expression | -| enum.cpp:58:3:58:7 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:58:3:58:3 | a | expression | -| enum.cpp:58:3:58:7 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:58:7:58:7 | a | expression | -| enum.cpp:59:3:59:4 | - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:59:4:59:4 | a | expression | -| enum.cpp:60:3:60:7 | ... % ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:60:3:60:3 | a | expression | -| enum.cpp:61:3:61:7 | ... / ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:61:3:61:3 | a | expression | -| enum.cpp:62:3:62:7 | ... * ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:62:3:62:3 | a | expression | -| enum.cpp:65:3:65:7 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:65:3:65:3 | a | expression | -| enum.cpp:65:3:65:7 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:65:7:65:7 | b | expression | -| enum.cpp:66:3:66:8 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:66:3:66:3 | a | expression | -| enum.cpp:66:3:66:8 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:66:8:66:8 | b | expression | -| enum.cpp:67:3:67:4 | ! ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:67:4:67:4 | b | expression | -| enum.cpp:70:3:70:7 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:70:3:70:3 | a | expression | -| enum.cpp:70:3:70:7 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:70:7:70:7 | b | expression | -| enum.cpp:71:3:71:4 | ~ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:71:4:71:4 | a | expression | -| enum.cpp:72:3:72:7 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:72:3:72:3 | a | expression | -| enum.cpp:72:3:72:7 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:72:7:72:7 | b | expression | -| enum.cpp:73:3:73:8 | ... >> ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:73:3:73:3 | a | expression | -| enum.cpp:74:3:74:8 | ... << ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:74:3:74:3 | a | expression | -| enum.cpp:75:5:75:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:75:3:75:3 | a | expression | -| enum.cpp:75:5:75:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:75:8:75:8 | b | expression | -| enum.cpp:76:5:76:5 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:76:3:76:3 | a | expression | -| enum.cpp:77:5:77:5 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:77:3:77:3 | a | expression | -| enum.cpp:78:5:78:5 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:78:3:78:3 | a | expression | -| enum_class.cpp:49:22:49:22 | call to operator+ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:49:3:49:20 | ML | expression | -| enum_class.cpp:50:23:50:23 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:50:3:50:21 | SML | expression | -| enum_class.cpp:50:23:50:23 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:50:25:50:42 | ML | expression | -| enum_class.cpp:51:3:51:3 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:51:4:51:26 | Haskell | expression | -| enum_class.cpp:52:26:52:26 | call to operator% | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:52:3:52:24 | Racket | expression | -| enum_class.cpp:53:23:53:23 | call to operator/ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:53:3:53:21 | Elm | expression | -| enum_class.cpp:54:26:54:26 | call to operator* | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:54:3:54:24 | Scheme | expression | -| enum_class.cpp:57:27:57:27 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:57:3:57:25 | Haskell | expression | -| enum_class.cpp:57:27:57:27 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:57:29:57:47 | Elm | expression | -| enum_class.cpp:58:24:58:24 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:58:3:58:22 | Lisp | expression | -| enum_class.cpp:58:24:58:24 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:58:27:58:48 | Racket | expression | -| enum_class.cpp:59:3:59:3 | call to operator! | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:59:4:59:25 | Scheme | expression | -| enum_class.cpp:62:23:62:23 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:62:3:62:21 | Elm | expression | -| enum_class.cpp:62:23:62:23 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:62:25:62:46 | Racket | expression | -| enum_class.cpp:63:3:63:3 | call to operator~ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:63:4:63:24 | Idris | expression | -| enum_class.cpp:64:22:64:22 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:64:3:64:20 | ML | expression | -| enum_class.cpp:64:22:64:22 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:64:24:64:44 | OCaml | expression | -| enum_class.cpp:65:25:65:25 | call to operator>> | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:65:3:65:23 | OCaml | expression | -| enum_class.cpp:66:24:66:24 | call to operator<< | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:66:3:66:22 | Lisp | expression | -| enum_class.cpp:67:5:67:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:67:3:67:3 | l | expression | -| enum_class.cpp:67:5:67:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:67:8:67:28 | OCaml | expression | -| enum_class.cpp:68:5:68:5 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:68:3:68:3 | l | expression | -| enum_class.cpp:69:5:69:5 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:69:3:69:3 | l | expression | -| enum_class.cpp:70:5:70:5 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:70:3:70:3 | l | expression | -| enum_class.cpp:85:5:85:5 | call to operator+ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:85:3:85:3 | a | expression | -| enum_class.cpp:86:5:86:5 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:86:3:86:3 | a | expression | -| enum_class.cpp:86:5:86:5 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:86:7:86:7 | b | expression | -| enum_class.cpp:87:3:87:3 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:87:4:87:4 | a | expression | -| enum_class.cpp:88:5:88:5 | call to operator% | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:88:3:88:3 | a | expression | -| enum_class.cpp:89:5:89:5 | call to operator/ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:89:3:89:3 | a | expression | -| enum_class.cpp:90:5:90:5 | call to operator* | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:90:3:90:3 | b | expression | -| enum_class.cpp:93:5:93:5 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:93:3:93:3 | a | expression | -| enum_class.cpp:93:5:93:5 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:93:7:93:7 | b | expression | -| enum_class.cpp:94:5:94:5 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:94:3:94:3 | a | expression | -| enum_class.cpp:94:5:94:5 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:94:8:94:8 | b | expression | -| enum_class.cpp:95:3:95:3 | call to operator! | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:95:4:95:4 | a | expression | -| enum_class.cpp:98:5:98:5 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:98:3:98:3 | a | expression | -| enum_class.cpp:98:5:98:5 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:98:7:98:7 | b | expression | -| enum_class.cpp:99:3:99:3 | call to operator~ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:99:4:99:4 | a | expression | -| enum_class.cpp:100:5:100:5 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:100:3:100:3 | a | expression | -| enum_class.cpp:100:5:100:5 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:100:7:100:7 | b | expression | -| enum_class.cpp:101:5:101:5 | call to operator>> | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:101:3:101:3 | a | expression | -| enum_class.cpp:102:5:102:5 | call to operator<< | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:102:3:102:3 | a | expression | +| enum.cpp:20:3:20:8 | Avenue | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:20:12:20:16 | Place | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:21:3:21:7 | Place | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:21:11:21:15 | Place | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:22:4:22:9 | Avenue | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:23:3:23:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:24:3:24:8 | Avenue | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:25:3:25:11 | Boulevard | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:28:3:28:6 | Lane | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:28:10:28:13 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:29:3:29:7 | Place | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:29:12:29:15 | Lane | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:30:4:30:7 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:33:3:33:11 | Boulevard | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:33:15:33:23 | Boulevard | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:34:4:34:7 | Lane | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:35:3:35:7 | Place | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:35:11:35:14 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:36:3:36:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:37:3:37:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:38:3:38:8 | Avenue | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:38:13:38:17 | Place | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:39:3:39:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:39:11:39:14 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:40:3:40:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:40:12:40:15 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:41:3:41:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:41:12:41:15 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:42:3:42:6 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:42:9:42:12 | Road | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:57:3:57:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:57:7:57:7 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:58:3:58:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:58:7:58:7 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:59:4:59:4 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:60:3:60:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:61:3:61:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:62:3:62:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:65:3:65:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:65:7:65:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:66:3:66:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:66:8:66:8 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:67:4:67:4 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:70:3:70:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:70:7:70:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:71:4:71:4 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:72:3:72:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:72:7:72:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:73:3:73:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:74:3:74:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:75:3:75:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:75:8:75:8 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:76:3:76:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:77:3:77:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:78:3:78:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:79:3:79:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum.cpp:79:6:79:6 | b | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:1:6:1:11 | Street | Street | +| enum_class.cpp:48:3:48:20 | ML | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:49:3:49:21 | SML | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:49:25:49:42 | ML | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:50:4:50:26 | Haskell | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:51:3:51:24 | Racket | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:52:3:52:21 | Elm | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:53:3:53:24 | Scheme | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:56:3:56:25 | Haskell | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:56:29:56:47 | Elm | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:57:3:57:22 | Lisp | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:57:27:57:48 | Racket | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:58:4:58:25 | Scheme | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:61:3:61:21 | Elm | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:61:25:61:46 | Racket | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:62:4:62:24 | Idris | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:63:3:63:20 | ML | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:63:24:63:44 | OCaml | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:64:3:64:23 | OCaml | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:65:3:65:22 | Lisp | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:66:3:66:3 | l | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:66:8:66:28 | OCaml | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:67:3:67:3 | l | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:68:3:68:3 | l | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:69:3:69:3 | l | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:70:3:70:24 | FSharp | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:70:27:70:47 | OCaml | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:85:3:85:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:86:3:86:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:86:7:86:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:87:4:87:4 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:88:3:88:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:89:3:89:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:90:3:90:3 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:93:3:93:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:93:7:93:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:94:3:94:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:94:8:94:8 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:95:4:95:4 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:98:3:98:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:98:7:98:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:99:4:99:4 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:100:3:100:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:100:7:100:7 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:101:3:101:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:102:3:102:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:103:3:103:3 | a | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | +| enum_class.cpp:103:6:103:6 | b | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:1:12:1:25 | FunctionalLang | FunctionalLang | diff --git a/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected.gcc b/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected.gcc new file mode 100644 index 0000000000..a264afb5f3 --- /dev/null +++ b/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected.gcc @@ -0,0 +1,93 @@ +| enum.cpp:21:3:21:16 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:21:3:21:8 | Avenue | expression | +| enum.cpp:21:3:21:16 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:21:12:21:16 | Place | expression | +| enum.cpp:22:3:22:15 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:22:3:22:7 | Place | expression | +| enum.cpp:22:3:22:15 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:22:11:22:15 | Place | expression | +| enum.cpp:23:3:23:9 | - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:23:4:23:9 | Avenue | expression | +| enum.cpp:24:3:24:10 | ... % ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:24:3:24:6 | Road | expression | +| enum.cpp:25:3:25:12 | ... / ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:25:3:25:8 | Avenue | expression | +| enum.cpp:26:3:26:15 | ... * ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:26:3:26:11 | Boulevard | expression | +| enum.cpp:29:3:29:13 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:29:3:29:6 | Lane | expression | +| enum.cpp:29:3:29:13 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:29:10:29:13 | Road | expression | +| enum.cpp:30:3:30:15 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:30:3:30:7 | Place | expression | +| enum.cpp:30:3:30:15 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:30:12:30:15 | Lane | expression | +| enum.cpp:31:3:31:7 | ! ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:31:4:31:7 | Road | expression | +| enum.cpp:34:3:34:23 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:34:3:34:11 | Boulevard | expression | +| enum.cpp:34:3:34:23 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:34:15:34:23 | Boulevard | expression | +| enum.cpp:35:3:35:7 | ~ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:35:4:35:7 | Lane | expression | +| enum.cpp:36:3:36:14 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:36:3:36:7 | Place | expression | +| enum.cpp:36:3:36:14 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:36:11:36:14 | Road | expression | +| enum.cpp:37:3:37:11 | ... >> ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:37:3:37:6 | Road | expression | +| enum.cpp:38:3:38:11 | ... << ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:38:3:38:6 | Road | expression | +| enum.cpp:39:10:39:10 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:39:3:39:8 | Avenue | expression | +| enum.cpp:39:10:39:10 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:39:13:39:17 | Place | expression | +| enum.cpp:40:8:40:8 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:40:3:40:6 | Road | expression | +| enum.cpp:40:8:40:8 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:40:11:40:14 | Road | expression | +| enum.cpp:41:8:41:8 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:41:3:41:6 | Road | expression | +| enum.cpp:41:8:41:8 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:41:12:41:15 | Road | expression | +| enum.cpp:42:8:42:8 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:42:3:42:6 | Road | expression | +| enum.cpp:42:8:42:8 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:42:12:42:15 | Road | expression | +| enum.cpp:57:3:57:7 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:57:3:57:3 | a | expression | +| enum.cpp:57:3:57:7 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:57:7:57:7 | a | expression | +| enum.cpp:58:3:58:7 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:58:3:58:3 | a | expression | +| enum.cpp:58:3:58:7 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:58:7:58:7 | a | expression | +| enum.cpp:59:3:59:4 | - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:59:4:59:4 | a | expression | +| enum.cpp:60:3:60:7 | ... % ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:60:3:60:3 | a | expression | +| enum.cpp:61:3:61:7 | ... / ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:61:3:61:3 | a | expression | +| enum.cpp:62:3:62:7 | ... * ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:62:3:62:3 | a | expression | +| enum.cpp:65:3:65:7 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:65:3:65:3 | a | expression | +| enum.cpp:65:3:65:7 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:65:7:65:7 | b | expression | +| enum.cpp:66:3:66:8 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:66:3:66:3 | a | expression | +| enum.cpp:66:3:66:8 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:66:8:66:8 | b | expression | +| enum.cpp:67:3:67:4 | ! ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:67:4:67:4 | b | expression | +| enum.cpp:70:3:70:7 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:70:3:70:3 | a | expression | +| enum.cpp:70:3:70:7 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:70:7:70:7 | b | expression | +| enum.cpp:71:3:71:4 | ~ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:71:4:71:4 | a | expression | +| enum.cpp:72:3:72:7 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:72:3:72:3 | a | expression | +| enum.cpp:72:3:72:7 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:72:7:72:7 | b | expression | +| enum.cpp:73:3:73:8 | ... >> ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:73:3:73:3 | a | expression | +| enum.cpp:74:3:74:8 | ... << ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:74:3:74:3 | a | expression | +| enum.cpp:75:5:75:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:75:3:75:3 | a | expression | +| enum.cpp:75:5:75:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:75:8:75:8 | b | expression | +| enum_class.cpp:49:22:49:22 | call to operator+ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:49:3:49:20 | ML | expression | +| enum_class.cpp:50:23:50:23 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:50:3:50:21 | SML | expression | +| enum_class.cpp:50:23:50:23 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:50:25:50:42 | ML | expression | +| enum_class.cpp:51:3:51:3 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:51:4:51:26 | Haskell | expression | +| enum_class.cpp:52:26:52:26 | call to operator% | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:52:3:52:24 | Racket | expression | +| enum_class.cpp:53:23:53:23 | call to operator/ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:53:3:53:21 | Elm | expression | +| enum_class.cpp:54:26:54:26 | call to operator* | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:54:3:54:24 | Scheme | expression | +| enum_class.cpp:57:27:57:27 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:57:3:57:25 | Haskell | expression | +| enum_class.cpp:57:27:57:27 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:57:29:57:47 | Elm | expression | +| enum_class.cpp:58:24:58:24 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:58:3:58:22 | Lisp | expression | +| enum_class.cpp:58:24:58:24 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:58:27:58:48 | Racket | expression | +| enum_class.cpp:59:3:59:3 | call to operator! | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:59:4:59:25 | Scheme | expression | +| enum_class.cpp:62:23:62:23 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:62:3:62:21 | Elm | expression | +| enum_class.cpp:62:23:62:23 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:62:25:62:46 | Racket | expression | +| enum_class.cpp:63:3:63:3 | call to operator~ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:63:4:63:24 | Idris | expression | +| enum_class.cpp:64:22:64:22 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:64:3:64:20 | ML | expression | +| enum_class.cpp:64:22:64:22 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:64:24:64:44 | OCaml | expression | +| enum_class.cpp:65:25:65:25 | call to operator>> | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:65:3:65:23 | OCaml | expression | +| enum_class.cpp:66:24:66:24 | call to operator<< | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:66:3:66:22 | Lisp | expression | +| enum_class.cpp:67:5:67:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:67:3:67:3 | l | expression | +| enum_class.cpp:67:5:67:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:67:8:67:28 | OCaml | expression | +| enum_class.cpp:68:5:68:5 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:68:3:68:3 | l | expression | +| enum_class.cpp:69:5:69:5 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:69:3:69:3 | l | expression | +| enum_class.cpp:70:5:70:5 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:70:3:70:3 | l | expression | +| enum_class.cpp:85:5:85:5 | call to operator+ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:85:3:85:3 | a | expression | +| enum_class.cpp:86:5:86:5 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:86:3:86:3 | a | expression | +| enum_class.cpp:86:5:86:5 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:86:7:86:7 | b | expression | +| enum_class.cpp:87:3:87:3 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:87:4:87:4 | a | expression | +| enum_class.cpp:88:5:88:5 | call to operator% | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:88:3:88:3 | a | expression | +| enum_class.cpp:89:5:89:5 | call to operator/ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:89:3:89:3 | a | expression | +| enum_class.cpp:90:5:90:5 | call to operator* | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:90:3:90:3 | b | expression | +| enum_class.cpp:93:5:93:5 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:93:3:93:3 | a | expression | +| enum_class.cpp:93:5:93:5 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:93:7:93:7 | b | expression | +| enum_class.cpp:94:5:94:5 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:94:3:94:3 | a | expression | +| enum_class.cpp:94:5:94:5 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:94:8:94:8 | b | expression | +| enum_class.cpp:95:3:95:3 | call to operator! | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:95:4:95:4 | a | expression | +| enum_class.cpp:98:5:98:5 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:98:3:98:3 | a | expression | +| enum_class.cpp:98:5:98:5 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:98:7:98:7 | b | expression | +| enum_class.cpp:99:3:99:3 | call to operator~ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:99:4:99:4 | a | expression | +| enum_class.cpp:100:5:100:5 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:100:3:100:3 | a | expression | +| enum_class.cpp:100:5:100:5 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:100:7:100:7 | b | expression | +| enum_class.cpp:101:5:101:5 | call to operator>> | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:101:3:101:3 | a | expression | +| enum_class.cpp:102:5:102:5 | call to operator<< | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:102:3:102:3 | a | expression | diff --git a/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected.qcc b/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected.qcc new file mode 100644 index 0000000000..a264afb5f3 --- /dev/null +++ b/cpp/autosar/test/rules/A4-5-1/EnumUsedInArithmeticContexts.expected.qcc @@ -0,0 +1,93 @@ +| enum.cpp:21:3:21:16 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:21:3:21:8 | Avenue | expression | +| enum.cpp:21:3:21:16 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:21:12:21:16 | Place | expression | +| enum.cpp:22:3:22:15 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:22:3:22:7 | Place | expression | +| enum.cpp:22:3:22:15 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:22:11:22:15 | Place | expression | +| enum.cpp:23:3:23:9 | - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:23:4:23:9 | Avenue | expression | +| enum.cpp:24:3:24:10 | ... % ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:24:3:24:6 | Road | expression | +| enum.cpp:25:3:25:12 | ... / ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:25:3:25:8 | Avenue | expression | +| enum.cpp:26:3:26:15 | ... * ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:26:3:26:11 | Boulevard | expression | +| enum.cpp:29:3:29:13 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:29:3:29:6 | Lane | expression | +| enum.cpp:29:3:29:13 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:29:10:29:13 | Road | expression | +| enum.cpp:30:3:30:15 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:30:3:30:7 | Place | expression | +| enum.cpp:30:3:30:15 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:30:12:30:15 | Lane | expression | +| enum.cpp:31:3:31:7 | ! ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:31:4:31:7 | Road | expression | +| enum.cpp:34:3:34:23 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:34:3:34:11 | Boulevard | expression | +| enum.cpp:34:3:34:23 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:34:15:34:23 | Boulevard | expression | +| enum.cpp:35:3:35:7 | ~ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:35:4:35:7 | Lane | expression | +| enum.cpp:36:3:36:14 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:36:3:36:7 | Place | expression | +| enum.cpp:36:3:36:14 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:36:11:36:14 | Road | expression | +| enum.cpp:37:3:37:11 | ... >> ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:37:3:37:6 | Road | expression | +| enum.cpp:38:3:38:11 | ... << ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:38:3:38:6 | Road | expression | +| enum.cpp:39:10:39:10 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:39:3:39:8 | Avenue | expression | +| enum.cpp:39:10:39:10 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:39:13:39:17 | Place | expression | +| enum.cpp:40:8:40:8 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:40:3:40:6 | Road | expression | +| enum.cpp:40:8:40:8 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:40:11:40:14 | Road | expression | +| enum.cpp:41:8:41:8 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:41:3:41:6 | Road | expression | +| enum.cpp:41:8:41:8 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:41:12:41:15 | Road | expression | +| enum.cpp:42:8:42:8 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:42:3:42:6 | Road | expression | +| enum.cpp:42:8:42:8 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:42:12:42:15 | Road | expression | +| enum.cpp:57:3:57:7 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:57:3:57:3 | a | expression | +| enum.cpp:57:3:57:7 | ... + ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:57:7:57:7 | a | expression | +| enum.cpp:58:3:58:7 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:58:3:58:3 | a | expression | +| enum.cpp:58:3:58:7 | ... - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:58:7:58:7 | a | expression | +| enum.cpp:59:3:59:4 | - ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:59:4:59:4 | a | expression | +| enum.cpp:60:3:60:7 | ... % ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:60:3:60:3 | a | expression | +| enum.cpp:61:3:61:7 | ... / ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:61:3:61:3 | a | expression | +| enum.cpp:62:3:62:7 | ... * ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:62:3:62:3 | a | expression | +| enum.cpp:65:3:65:7 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:65:3:65:3 | a | expression | +| enum.cpp:65:3:65:7 | ... && ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:65:7:65:7 | b | expression | +| enum.cpp:66:3:66:8 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:66:3:66:3 | a | expression | +| enum.cpp:66:3:66:8 | ... \|\| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:66:8:66:8 | b | expression | +| enum.cpp:67:3:67:4 | ! ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:67:4:67:4 | b | expression | +| enum.cpp:70:3:70:7 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:70:3:70:3 | a | expression | +| enum.cpp:70:3:70:7 | ... \| ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:70:7:70:7 | b | expression | +| enum.cpp:71:3:71:4 | ~ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:71:4:71:4 | a | expression | +| enum.cpp:72:3:72:7 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:72:3:72:3 | a | expression | +| enum.cpp:72:3:72:7 | ... ^ ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:72:7:72:7 | b | expression | +| enum.cpp:73:3:73:8 | ... >> ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:73:3:73:3 | a | expression | +| enum.cpp:74:3:74:8 | ... << ... | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:74:3:74:3 | a | expression | +| enum.cpp:75:5:75:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:75:3:75:3 | a | expression | +| enum.cpp:75:5:75:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum.cpp:75:8:75:8 | b | expression | +| enum_class.cpp:49:22:49:22 | call to operator+ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:49:3:49:20 | ML | expression | +| enum_class.cpp:50:23:50:23 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:50:3:50:21 | SML | expression | +| enum_class.cpp:50:23:50:23 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:50:25:50:42 | ML | expression | +| enum_class.cpp:51:3:51:3 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:51:4:51:26 | Haskell | expression | +| enum_class.cpp:52:26:52:26 | call to operator% | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:52:3:52:24 | Racket | expression | +| enum_class.cpp:53:23:53:23 | call to operator/ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:53:3:53:21 | Elm | expression | +| enum_class.cpp:54:26:54:26 | call to operator* | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:54:3:54:24 | Scheme | expression | +| enum_class.cpp:57:27:57:27 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:57:3:57:25 | Haskell | expression | +| enum_class.cpp:57:27:57:27 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:57:29:57:47 | Elm | expression | +| enum_class.cpp:58:24:58:24 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:58:3:58:22 | Lisp | expression | +| enum_class.cpp:58:24:58:24 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:58:27:58:48 | Racket | expression | +| enum_class.cpp:59:3:59:3 | call to operator! | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:59:4:59:25 | Scheme | expression | +| enum_class.cpp:62:23:62:23 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:62:3:62:21 | Elm | expression | +| enum_class.cpp:62:23:62:23 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:62:25:62:46 | Racket | expression | +| enum_class.cpp:63:3:63:3 | call to operator~ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:63:4:63:24 | Idris | expression | +| enum_class.cpp:64:22:64:22 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:64:3:64:20 | ML | expression | +| enum_class.cpp:64:22:64:22 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:64:24:64:44 | OCaml | expression | +| enum_class.cpp:65:25:65:25 | call to operator>> | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:65:3:65:23 | OCaml | expression | +| enum_class.cpp:66:24:66:24 | call to operator<< | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:66:3:66:22 | Lisp | expression | +| enum_class.cpp:67:5:67:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:67:3:67:3 | l | expression | +| enum_class.cpp:67:5:67:5 | call to operator&= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:67:8:67:28 | OCaml | expression | +| enum_class.cpp:68:5:68:5 | call to operator^= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:68:3:68:3 | l | expression | +| enum_class.cpp:69:5:69:5 | call to operator>>= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:69:3:69:3 | l | expression | +| enum_class.cpp:70:5:70:5 | call to operator<<= | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:70:3:70:3 | l | expression | +| enum_class.cpp:85:5:85:5 | call to operator+ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:85:3:85:3 | a | expression | +| enum_class.cpp:86:5:86:5 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:86:3:86:3 | a | expression | +| enum_class.cpp:86:5:86:5 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:86:7:86:7 | b | expression | +| enum_class.cpp:87:3:87:3 | call to operator- | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:87:4:87:4 | a | expression | +| enum_class.cpp:88:5:88:5 | call to operator% | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:88:3:88:3 | a | expression | +| enum_class.cpp:89:5:89:5 | call to operator/ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:89:3:89:3 | a | expression | +| enum_class.cpp:90:5:90:5 | call to operator* | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:90:3:90:3 | b | expression | +| enum_class.cpp:93:5:93:5 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:93:3:93:3 | a | expression | +| enum_class.cpp:93:5:93:5 | call to operator&& | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:93:7:93:7 | b | expression | +| enum_class.cpp:94:5:94:5 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:94:3:94:3 | a | expression | +| enum_class.cpp:94:5:94:5 | call to operator\|\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:94:8:94:8 | b | expression | +| enum_class.cpp:95:3:95:3 | call to operator! | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:95:4:95:4 | a | expression | +| enum_class.cpp:98:5:98:5 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:98:3:98:3 | a | expression | +| enum_class.cpp:98:5:98:5 | call to operator\| | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:98:7:98:7 | b | expression | +| enum_class.cpp:99:3:99:3 | call to operator~ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:99:4:99:4 | a | expression | +| enum_class.cpp:100:5:100:5 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:100:3:100:3 | a | expression | +| enum_class.cpp:100:5:100:5 | call to operator^ | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:100:7:100:7 | b | expression | +| enum_class.cpp:101:5:101:5 | call to operator>> | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:101:3:101:3 | a | expression | +| enum_class.cpp:102:5:102:5 | call to operator<< | Enum $@ is used as an operand of arithmetic operation. | enum_class.cpp:102:3:102:3 | a | expression | diff --git a/cpp/autosar/test/rules/A4-5-1/enum.cpp b/cpp/autosar/test/rules/A4-5-1/enum.cpp index afc4d97504..e6f3f178bc 100644 --- a/cpp/autosar/test/rules/A4-5-1/enum.cpp +++ b/cpp/autosar/test/rules/A4-5-1/enum.cpp @@ -14,7 +14,6 @@ void test_enum() { Avenue <= Avenue; // COMPLIANT Place > Road; // COMPLIANT Boulevard >= Avenue; // COMPLIANT - Place &Avenue; // COMPLIANT arr[Road] = 1; // COMPLIANT // arithmetic @@ -40,6 +39,7 @@ void test_enum() { Road ^= Road; // NON_COMPLIANT Road >>= Road; // NON_COMPLIANT Road <<= Road; // NON_COMPLIANT + Road &Road; // NON_COMPLIANT } void test_enum_var() { @@ -51,7 +51,7 @@ void test_enum_var() { a <= b; // COMPLIANT a > b; // COMPLIANT a >= b; // COMPLIANT - a &b; // COMPLIANT + Street *c = &a; // COMPLIANT // arithmetic a + a; // NON_COMPLIANT @@ -76,4 +76,5 @@ void test_enum_var() { a ^= 1; // NON_COMPLIANT a >>= 1; // NON_COMPLIANT a <<= 1; // NON_COMPLIANT + a &b; // NON_COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A4-5-1/enum.cpp.gcc b/cpp/autosar/test/rules/A4-5-1/enum.cpp.gcc new file mode 100644 index 0000000000..074aa4404f --- /dev/null +++ b/cpp/autosar/test/rules/A4-5-1/enum.cpp.gcc @@ -0,0 +1,79 @@ +enum Street { Road, Lane, Avenue, Boulevard, Place }; + +Street operator&=(Street left, Street right) { return left; } +Street operator^=(Street left, int) { return left; } +Street operator>>=(Street left, int) { return left; } +Street operator<<=(Street left, int) { return left; } + +void test_enum() { + int arr[Street::Place] = {}; + Street e = Lane; // COMPLIANT + Lane == Lane; // COMPLIANT + Place != Place; // COMPLIANT + Road < Lane; // COMPLIANT + Avenue <= Avenue; // COMPLIANT + Place > Road; // COMPLIANT + Boulevard >= Avenue; // COMPLIANT + Place &Avenue; // COMPLIANT + arr[Road] = 1; // COMPLIANT + + // arithmetic + Avenue + Place; // NON_COMPLIANT + Place - Place; // NON_COMPLIANT + -Avenue; // NON_COMPLIANT + Road % 0; // NON_COMPLIANT + Avenue / 1; // NON_COMPLIANT + Boulevard * 2; // NON_COMPLIANT + + // logical + Lane &&Road; // NON_COMPLIANT + Place || Lane; // NON_COMPLIANT + !Road; // NON_COMPLIANT + + // bitwise + Boulevard | Boulevard; // NON_COMPLIANT + ~Lane; // NON_COMPLIANT + Place ^ Road; // NON_COMPLIANT + Road >> 1; // NON_COMPLIANT + Road << 1; // NON_COMPLIANT + Avenue &= Place; // NON_COMPLIANT + Road ^= Road; // NON_COMPLIANT + Road >>= Road; // NON_COMPLIANT + Road <<= Road; // NON_COMPLIANT +} + +void test_enum_var() { + Street a = Lane; // COMPLIANT + Street b = Road; // COMPLIANT + a == b; // COMPLIANT + a != b; // COMPLIANT + a < b; // COMPLIANT + a <= b; // COMPLIANT + a > b; // COMPLIANT + a >= b; // COMPLIANT + a &b; // COMPLIANT + + // arithmetic + a + a; // NON_COMPLIANT + a - a; // NON_COMPLIANT + -a; // NON_COMPLIANT + a % 0; // NON_COMPLIANT + a / 1; // NON_COMPLIANT + a * 2; // NON_COMPLIANT + + // logical + a &&b; // NON_COMPLIANT + a || b; // NON_COMPLIANT + !b; // NON_COMPLIANT + + // bitwise + a | b; // NON_COMPLIANT + ~a; // NON_COMPLIANT + a ^ b; // NON_COMPLIANT + a >> 1; // NON_COMPLIANT + a << 1; // NON_COMPLIANT + a &= b; // NON_COMPLIANT + // a ^= 1; // NON_COMPLIANT - this is compiler checked + // a >>= 1; // NON_COMPLIANT - this is compiler checked + // a <<= 1; // NON_COMPLIANT - this is compiler checked +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A4-5-1/enum.cpp.qcc b/cpp/autosar/test/rules/A4-5-1/enum.cpp.qcc new file mode 100644 index 0000000000..074aa4404f --- /dev/null +++ b/cpp/autosar/test/rules/A4-5-1/enum.cpp.qcc @@ -0,0 +1,79 @@ +enum Street { Road, Lane, Avenue, Boulevard, Place }; + +Street operator&=(Street left, Street right) { return left; } +Street operator^=(Street left, int) { return left; } +Street operator>>=(Street left, int) { return left; } +Street operator<<=(Street left, int) { return left; } + +void test_enum() { + int arr[Street::Place] = {}; + Street e = Lane; // COMPLIANT + Lane == Lane; // COMPLIANT + Place != Place; // COMPLIANT + Road < Lane; // COMPLIANT + Avenue <= Avenue; // COMPLIANT + Place > Road; // COMPLIANT + Boulevard >= Avenue; // COMPLIANT + Place &Avenue; // COMPLIANT + arr[Road] = 1; // COMPLIANT + + // arithmetic + Avenue + Place; // NON_COMPLIANT + Place - Place; // NON_COMPLIANT + -Avenue; // NON_COMPLIANT + Road % 0; // NON_COMPLIANT + Avenue / 1; // NON_COMPLIANT + Boulevard * 2; // NON_COMPLIANT + + // logical + Lane &&Road; // NON_COMPLIANT + Place || Lane; // NON_COMPLIANT + !Road; // NON_COMPLIANT + + // bitwise + Boulevard | Boulevard; // NON_COMPLIANT + ~Lane; // NON_COMPLIANT + Place ^ Road; // NON_COMPLIANT + Road >> 1; // NON_COMPLIANT + Road << 1; // NON_COMPLIANT + Avenue &= Place; // NON_COMPLIANT + Road ^= Road; // NON_COMPLIANT + Road >>= Road; // NON_COMPLIANT + Road <<= Road; // NON_COMPLIANT +} + +void test_enum_var() { + Street a = Lane; // COMPLIANT + Street b = Road; // COMPLIANT + a == b; // COMPLIANT + a != b; // COMPLIANT + a < b; // COMPLIANT + a <= b; // COMPLIANT + a > b; // COMPLIANT + a >= b; // COMPLIANT + a &b; // COMPLIANT + + // arithmetic + a + a; // NON_COMPLIANT + a - a; // NON_COMPLIANT + -a; // NON_COMPLIANT + a % 0; // NON_COMPLIANT + a / 1; // NON_COMPLIANT + a * 2; // NON_COMPLIANT + + // logical + a &&b; // NON_COMPLIANT + a || b; // NON_COMPLIANT + !b; // NON_COMPLIANT + + // bitwise + a | b; // NON_COMPLIANT + ~a; // NON_COMPLIANT + a ^ b; // NON_COMPLIANT + a >> 1; // NON_COMPLIANT + a << 1; // NON_COMPLIANT + a &= b; // NON_COMPLIANT + // a ^= 1; // NON_COMPLIANT - this is compiler checked + // a >>= 1; // NON_COMPLIANT - this is compiler checked + // a <<= 1; // NON_COMPLIANT - this is compiler checked +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A4-5-1/enum_class.cpp b/cpp/autosar/test/rules/A4-5-1/enum_class.cpp index 6921917aea..0bbd3eb17c 100644 --- a/cpp/autosar/test/rules/A4-5-1/enum_class.cpp +++ b/cpp/autosar/test/rules/A4-5-1/enum_class.cpp @@ -43,7 +43,6 @@ void test_enum_class() { FunctionalLang::Elm <= FunctionalLang::Haskell; // COMPLIANT FunctionalLang::Idris > FunctionalLang::SML; // COMPLIANT FunctionalLang::Haskell >= FunctionalLang::Idris; // COMPLIANT - FunctionalLang::FSharp &FunctionalLang::OCaml; // COMPLIANT // arithmetic FunctionalLang::ML + 1; // NON_COMPLIANT @@ -59,15 +58,16 @@ void test_enum_class() { !FunctionalLang::Scheme; // NON_COMPLIANT // bitwise - FunctionalLang::Elm | FunctionalLang::Racket; // NON_COMPLIANT - ~FunctionalLang::Idris; // NON_COMPLIANT - FunctionalLang::ML ^ FunctionalLang::OCaml; // NON_COMPLIANT - FunctionalLang::OCaml >> 1; // NON_COMPLIANT - FunctionalLang::Lisp << 1; // NON_COMPLIANT - l &= FunctionalLang::OCaml; // NON_COMPLIANT - l ^= 1; // NON_COMPLIANT - l >>= 1; // NON_COMPLIANT - l <<= 1; // NON_COMPLIANT + FunctionalLang::Elm | FunctionalLang::Racket; // NON_COMPLIANT + ~FunctionalLang::Idris; // NON_COMPLIANT + FunctionalLang::ML ^ FunctionalLang::OCaml; // NON_COMPLIANT + FunctionalLang::OCaml >> 1; // NON_COMPLIANT + FunctionalLang::Lisp << 1; // NON_COMPLIANT + l &= FunctionalLang::OCaml; // NON_COMPLIANT + l ^= 1; // NON_COMPLIANT + l >>= 1; // NON_COMPLIANT + l <<= 1; // NON_COMPLIANT + FunctionalLang::FSharp &FunctionalLang::OCaml; // NON_COMPLIANT } void test_enum_class_vars() { @@ -79,7 +79,7 @@ void test_enum_class_vars() { a <= b; // COMPLIANT a > a; // COMPLIANT a >= a; // COMPLIANT - a &b; // COMPLIANT + FunctionalLang *c = &a; // COMPLIANT // arithmetic a + 1; // NON_COMPLIANT @@ -100,4 +100,21 @@ void test_enum_class_vars() { a ^ b; // NON_COMPLIANT a >> 1; // NON_COMPLIANT a << 1; // NON_COMPLIANT + a &b; // NON_COMPLIANT +} + +enum class byte : unsigned char {}; + +byte operator&(byte lhs, byte rhs) { return lhs; } +byte operator|(byte lhs, byte rhs) { return lhs; } +byte operator^(byte lhs, byte rhs) { return lhs; } +byte operator~(byte lhs) { return lhs; } +byte operator&=(byte lhs, byte rhs) { return lhs; } +byte operator|=(byte lhs, byte rhs) { return lhs; } + +void test_bitmasktype_enum_class() { // COMPLIANT - byte implements the + // BitmaskType trait. + byte one, two; + + one &two; } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A4-7-1/IntegerExpressionLeadToDataLoss.expected b/cpp/autosar/test/rules/A4-7-1/IntegerExpressionLeadToDataLoss.expected index d9c69bad90..9eb71ee301 100644 --- a/cpp/autosar/test/rules/A4-7-1/IntegerExpressionLeadToDataLoss.expected +++ b/cpp/autosar/test/rules/A4-7-1/IntegerExpressionLeadToDataLoss.expected @@ -8,4 +8,8 @@ | IntMultToLongc.cpp:109:13:109:28 | ... + ... | Binary expression ...+... may overflow. | | test.cpp:2:10:2:14 | ... + ... | Binary expression ...+... may overflow. | | test.cpp:22:12:22:16 | ... + ... | Binary expression ...+... may overflow. | -| test.cpp:52:7:52:14 | ... + ... | Binary expression ...+... may overflow. | +| test.cpp:50:7:50:14 | ... + ... | Binary expression ...+... may overflow. | +| test.cpp:62:8:62:10 | ... ++ | Binary expression ...++... may overflow. | +| test.cpp:91:10:91:17 | ... << ... | Binary expression ...<<... may overflow. | +| test.cpp:95:10:95:17 | ... << ... | Binary expression ...<<... may overflow. | +| test.cpp:98:8:98:15 | ... << ... | Binary expression ...<<... may overflow. | diff --git a/cpp/autosar/test/rules/A4-7-1/test.cpp b/cpp/autosar/test/rules/A4-7-1/test.cpp index 9527155618..416a228311 100644 --- a/cpp/autosar/test/rules/A4-7-1/test.cpp +++ b/cpp/autosar/test/rules/A4-7-1/test.cpp @@ -35,21 +35,66 @@ short test_addition_invalid_overflow_check(short x, short y) { return 0; } -void test_addition_loop_bound(unsigned int base, unsigned int size) { - if (size > 0) { - int n = size - 1; - for (int i = 0; i < n; i++) { - base + i; // COMPLIANT - `i` is bounded +void test_addition_loop_bound(unsigned short base, unsigned int n) { + if (n < 1000) { + for (unsigned int i = 0; i < n; i++) { // COMPLIANT + base + i; // COMPLIANT - `i` is bounded } } } -void test_addition_invalid_loop_bound(unsigned int base, unsigned int j, - unsigned int size) { - if (size > 0) { - int n = size - 1; - for (int i = 0; i < n; i++) { +void test_addition_invalid_loop_bound(unsigned short base, unsigned int j, + unsigned int n) { + if (n < 1000) { + for (unsigned int i = 0; i < n; i++) { // COMPLIANT base + j; // NON_COMPLIANT - guards are not related } } +} + +void test_loop_bound(unsigned int n) { + for (unsigned int i = 0; i < n; i++) { // COMPLIANT + } +} + +void test_loop_bound_bad(unsigned int n) { + for (unsigned short i = 0; i < n; + i++) { // NON_COMPLIANT - crement will overflow before loop bound is + // reached + } +} + +void test_assign_div(int i) { // COMPLIANT + i /= 2; +} + +void test_pointer() { + int *p = nullptr; + p++; // COMPLIANT - not covered by this rule + p--; // COMPLIANT - not covered by this rule +} + +extern unsigned int popcount(unsigned int); +#define PRECISION(x) popcount(x) +void test_guarded_shifts(unsigned int p1, int p2) { + unsigned int l1; + + if (p2 < popcount(p1) && p2 > 0) { + l1 = p1 << p2; // COMPLIANT + } + + if (p2 < PRECISION(p1) && p2 > 0) { + l1 = p1 << p2; // COMPLIANT + } + + if (p2 < popcount(p1)) { + l1 = p1 << p2; // NON_COMPLIANT - p2 could be negative + } + + if (p2 > 0) { + l1 = p1 << p2; // NON_COMPLIANT - p2 could have a higher precision + } + + l1 = p1 << p2; // NON_COMPLIANT - p2 may have a higher precision or could be + // negative } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.expected b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.expected deleted file mode 100644 index 655e5571e1..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:8:7:8:7 | i | If condition has non boolean type int. | -| test.cpp:10:7:10:7 | call to f | If condition has non boolean type int. | -| test.cpp:13:7:13:7 | a | If condition has non boolean type void *. | diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.qlref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.qlref deleted file mode 100644 index a2280d92c6..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-0-2/NonBooleanIfCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.testref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.testref new file mode 100644 index 0000000000..5f106ce750 --- /dev/null +++ b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.expected b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.expected deleted file mode 100644 index 091087b3a1..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:51:20:51:20 | i | Iteration condition has non boolean type int. | -| test.cpp:55:10:55:10 | j | Iteration condition has non boolean type int. | diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.qlref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.qlref deleted file mode 100644 index 535235d198..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-0-2/NonBooleanIterationCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.testref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.testref new file mode 100644 index 0000000000..36a500fcf8 --- /dev/null +++ b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/test.cpp b/cpp/autosar/test/rules/A5-0-2/test.cpp deleted file mode 100644 index 00fc281605..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include - -int f(); -void *g(); - -void test_non_boolean_conditions() { - int i = 1; - if (i) { // NON_COMPLIANT - } - if (f()) { // NON_COMPLIANT - } - void *a = g(); - if (a) { // NON_COMPLIANT - } -} - -class ClassA { -public: - explicit operator bool() const { return true; } -}; - -void test_boolean_conditions() { - int i = 1; - if ((bool)i) { // COMPLIANT - } - - int n = 1; - if ((const bool)n) { // COMPLIANT - handles accesses of const bool - } - - const bool constj = true; - const bool constk = false; - if (n && (constj <= constk)) { // COMPLIANT - handles accesses of const bool - } - - bool j = true; - bool k = false; - if (i && (j <= k)) { // COMPLIANT - because of C++ standard - } - - if (int i = 0) { // COMPLIANT - due to exception - } - - ClassA a; - if (a) { // COMPLIANT - a has an explicit operator bool() - } -} - -void test_non_boolean_iterations() { - int j; - for (int i = 10; i; i++) { // NON_COMPLIANT - j = 3; - } - - while (j) { // NON_COMPLIANT - int k = 3; - } -} - -void test_boolean_iterations() { - int j = 0; - for (int i = 0; i < 10; i++) { // COMPLIANT - j = i + j; - } - - int boolean = 0; - while (bool(boolean)) { // COMPLIANT - j = 5; - } - - while (int i = 0) { // COMPLIANT - due to exception - } - - ClassA a; - while (a) { // COMPLIANT - a has an explicit operator bool() - } -} - -template class ClassB { -public: - std::deque d; - void f() { - if (d.empty()) { // COMPLIANT - } - } -}; - -void class_b_test() { - ClassB b; - - b.f(); -} - -class ClassC { - void run() { - std::deque d; - if (!d.empty()) { // COMPLIANT - } - } -}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected b/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected index 4234d93b32..e2b51e5fb9 100644 --- a/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected +++ b/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected @@ -1,15 +1,19 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerArithmeticUsedWithPointersToNonFinalClasses.ql:45,62-70) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerArithmeticUsedWithPointersToNonFinalClasses.ql:46,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerArithmeticUsedWithPointersToNonFinalClasses.ql:55,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerArithmeticUsedWithPointersToNonFinalClasses.ql:61,3-11) edges -| test.cpp:10:18:10:20 | foo | test.cpp:11:23:11:25 | foo | -| test.cpp:10:18:10:20 | foo | test.cpp:11:50:11:52 | foo | -| test.cpp:22:18:22:20 | foo | test.cpp:24:18:24:20 | foo | -| test.cpp:35:11:35:17 | new | test.cpp:38:6:38:7 | l1 | -| test.cpp:35:11:35:17 | new | test.cpp:39:6:39:7 | l1 | -| test.cpp:37:11:37:13 | & ... | test.cpp:40:6:40:7 | l3 | -| test.cpp:37:11:37:13 | & ... | test.cpp:41:6:41:7 | l3 | -| test.cpp:38:6:38:7 | l1 | test.cpp:10:18:10:20 | foo | -| test.cpp:39:6:39:7 | l1 | test.cpp:22:18:22:20 | foo | -| test.cpp:40:6:40:7 | l3 | test.cpp:10:18:10:20 | foo | -| test.cpp:41:6:41:7 | l3 | test.cpp:22:18:22:20 | foo | +| test.cpp:10:18:10:20 | foo | test.cpp:11:23:11:25 | foo | provenance | | +| test.cpp:10:18:10:20 | foo | test.cpp:11:50:11:52 | foo | provenance | | +| test.cpp:22:18:22:20 | foo | test.cpp:24:18:24:20 | foo | provenance | | +| test.cpp:35:11:35:17 | new | test.cpp:38:6:38:7 | l1 | provenance | | +| test.cpp:35:11:35:17 | new | test.cpp:39:6:39:7 | l1 | provenance | | +| test.cpp:37:11:37:13 | & ... | test.cpp:40:6:40:7 | l3 | provenance | | +| test.cpp:37:11:37:13 | & ... | test.cpp:41:6:41:7 | l3 | provenance | | +| test.cpp:38:6:38:7 | l1 | test.cpp:10:18:10:20 | foo | provenance | | +| test.cpp:39:6:39:7 | l1 | test.cpp:22:18:22:20 | foo | provenance | | +| test.cpp:40:6:40:7 | l3 | test.cpp:10:18:10:20 | foo | provenance | | +| test.cpp:41:6:41:7 | l3 | test.cpp:22:18:22:20 | foo | provenance | | nodes | test.cpp:10:18:10:20 | foo | semmle.label | foo | | test.cpp:11:23:11:25 | foo | semmle.label | foo | diff --git a/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected.qcc b/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected.qcc new file mode 100644 index 0000000000..8b989f61b4 --- /dev/null +++ b/cpp/autosar/test/rules/A5-0-4/PointerArithmeticUsedWithPointersToNonFinalClasses.expected.qcc @@ -0,0 +1,115 @@ +edges +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1073:43:1073:45 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1078:49:1078:51 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1082:33:1082:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1083:33:1083:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1084:33:1084:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1093:48:1093:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1097:41:1097:43 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1093:48:1093:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1098:41:1098:43 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1093:48:1093:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1099:41:1099:43 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1121:19:1121:21 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1125:60:1125:62 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1121:19:1121:21 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1126:60:1126:62 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1121:19:1121:21 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1127:60:1127:62 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1136:35:1136:37 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1138:44:1138:46 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1141:30:1141:32 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1143:30:1143:32 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1146:29:1146:31 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1146:59:1146:61 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1147:30:1147:32 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1150:30:1150:32 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1152:30:1152:32 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1168:33:1168:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1170:34:1170:36 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1173:32:1173:34 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1178:33:1178:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1179:33:1179:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1180:33:1180:35 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1181:47:1181:49 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1182:45:1182:47 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1183:58:1183:60 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1184:58:1184:60 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1190:62:1190:64 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1191:57:1191:59 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1193:42:1193:44 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1195:41:1195:43 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1196:41:1196:43 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1197:65:1197:67 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1168:33:1168:35 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1170:34:1170:36 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1093:48:1093:50 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1173:32:1173:34 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1195:41:1195:43 | __s | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1121:19:1121:21 | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1262:48:1262:51 | & ... | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1262:48:1262:51 | & ... | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | +| test.cpp:10:18:10:20 | foo | test.cpp:11:23:11:25 | foo | +| test.cpp:10:18:10:20 | foo | test.cpp:11:50:11:52 | foo | +| test.cpp:22:18:22:20 | foo | test.cpp:24:18:24:20 | foo | +| test.cpp:35:11:35:17 | new | test.cpp:38:6:38:7 | l1 | +| test.cpp:35:11:35:17 | new | test.cpp:39:6:39:7 | l1 | +| test.cpp:37:11:37:13 | & ... | test.cpp:40:6:40:7 | l3 | +| test.cpp:37:11:37:13 | & ... | test.cpp:41:6:41:7 | l3 | +| test.cpp:38:6:38:7 | l1 | test.cpp:10:18:10:20 | foo | +| test.cpp:39:6:39:7 | l1 | test.cpp:22:18:22:20 | foo | +| test.cpp:40:6:40:7 | l3 | test.cpp:10:18:10:20 | foo | +| test.cpp:41:6:41:7 | l3 | test.cpp:22:18:22:20 | foo | +nodes +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1068:47:1068:49 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1073:43:1073:45 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1078:49:1078:51 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1082:33:1082:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1083:33:1083:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1084:33:1084:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1093:48:1093:50 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1097:41:1097:43 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1098:41:1098:43 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1099:41:1099:43 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1121:19:1121:21 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1125:60:1125:62 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1126:60:1126:62 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1127:60:1127:62 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1133:48:1133:50 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1136:35:1136:37 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1138:44:1138:46 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1141:30:1141:32 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1143:30:1143:32 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1146:29:1146:31 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1146:59:1146:61 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1147:30:1147:32 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1150:30:1150:32 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1152:30:1152:32 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1163:58:1163:62 | __key | semmle.label | __key | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1168:33:1168:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1170:34:1170:36 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1173:32:1173:34 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1178:33:1178:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1179:33:1179:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1180:33:1180:35 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1181:47:1181:49 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1182:45:1182:47 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1183:58:1183:60 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1184:58:1184:60 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1190:62:1190:64 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1191:57:1191:59 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1193:42:1193:44 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1195:41:1195:43 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1196:41:1196:43 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1197:65:1197:67 | __s | semmle.label | __s | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1262:48:1262:51 | & ... | semmle.label | & ... | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/utility:1262:48:1262:51 | & ... | semmle.label | & ... | +| test.cpp:10:18:10:20 | foo | semmle.label | foo | +| test.cpp:11:23:11:25 | foo | semmle.label | foo | +| test.cpp:11:50:11:52 | foo | semmle.label | foo | +| test.cpp:22:18:22:20 | foo | semmle.label | foo | +| test.cpp:24:18:24:20 | foo | semmle.label | foo | +| test.cpp:35:11:35:17 | new | semmle.label | new | +| test.cpp:37:11:37:13 | & ... | semmle.label | & ... | +| test.cpp:38:6:38:7 | l1 | semmle.label | l1 | +| test.cpp:39:6:39:7 | l1 | semmle.label | l1 | +| test.cpp:40:6:40:7 | l3 | semmle.label | l3 | +| test.cpp:41:6:41:7 | l3 | semmle.label | l3 | +subpaths +#select +| test.cpp:11:23:11:33 | ... + ... | test.cpp:35:11:35:17 | new | test.cpp:11:23:11:25 | foo | Pointer arithmetic with pointer to non-final class $@. | test.cpp:4:8:4:8 | S | S | +| test.cpp:11:23:11:33 | ... + ... | test.cpp:37:11:37:13 | & ... | test.cpp:11:23:11:25 | foo | Pointer arithmetic with pointer to non-final class $@. | test.cpp:4:8:4:8 | S | S | +| test.cpp:24:18:24:23 | access to array | test.cpp:35:11:35:17 | new | test.cpp:24:18:24:20 | foo | Pointer arithmetic with pointer to non-final class $@. | test.cpp:4:8:4:8 | S | S | +| test.cpp:24:18:24:23 | access to array | test.cpp:37:11:37:13 | & ... | test.cpp:24:18:24:20 | foo | Pointer arithmetic with pointer to non-final class $@. | test.cpp:4:8:4:8 | S | S | diff --git a/cpp/autosar/test/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.expected b/cpp/autosar/test/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.expected index 9e783c3b14..22300512fc 100644 --- a/cpp/autosar/test/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.expected +++ b/cpp/autosar/test/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.expected @@ -1,3 +1,8 @@ -| test.cpp:5:9:5:25 | constant string | Literal value "constant string" used outside of type initialization StringLiteral | -| test.cpp:14:23:14:25 | 100 | Literal value 100 used outside of type initialization Literal | -| test.cpp:54:7:54:7 | 1 | Literal value 1 used outside of type initialization Literal | +| test.cpp:5:9:5:25 | constant string | Literal value '"constant string"' used outside of type initialization. | +| test.cpp:14:23:14:25 | 100 | Literal value '100' used outside of type initialization. | +| test.cpp:54:7:54:7 | 1 | Literal value '1' used outside of type initialization. | +| test.cpp:75:23:75:28 | test | Literal value '"test"' used outside of type initialization. | +| test.cpp:76:19:76:28 | not okay | Literal value '"not okay"' used outside of type initialization. | +| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. | +| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. | +| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. | diff --git a/cpp/autosar/test/rules/A5-1-1/test.cpp b/cpp/autosar/test/rules/A5-1-1/test.cpp index f6b9e44ea3..65e691fd32 100644 --- a/cpp/autosar/test/rules/A5-1-1/test.cpp +++ b/cpp/autosar/test/rules/A5-1-1/test.cpp @@ -2,7 +2,7 @@ #include void test_exception1() { - throw "constant string"; // NOT_COMPLIANT - not used in type initialization + throw "constant string"; // NON_COMPLIANT - not used in type initialization } void test_exception2() { @@ -52,4 +52,74 @@ void test_class() { void test_assignment() { int x = 0; // COMPLIANT - used in type initialization x = 1; // NON_COMPLIANT - used in assignment +} + +void test_stream(std::ostream &os, const char *str) noexcept { + os << str << "logging string"; // COMPLIANT - literal used in stream write +} + +#define WRAPPER_MACRO(X, Y) test_stream(X, Y) + +void test_wrapper_stream(std::ostream &os, const char *str) noexcept { + test_stream(os, "test"); // COMPLIANT - wrapper for stream write + WRAPPER_MACRO(os, "test"); // COMPLIANT - wrapper for stream write +} + +void test_stream_two(std::ostream &os, const char *str, + const char *alt) noexcept { + os << str << "logging string"; // COMPLIANT - literal used in stream write + throw alt; +} + +void test_not_wrapper_stream(std::ostream &os, const char *str) noexcept { + test_stream_two(os, "test", + "not okay"); // NON_COMPLIANT - test_stream_two is + // not actually exclusively a wrapper +} + +#define MACRO_LOG(test_str) \ + do { \ + struct test_struct { \ + static const char *get_str() { \ + return static_cast(test_str); \ + } \ + }; \ + } while (false) + +void f() { + MACRO_LOG("test"); // COMPLIANT - exclusion +} + +template struct S1 { static constexpr size_t value(); }; + +template <> struct S1 { + static constexpr size_t value() { return sizeof(int); }; +}; + +constexpr size_t g1 = S1::value(); +constexpr size_t f1() { return sizeof(int); } + +template struct S2 { + T m1[size]; // COMPLIANT + T m2[4]; // NON_COMPLIANT +}; + +template struct S3 { + static constexpr T value = val; // COMPLIANT; +}; + +void test_fp_reported_in_371() { + struct S2 l1; // COMPLIANT + struct S2 l2; // COMPLIANT + struct S2 l3; // COMPLIANT + + S3 l4; // COMPLIANT + S3::value> l5; // COMPLIANT + S3 l6; // COMPLIANT + S3::value> l7; // COMPLIANT + + constexpr float l8 = 3.14159f; +#define delta 0.1f + for (float i = 0.0f; i < l8; i += delta) { // COMPLIANT + } } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-1-3/test.cpp b/cpp/autosar/test/rules/A5-1-3/test.cpp index cca6898710..d44419ff9b 100644 --- a/cpp/autosar/test/rules/A5-1-3/test.cpp +++ b/cpp/autosar/test/rules/A5-1-3/test.cpp @@ -28,4 +28,11 @@ void test() { l1 += 1; }; // clang-format on -} \ No newline at end of file +} + +#define PARAM_MACRO [](int i) { i; }; + +int test_lambda_in_macro() { + PARAM_MACRO // COMPLIANT + return 0; +} diff --git a/cpp/autosar/test/rules/A5-1-7/LambdaPassedToDecltype.expected b/cpp/autosar/test/rules/A5-1-7/LambdaPassedToDecltype.expected index 8f6447a96b..56896d69fd 100644 --- a/cpp/autosar/test/rules/A5-1-7/LambdaPassedToDecltype.expected +++ b/cpp/autosar/test/rules/A5-1-7/LambdaPassedToDecltype.expected @@ -1 +1,7 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToDecltype.ql:20,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToDecltype.ql:21,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToDecltype.ql:23,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToDecltype.ql:28,44-52) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToDecltype.ql:39,47-55) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToDecltype.ql:40,9-17) | test.cpp:14:23:14:24 | decltype(...) | Lambda $@ passed as operand to decltype. | test.cpp:5:13:5:30 | [...](...){...} | expression | diff --git a/cpp/autosar/test/rules/A5-1-7/LambdaPassedToTypeid.expected b/cpp/autosar/test/rules/A5-1-7/LambdaPassedToTypeid.expected index 4b19073ded..8f86a87616 100644 --- a/cpp/autosar/test/rules/A5-1-7/LambdaPassedToTypeid.expected +++ b/cpp/autosar/test/rules/A5-1-7/LambdaPassedToTypeid.expected @@ -1,6 +1,10 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToTypeid.ql:21,50-58) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToTypeid.ql:22,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToTypeid.ql:24,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (LambdaPassedToTypeid.ql:27,39-47) edges -| test.cpp:5:13:5:30 | [...](...){...} | test.cpp:8:38:8:39 | l1 | -| test.cpp:6:13:6:30 | [...](...){...} | test.cpp:9:38:9:39 | l2 | +| test.cpp:5:13:5:30 | [...](...){...} | test.cpp:8:38:8:39 | l1 | provenance | | +| test.cpp:6:13:6:30 | [...](...){...} | test.cpp:9:38:9:39 | l2 | provenance | | nodes | test.cpp:5:13:5:30 | [...](...){...} | semmle.label | [...](...){...} | | test.cpp:6:13:6:30 | [...](...){...} | semmle.label | [...](...){...} | diff --git a/cpp/autosar/test/rules/A5-1-9/test.cpp b/cpp/autosar/test/rules/A5-1-9/test.cpp index 5d1f455d51..511be302a0 100644 --- a/cpp/autosar/test/rules/A5-1-9/test.cpp +++ b/cpp/autosar/test/rules/A5-1-9/test.cpp @@ -86,16 +86,16 @@ void test_noncompliant() { #include class Test_issue468 { public: - template static void LogInfo(const As &... rest) { + template static void LogInfo(const As &...rest) { [](const std::string &s) -> void { LogInfo(s); }; // COMPLIANT } - template static void LogWarn(const As &... rest) { + template static void LogWarn(const As &...rest) { [](const std::string &s) -> void { LogWarn(s); }; // COMPLIANT } - template static void LogError(const As &... rest) { + template static void LogError(const As &...rest) { [](const std::string &s) -> void { LogError(s); }; // NON_COMPLIANT } - template static void LogFatal(const As &... rest) { + template static void LogFatal(const As &...rest) { [](const std::string &s) -> void { LogError(s); }; // NON_COMPLIANT } void instantiate() { @@ -104,4 +104,10 @@ class Test_issue468 { LogError("Error"); LogFatal("Fatal"); } -}; \ No newline at end of file +}; + +#define MACRO() [](int i) -> int { return i + 3; } +void test_macros() { + MACRO(); // COMPLIANT + MACRO(); // COMPLIANT - no duplication +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.expected b/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.expected deleted file mode 100644 index 81f26e5130..0000000000 --- a/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:14:14:14:29 | ... == ... | A pointer to member virtual function $@ is tested for equality with non-null-pointer-constant $@. | test.cpp:6:16:6:17 | F3 | F3 | test.cpp:14:24:14:29 | F1 | F1 | -| test.cpp:15:14:15:29 | ... == ... | A pointer to member virtual function $@ is tested for equality with non-null-pointer-constant $@. | test.cpp:6:16:6:17 | F3 | F3 | test.cpp:15:24:15:29 | F2 | F2 | diff --git a/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.qlref b/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.qlref deleted file mode 100644 index 5f588b44ab..0000000000 --- a/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.testref b/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.testref new file mode 100644 index 0000000000..ca8eab9681 --- /dev/null +++ b/cpp/autosar/test/rules/A5-10-1/PointerToMemberVirtualFunctionWithNullPointerConstant.testref @@ -0,0 +1 @@ +cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-10-1/PotentiallyVirtualPointerOnlyComparesToNullptr.testref b/cpp/autosar/test/rules/A5-10-1/PotentiallyVirtualPointerOnlyComparesToNullptr.testref new file mode 100644 index 0000000000..ca8eab9681 --- /dev/null +++ b/cpp/autosar/test/rules/A5-10-1/PotentiallyVirtualPointerOnlyComparesToNullptr.testref @@ -0,0 +1 @@ +cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-2/TraditionalCStyleCastsUsed.expected b/cpp/autosar/test/rules/A5-2-2/TraditionalCStyleCastsUsed.expected index 1b349cea04..a7b7eef66c 100644 --- a/cpp/autosar/test/rules/A5-2-2/TraditionalCStyleCastsUsed.expected +++ b/cpp/autosar/test/rules/A5-2-2/TraditionalCStyleCastsUsed.expected @@ -1,3 +1,9 @@ -| test.cpp:8:22:8:37 | (uint32_t)... | Use of explicit C-style Cast | -| test.cpp:9:22:9:32 | (unsigned int)... | Use of explicit C-style Cast | -| test.cpp:15:3:15:13 | (void)... | Use of explicit C-style Cast | +| test.cpp:8:22:8:37 | (uint32_t)... | Use of explicit c-style cast to uint32_t. | test.cpp:8:22:8:37 | (uint32_t)... | | +| test.cpp:9:22:9:32 | (unsigned int)... | Use of explicit c-style cast to unsigned int. | test.cpp:9:22:9:32 | (unsigned int)... | | +| test.cpp:15:3:15:13 | (void)... | Use of explicit c-style cast to void. | test.cpp:15:3:15:13 | (void)... | | +| test.cpp:77:3:77:11 | (int)... | Use of explicit c-style cast to int generated from macro $@. | test.cpp:70:1:70:31 | #define ADD_ONE(x) ((int)x) + 1 | ADD_ONE | +| test.cpp:79:3:79:18 | (int)... | Use of explicit c-style cast to int generated from macro $@. | test.cpp:71:1:71:36 | #define NESTED_ADD_ONE(x) ADD_ONE(x) | NESTED_ADD_ONE | +| test.cpp:85:19:85:26 | (int)... | Use of explicit c-style cast to int. | test.cpp:85:19:85:26 | (int)... | | +| test.cpp:86:27:86:34 | (int)... | Use of explicit c-style cast to int. | test.cpp:86:27:86:34 | (int)... | | +| test.cpp:114:10:114:13 | (int)... | Use of explicit c-style cast to int. | test.cpp:114:10:114:13 | (int)... | | +| test.cpp:149:12:149:26 | (unsigned int)... | Use of explicit c-style cast to unsigned int. | test.cpp:149:12:149:26 | (unsigned int)... | | diff --git a/cpp/autosar/test/rules/A5-2-2/options.clang b/cpp/autosar/test/rules/A5-2-2/options.clang new file mode 100644 index 0000000000..a275a21895 --- /dev/null +++ b/cpp/autosar/test/rules/A5-2-2/options.clang @@ -0,0 +1 @@ +-I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-2/options.gcc b/cpp/autosar/test/rules/A5-2-2/options.gcc new file mode 100644 index 0000000000..a275a21895 --- /dev/null +++ b/cpp/autosar/test/rules/A5-2-2/options.gcc @@ -0,0 +1 @@ +-I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-2/options.qcc b/cpp/autosar/test/rules/A5-2-2/options.qcc new file mode 100644 index 0000000000..a275a21895 --- /dev/null +++ b/cpp/autosar/test/rules/A5-2-2/options.qcc @@ -0,0 +1 @@ +-I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-2/test.cpp b/cpp/autosar/test/rules/A5-2-2/test.cpp index 23820114cb..fb39868560 100644 --- a/cpp/autosar/test/rules/A5-2-2/test.cpp +++ b/cpp/autosar/test/rules/A5-2-2/test.cpp @@ -6,7 +6,7 @@ int foo() { return 1; } void test_c_style_cast() { double f = 3.14; std::uint32_t n1 = (std::uint32_t)f; // NON_COMPLIANT - C-style cast - std::uint32_t n2 = unsigned(f); // NON_COMPLIANT - functional cast + std::uint32_t n2 = unsigned(f); // COMPLIANT[FALSE_POSITIVE] std::uint8_t n3 = 1; std::uint8_t n4 = 1; @@ -45,12 +45,12 @@ void test_cpp_style_cast() { class A5_2_2a { public: template - static void Foo(const std::string &name, As &&... rest) { + static void Foo(const std::string &name, As &&...rest) { Fun(Log( std::forward(rest)...)); // COMPLIANT - reported as a false positive } - template static std::string Log(As &&... tail) { + template static std::string Log(As &&...tail) { return std::string(); } @@ -65,4 +65,96 @@ class A5_2_2 final { void a5_2_2_test() { A5_2_2 a; a.f(""); -} \ No newline at end of file +} + +#define ADD_ONE(x) ((int)x) + 1 +#define NESTED_ADD_ONE(x) ADD_ONE(x) +#define NO_CAST_ADD_ONE(x) x + 1 + +#include "macro_c_style_casts.h" + +void test_macro_cast() { + ADD_ONE(1); // NON_COMPLIANT - expansion of user-defined macro creates + // c-style cast + NESTED_ADD_ONE(1); // NON_COMPLIANT - expansion of user-defined macro creates + // c-style cast + LIBRARY_ADD_TWO(1); // COMPLIANT - macro generating the cast is defined in a + // library, and is not modifiable by the user + LIBRARY_NESTED_ADD_TWO(1); // COMPLIANT - macro generating the cast is defined + // in a library, and is not modifiable by the user + NO_CAST_ADD_ONE((int)1.0); // NON_COMPLIANT - cast in argument to macro + LIBRARY_NO_CAST_ADD_TWO((int)1.0); // NON_COMPLIANT - library macro with + // c-style cast in argument, written by + // user so should be reported +} + +class D { +public: + D(int x) : fx(x), fy(0) {} + D(int x, int y) : fx(x), fy(y) {} + +private: + int fx; + int fy; +}; + +D testNonFunctionalCast() { + return (D)1; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +D testFunctionalCast() { + return D(1); // COMPLIANT +} + +D testFunctionalCastMulti() { + return D(1, 2); // COMPLIANT +} + +template T testFunctionalCastTemplate() { + return T(1); // COMPLIANT[FALSE_POSITIVE] +} + +template T testFunctionalCastTemplateMulti() { + return T(1, 2); // COMPLIANT +} + +void testFunctionalCastTemplateUse() { + testFunctionalCastTemplate(); + testFunctionalCastTemplate(); + testFunctionalCastTemplateMulti(); +} + +template class E { +public: + class F { + public: + F(int x) : fx(x), fy(0) {} + F(int x, int y) : fx(x), fy(y) {} + + private: + int fx; + int fy; + }; + + F f() { + return F(1); // COMPLIANT + } + + D d() { + return D(1); // COMPLIANT + } + + int i() { + double f = 3.14; + return (unsigned int)f; // NON_COMPLIANT + } +}; + +class G {}; + +void testE() { + E e; + e.f(); + e.d(); + e.i(); +} diff --git a/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.qlref b/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.qlref deleted file mode 100644 index 3cfb0444cc..0000000000 --- a/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-2-4/ReinterpretCastUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.testref b/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.testref new file mode 100644 index 0000000000..81f18c2d9c --- /dev/null +++ b/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.expected b/cpp/autosar/test/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.expected index 90516e6d96..34dbb0db4d 100644 --- a/cpp/autosar/test/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.expected +++ b/cpp/autosar/test/rules/A5-2-6/OperandsOfALogicalAndOrNotParenthesized.expected @@ -1,3 +1,3 @@ -| test.cpp:3:7:3:23 | ... && ... | Binary $@ operand of logical operation is not parenthesized. | test.cpp:3:7:3:12 | ... > ... | operator | -| test.cpp:3:7:3:23 | ... && ... | Binary $@ operand of logical operation is not parenthesized. | test.cpp:3:17:3:23 | ... < ... | operator | -| test.cpp:7:7:7:24 | ... \|\| ... | Binary $@ operand of logical operation is not parenthesized. | test.cpp:7:19:7:24 | ... > ... | operator | +| test.cpp:3:7:3:23 | ... && ... | $@ of logical operation && is not parenthesized. | test.cpp:3:7:3:12 | ... > ... | Left operand > | +| test.cpp:3:7:3:23 | ... && ... | $@ of logical operation && is not parenthesized. | test.cpp:3:17:3:23 | ... < ... | Right operand < | +| test.cpp:7:7:7:24 | ... \|\| ... | $@ of logical operation \|\| is not parenthesized. | test.cpp:7:19:7:24 | ... > ... | Right operand > | diff --git a/cpp/autosar/test/rules/A5-2-6/test.cpp b/cpp/autosar/test/rules/A5-2-6/test.cpp index 9b7976ed23..961eef3b36 100644 --- a/cpp/autosar/test/rules/A5-2-6/test.cpp +++ b/cpp/autosar/test/rules/A5-2-6/test.cpp @@ -15,4 +15,22 @@ void f2(int p1, int p2) { if ((p1 > 0) || (p2 > 0)) { // COMPLIANT f1(); } + + struct Sample { + int x; + } sample; + + if ((p1 > 0) || + sample.x) { // COMPLIANT: struct member accessors (.) are excluded + f1(); + } + + (p1 > 0) && (p2 > 0) && (p1 > p2); // COMPLIANT - no ambiguity + + Sample *sample_ptr = &sample; + + if ((p1 > 0) || sample_ptr->x) { // COMPLIANT: struct member accessors with + // dereference (->) are excluded + f1(); + } } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.qlref b/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.qlref deleted file mode 100644 index 5cffcd0809..0000000000 --- a/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-3-2/NullPointersDereferenced.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.testref b/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.testref new file mode 100644 index 0000000000..610e626032 --- /dev/null +++ b/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.testref @@ -0,0 +1 @@ +cpp/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.expected b/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.expected deleted file mode 100644 index 9f4343cf1c..0000000000 --- a/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:4:3:4:14 | goto ... | Use of goto. | diff --git a/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.qlref b/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.qlref deleted file mode 100644 index d3516aa03b..0000000000 --- a/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A6-6-1/GotoStatementUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.testref b/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.testref new file mode 100644 index 0000000000..44d306f80c --- /dev/null +++ b/cpp/autosar/test/rules/A6-6-1/GotoStatementUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A6-6-1/test.cpp b/cpp/autosar/test/rules/A6-6-1/test.cpp deleted file mode 100644 index d13f01961c..0000000000 --- a/cpp/autosar/test/rules/A6-6-1/test.cpp +++ /dev/null @@ -1,9 +0,0 @@ -void test_goto() { - int x = 1; - - goto label1; // NON_COMPLIANT - -label1: - - x = 2; -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.expected b/cpp/autosar/test/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.expected deleted file mode 100644 index dd673a2e68..0000000000 --- a/cpp/autosar/test/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:7:14:7:14 | p | Non-constant parameter p points to an object and is not modified. | diff --git a/cpp/autosar/test/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.qlref b/cpp/autosar/test/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.qlref deleted file mode 100644 index 5f5b01f4bc..0000000000 --- a/cpp/autosar/test/rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-1-1/DeclarationUnmodifiedParamMissingConstSpecifier.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-1-1/test.cpp b/cpp/autosar/test/rules/A7-1-1/test.cpp index 4ee62ed013..1fdc0d66eb 100644 --- a/cpp/autosar/test/rules/A7-1-1/test.cpp +++ b/cpp/autosar/test/rules/A7-1-1/test.cpp @@ -4,7 +4,7 @@ void f1(int *p) { // COMPLIANT *p += 2; } -void f2(int *p) { // NON_COMPLIANT +void f2(int *p) { // COMPLIANT - we ignore parameters for this rule int l4 = 1; // NON_COMPLIANT int *p1 = p; // NON_COMPLIANT } @@ -66,8 +66,7 @@ class Issue18 { public: template void F(const T &s) { // ignore uninstantiated templates - std::ostream ostr; // COMPLIANT - ostr << s; // <= Modified here + std::cout << s << '\n'; // COMPLIANT return; } }; @@ -77,4 +76,23 @@ int main(int, char **) noexcept { new A7_1_1b(0); (new Issue18)->F(0); -} \ No newline at end of file +} + +template extern constexpr bool recurse_var = true; // COMPLIANT + +template +extern constexpr bool recurse_var = B1 &&recurse_var; + +void fp_621() { recurse_var; } + +#include + +void variadic_forwarding() {} + +template +void variadic_forwarding(T &&first, Args &&...rest) { + first; + variadic_forwarding(std::forward(rest)...); +} + +int test_variadic_forwarding() { variadic_forwarding(1, 1.1, "a"); } diff --git a/cpp/autosar/test/rules/A7-1-2/FunctionMissingConstexpr.expected b/cpp/autosar/test/rules/A7-1-2/FunctionMissingConstexpr.expected deleted file mode 100644 index 666721d0ad..0000000000 --- a/cpp/autosar/test/rules/A7-1-2/FunctionMissingConstexpr.expected +++ /dev/null @@ -1,12 +0,0 @@ -| test.cpp:30:3:30:17 | NonLiteralClass | NonLiteralClass function could be marked as 'constexpr'. | -| test.cpp:59:5:59:6 | h1 | h1 function could be marked as 'constexpr'. | -| test.cpp:67:5:67:6 | h2 | h2 function could be marked as 'constexpr'. | -| test.cpp:100:5:100:6 | h8 | h8 function could be marked as 'constexpr'. | -| test.cpp:117:7:117:9 | mf1 | mf1 function could be marked as 'constexpr'. | -| test.cpp:126:3:126:23 | MissingConstexprClass | MissingConstexprClass function could be marked as 'constexpr'. | -| test.cpp:127:3:127:23 | MissingConstexprClass | MissingConstexprClass function could be marked as 'constexpr'. | -| test.cpp:128:3:128:23 | MissingConstexprClass | MissingConstexprClass function could be marked as 'constexpr'. | -| test.cpp:161:3:161:26 | VariantMemberInitialized | VariantMemberInitialized function could be marked as 'constexpr'. | -| test.cpp:162:3:162:26 | VariantMemberInitialized | VariantMemberInitialized function could be marked as 'constexpr'. | -| test.cpp:163:3:163:26 | VariantMemberInitialized | VariantMemberInitialized function could be marked as 'constexpr'. | -| test.cpp:190:3:190:22 | VariantMemberNotInit | VariantMemberNotInit function could be marked as 'constexpr'. | diff --git a/cpp/autosar/test/rules/A7-1-2/FunctionMissingConstexpr.qlref b/cpp/autosar/test/rules/A7-1-2/FunctionMissingConstexpr.qlref deleted file mode 100644 index 723a910948..0000000000 --- a/cpp/autosar/test/rules/A7-1-2/FunctionMissingConstexpr.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-1-2/FunctionMissingConstexpr.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-1-2/VariableMissingConstexpr.expected b/cpp/autosar/test/rules/A7-1-2/VariableMissingConstexpr.expected index 6b6ed61dc8..5feec712b8 100644 --- a/cpp/autosar/test/rules/A7-1-2/VariableMissingConstexpr.expected +++ b/cpp/autosar/test/rules/A7-1-2/VariableMissingConstexpr.expected @@ -1,12 +1,24 @@ -| test.cpp:4:5:4:6 | g1 | Variable g1 could be marked 'constexpr'. | -| test.cpp:6:5:6:6 | g2 | Variable g2 could be marked 'constexpr'. | -| test.cpp:13:14:13:15 | lc | Variable lc could be marked 'constexpr'. | -| test.cpp:15:14:15:16 | lca | Variable lca could be marked 'constexpr'. | -| test.cpp:23:15:23:17 | lc2 | Variable lc2 could be marked 'constexpr'. | -| test.cpp:25:15:25:18 | lc2a | Variable lc2a could be marked 'constexpr'. | -| test.cpp:41:14:41:15 | l2 | Variable l2 could be marked 'constexpr'. | -| test.cpp:44:16:44:17 | lc | Variable lc could be marked 'constexpr'. | -| test.cpp:45:17:45:19 | lc2 | Variable lc2 could be marked 'constexpr'. | -| test.cpp:55:7:55:8 | m2 | Variable m2 could be marked 'constexpr'. | -| test.cpp:130:7:130:8 | m1 | Variable m1 could be marked 'constexpr'. | -| test.cpp:141:7:141:8 | m1 | Variable m1 could be marked 'constexpr'. | +| test.cpp:4:5:4:6 | g1 | Variable 'g1' could be marked 'constexpr'. | +| test.cpp:6:5:6:6 | g2 | Variable 'g2' could be marked 'constexpr'. | +| test.cpp:13:14:13:15 | lc | Variable 'lc' could be marked 'constexpr'. | +| test.cpp:15:14:15:16 | lca | Variable 'lca' could be marked 'constexpr'. | +| test.cpp:23:15:23:17 | lc2 | Variable 'lc2' could be marked 'constexpr'. | +| test.cpp:25:15:25:18 | lc2a | Variable 'lc2a' could be marked 'constexpr'. | +| test.cpp:41:14:41:15 | l2 | Variable 'l2' could be marked 'constexpr'. | +| test.cpp:44:16:44:17 | lc | Variable 'lc' could be marked 'constexpr'. | +| test.cpp:45:17:45:19 | lc2 | Variable 'lc2' could be marked 'constexpr'. | +| test.cpp:55:7:55:8 | m2 | Variable 'm2' could be marked 'constexpr' and static. | +| test.cpp:65:7:65:8 | x2 | Variable 'x2' could be marked 'constexpr'. | +| test.cpp:66:13:66:14 | x3 | Variable 'x3' could be marked 'constexpr'. | +| test.cpp:76:7:76:8 | m1 | Variable 'm1' could be marked 'constexpr' and static. | +| test.cpp:91:7:91:8 | l1 | Variable 'l1' could be marked 'constexpr'. | +| test.cpp:105:7:105:8 | l6 | Variable 'l6' could be marked 'constexpr'. | +| test.cpp:107:7:107:8 | l8 | Variable 'l8' could be marked 'constexpr'. | +| test.cpp:110:7:110:9 | l10 | Variable 'l10' could be marked 'constexpr'. | +| test.cpp:113:7:113:9 | l12 | Variable 'l12' could be marked 'constexpr'. | +| test.cpp:118:7:118:9 | l15 | Variable 'l15' could be marked 'constexpr'. | +| test.cpp:120:7:120:9 | l16 | Variable 'l16' could be marked 'constexpr'. | +| test.cpp:121:7:121:9 | l17 | Variable 'l17' could be marked 'constexpr'. | +| test.cpp:127:7:127:9 | l21 | Variable 'l21' could be marked 'constexpr'. | +| test.cpp:132:7:132:9 | l24 | Variable 'l24' could be marked 'constexpr'. | +| test.cpp:133:7:133:9 | l25 | Variable 'l25' could be marked 'constexpr'. | diff --git a/cpp/autosar/test/rules/A7-1-2/test.cpp b/cpp/autosar/test/rules/A7-1-2/test.cpp index 020ba09a2b..5366a59f95 100644 --- a/cpp/autosar/test/rules/A7-1-2/test.cpp +++ b/cpp/autosar/test/rules/A7-1-2/test.cpp @@ -56,152 +56,92 @@ class MemberConstExpr { int m3 = 0; // COMPLIANT - can be set by constructor }; -int h1(int x, int y) { // NON_COMPLIANT - return x + y; -} - -constexpr int h1_correct(int x, int y) { // COMPLIANT - return x + y; -} - -int h2(int x) { return h1(x, 1) + 1; } // NON_COMPLIANT -constexpr int h2_correct(int x) { return h1_correct(x, 1) + 1; } // COMPLIANT - -int h3(int x) { // COMPLIANT - uses goto, so can't be constexpr - if (x) { - goto l1; - } else { - return 10; - } -l1: - return 1; -} - -int h4(int x) { // COMPLIANT - uses try, so can't be constexpr - try { - return 1; - } catch (...) { - } -} - -int h5(int x) { // COMPLIANT - declares non literal local var - NonLiteralClass nlc; -} +int h1(int x, int y) { return x + y; } -int h6(int x) { // COMPLIANT - declares static variable - static int i = x; - return x; -} - -int h7(int x) { // COMPLIANT - declares no init variable - int i; -} - -int h8(int x) { // NON_COMPLIANT - could be constexpr - int i = x; - return i; -} - -constexpr int h8_correct(int x) { // COMPLIANT - int i = x; - return i; -} +constexpr int h1_const(int x, int y) { return x + y; } -int h9(int x) { // COMPLIANT - declares thread local variable - thread_local int i = x; - return x; +int h2() { + int x1 = h1(1, 1); // COMPLIANT + int x2 = h1_const(1, 1); // NON_COMPLIANT + const int x3 = h1_const(1, 1); // NON_COMPLIANT + constexpr int x4 = h1_const(1, 1); // COMPLIANT } -class ConstexprFunctionClass { -public: - int mf1(int x) { return m1 + x; } // NON_COMPLIANT - constexpr int mf1_correct(int x) { return m1 + x; } // COMPLIANT - -private: - int m1; -}; - class MissingConstexprClass { public: MissingConstexprClass() = default; // NON_COMPLIANT MissingConstexprClass(int i) = delete; // NON_COMPLIANT MissingConstexprClass(int i, LiteralClass lc) {} // NON_COMPLIANT private: - int m1 = 0; -}; - -class VirtualBaseClass {}; - -class DerivedClass : public virtual VirtualBaseClass { -public: - DerivedClass() = default; // COMPLIANT - DerivedClass(int i) = delete; // COMPLIANT - DerivedClass(int i, LiteralClass lc) {} // COMPLIANT -private: - int m1 = 0; -}; - -class NotAllMembersInitializedClass { -public: - NotAllMembersInitializedClass() = default; // COMPLIANT - NotAllMembersInitializedClass(int i) = delete; // COMPLIANT - NotAllMembersInitializedClass(int i, LiteralClass lc) {} // COMPLIANT -private: - int m1; -}; - -class NonLiteralParamsClass { -public: - NonLiteralParamsClass(int i, NonLiteralClass lc) {} // COMPLIANT -}; - -// Variant members are always initialized, so this can be marked constexpr -class VariantMemberInitialized { -public: - VariantMemberInitialized() = default; // NON_COMPLIANT - VariantMemberInitialized(int i) = delete; // NON_COMPLIANT - VariantMemberInitialized(int i, LiteralClass lc) {} // NON_COMPLIANT -private: - union { - int i = 0; - short s; - }; + int m1 = 0; // NON_COMPLIANT }; -class VariantMemberInitConstexpr { -public: - constexpr VariantMemberInitConstexpr() = default; // COMPLIANT - constexpr VariantMemberInitConstexpr(int i) = delete; // COMPLIANT - constexpr VariantMemberInitConstexpr(int i, LiteralClass lc) {} // COMPLIANT -private: - union { - int i = 0; - short s; - }; -}; +extern int random(); +constexpr int add(int x, int y) { return x + y; } +// Example with compile time constant literal value as default argument +constexpr int add1(int x, int y = 1) { return x + y; } +// Example with compile time constant function call as default argument +constexpr int add2(int x, int y = add(add1(1), 2)) { return x + y; } +// Example with non compile time constant function call as default argument +constexpr int add3(int x, int y = random()) { return x + y; } +// Example with compile time constant literal value as default arguments +constexpr int add4(int x = 1, int y = 2) { return x + y; } + +constexpr void fp_reported_in_466(int p) { + int l1 = add(1, 2); // NON_COMPLIANT + int l2 = add(1, p); // COMPLIANT + + int l3 = 0; + if (p > 0) { + l3 = 1; + } else { + l3 = p; + } -// Variant members are not initialized at declaration, so we can only mark the -// constructors as constexpr if we explicitly initialize the variant member -class VariantMemberNotInit { -public: - VariantMemberNotInit() = default; // COMPLIANT - VariantMemberNotInit(int pi) = delete; // COMPLIANT - VariantMemberNotInit(int pi, LiteralClass lc) {} // COMPLIANT - VariantMemberNotInit(LiteralClass lc, int pi) : i(pi) {} // NON_COMPLIANT - constexpr VariantMemberNotInit(LiteralClass lc, short pi) // COMPLIANT - : i(pi) {} + constexpr int l4 = add(1, 2); // COMPLIANT + + int l5 = + add(l3, 2); // COMPLIANT - l3 is not compile time constant on all paths + int l6 = add(l4, 2); // NON_COMPLIANT + int l7 = add(l1, 2); // COMPLIANT - l1 is not constexpr + int l8 = + add1(l4, 2); // NON_COMPLIANT - all arguments are compile time constants + int l9 = add1(l1, 2); // COMPLIANT - l1 is not constexpr + int l10 = add1(l4); // NON_COMPLIANT - argument and the default value of the + // second argument are compile time constants + int l11 = add1(l1); // COMPLIANT - l1 is not constexpr + int l12 = add1(1); // NON_COMPLIANT + int l13 = + add1(1, l3); // COMPLIANT - l3 is not compile time constant on all paths + int l14 = + add1(l3); // COMPLIANT - l3 is not compile time constant on all paths + int l15 = add2(1); // NON_COMPLIANT - provided argument and default value are + // compile time constants + int l16 = add2(1, 2); // NON_COMPLIANT + int l17 = add2(l4, 2); // NON_COMPLIANT + int l18 = add2(l1, 2); // COMPLIANT - l1 is not constexpr + int l19 = + add2(l3); // COMPLIANT - l3 is not compile time constant on all paths + int l20 = + add2(l3, 1); // COMPLIANT - l3 is not compile time constant on all paths + int l21 = add3(1, 1); // NON_COMPLIANT + int l22 = add3(1); // COMPLIANT - default value for second argument is not a + // compile time constant + int l23 = + add3(1, l3); // COMPLIANT - l3 is not compile time constant on all paths + int l24 = add4(); // NON_COMPLIANT - default values are compile time constants + int l25 = add4(1); // NON_COMPLIANT - default value for second argument is a + // compile time constant + int l26 = + add4(1, l3); // COMPLIANT - l3 is not compile time constant on all paths +} -private: - union { - int i; - short s; - }; -}; +template T *init(T **t) {} -class ExcludedCases { -public: - ~ExcludedCases() {} // COMPLIANT +template T *init() { + T *t = nullptr; // COMPLIANT - initialized below + init(&t); // Init is ignored in uninitialized template + return t; +} - void operator=(ExcludedCases &) {} // COMPLIANT - void operator=(ExcludedCases &&) {} // COMPLIANT -}; \ No newline at end of file +void test_template_instantiation() { int *t = init(); } diff --git a/cpp/autosar/test/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.expected b/cpp/autosar/test/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.expected index 9d6a710449..d845df142d 100644 --- a/cpp/autosar/test/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.expected +++ b/cpp/autosar/test/rules/A7-1-3/CvQualifiersNotPlacedOnTheRightHandSide.expected @@ -1,3 +1,4 @@ | test.cpp:9:16:9:19 | definition of ptr1 | There is possibly a const or volatile specifier on the left hand side of typedef name $@. | test.cpp:1:7:1:12 | intptr | intptr | | test.cpp:10:19:10:22 | definition of ptr2 | There is possibly a const or volatile specifier on the left hand side of typedef name $@. | test.cpp:1:7:1:12 | intptr | intptr | | test.cpp:19:21:19:24 | definition of ptr8 | There is possibly a const or volatile specifier on the left hand side of typedef name $@. | test.cpp:3:7:3:17 | constintptr | constintptr | +| test.cpp:32:23:32:26 | definition of u32d | There is possibly a const or volatile specifier on the left hand side of typedef name uint32_t. | test.cpp:32:23:32:26 | definition of u32d | | diff --git a/cpp/autosar/test/rules/A7-1-3/test.cpp b/cpp/autosar/test/rules/A7-1-3/test.cpp index 621a64115d..39f53b8623 100644 --- a/cpp/autosar/test/rules/A7-1-3/test.cpp +++ b/cpp/autosar/test/rules/A7-1-3/test.cpp @@ -18,4 +18,16 @@ void f() { constintptr const ptr7 = &l; // COMPLIANT const constintptr ptr8 = &l; // NON_COMPLIANT inttypedef ptr9 = l; // COMPLIANT +} + +#include + +void false_positive() { + std::uint8_t u8{0}; + + auto const u32 = static_cast(u8); // COMPLIANT - auto ignored + std::uint32_t const u32b = static_cast(u8); // COMPLIANT + + const auto u32c = static_cast(u8); // COMPLIANT - auto ignored + const std::uint32_t u32d = static_cast(u8); // NON_COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.expected b/cpp/autosar/test/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.expected index df7b4aafcd..7a5da3abac 100644 --- a/cpp/autosar/test/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.expected +++ b/cpp/autosar/test/rules/A7-1-5/AutoSpecifierNotUsedAppropriatelyInVariableDefinition.expected @@ -4,3 +4,4 @@ | test.cpp:27:8:27:8 | a | Use of auto in variable definition is not the result of a function call, lambda expression, or non-fundamental type initializer. | | test.cpp:28:8:28:8 | b | Use of auto in variable definition is not the result of a function call, lambda expression, or non-fundamental type initializer. | | test.cpp:81:10:81:10 | a | Use of auto in variable definition is not the result of a function call, lambda expression, or non-fundamental type initializer. | +| test.cpp:111:13:111:13 | a | Use of auto in variable definition is not the result of a function call, lambda expression, or non-fundamental type initializer. | diff --git a/cpp/autosar/test/rules/A7-1-5/test.cpp b/cpp/autosar/test/rules/A7-1-5/test.cpp index 4f85b3eb4a..ba2ce2be81 100644 --- a/cpp/autosar/test/rules/A7-1-5/test.cpp +++ b/cpp/autosar/test/rules/A7-1-5/test.cpp @@ -105,4 +105,31 @@ void instantiate() { Test_381 t381; t381.test_381_1(); t381.test_381_2(); -} \ No newline at end of file +} +class Foo {}; +void test_loop() { + for (auto a : {8, 9, 10}) { // NON_COMPLIANT - a is initialized with a + // non-constant initializer + a; + } + + std::vector v = {1, 2, 3}; + for (auto &a : v) { // COMPLIANT - a is intialized with a function call + a; + } + + Foo f1; + Foo f2; + for (auto &a : {f1, f2}) { // COMPLIANT - initialized with a non-fundamental + // type + a; + } +} + +template void test_template(std::vector v2) { + for (auto &a : v2) { // COMPLIANT - a is intialized with a function call + a; + } +} + +void test_template_instantiation() { test_template({1, 2, 3}); } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.expected b/cpp/autosar/test/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.expected index c7abb3ce6f..b84f71e952 100644 --- a/cpp/autosar/test/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.expected +++ b/cpp/autosar/test/rules/A7-1-7/IdentifierDeclarationAndInitializationNotOnSeparateLines.expected @@ -26,3 +26,4 @@ | test.cpp:66:12:66:12 | definition of g | Expression statement and identifier are on the same line. | | test.cpp:82:7:82:10 | definition of S3_a | Expression statement and identifier are on the same line. | | test.cpp:82:17:82:20 | definition of S3_b | Expression statement and identifier are on the same line. | +| test.cpp:154:24:154:24 | definition of y | Expression statement and identifier are on the same line. | diff --git a/cpp/autosar/test/rules/A7-1-7/test.cpp b/cpp/autosar/test/rules/A7-1-7/test.cpp index de6e013291..80f6542b11 100644 --- a/cpp/autosar/test/rules/A7-1-7/test.cpp +++ b/cpp/autosar/test/rules/A7-1-7/test.cpp @@ -147,3 +147,19 @@ struct s_357 { // clang-format on } }; + +void example_function() { f1(); } // COMPLIANT + +// clang-format off +typedef struct x { int y; } z; //COMPLIANT - for struct typedef and struct var //NON_COMPLIANT - for struct all on one line +// clang-format on + +#define foo(x, y) \ + x++; \ + y++; + +void test_foo() { + int a = 1; + int b = 1; + foo(a, b); // COMPLIANT +} diff --git a/cpp/autosar/test/rules/A7-2-1/NonEnumeratorEnumValue.expected b/cpp/autosar/test/rules/A7-2-1/NonEnumeratorEnumValue.expected index 9c99c44897..6ac5cfca86 100644 --- a/cpp/autosar/test/rules/A7-2-1/NonEnumeratorEnumValue.expected +++ b/cpp/autosar/test/rules/A7-2-1/NonEnumeratorEnumValue.expected @@ -7,4 +7,4 @@ | test.cpp:27:12:27:25 | (Foo)... | Cast to enum $@ with from expression with range 0...3 which may not be one of the enumerator values in function test_bitwise_or. | test.cpp:2:6:2:8 | Foo | Foo | | test.cpp:28:12:28:25 | (Foo)... | Cast to enum $@ with from expression with range 0...7 which may not be one of the enumerator values in function test_bitwise_or. | test.cpp:2:6:2:8 | Foo | Foo | | test.cpp:39:12:39:17 | (Bar)... | Cast to enum $@ with from expression with range 1...1 which may not be one of the enumerator values in function test_constant. | test.cpp:5:6:5:8 | Bar | Bar | -| test.cpp:41:12:41:17 | (Bar)... | Cast to enum $@ with from expression with value 1_+ which is not one of the enumerator values in function test_constant. | test.cpp:5:6:5:8 | Bar | Bar | +| test.cpp:41:12:41:17 | (Bar)... | Cast to enum $@ with from expression with value 1 which is not one of the enumerator values in function test_constant. | test.cpp:5:6:5:8 | Bar | Bar | diff --git a/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.expected b/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.expected deleted file mode 100644 index d9c51d2b77..0000000000 --- a/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:1:6:1:6 | C | Base type of enumeration is not explicitly specified. | -| test.cpp:3:12:3:12 | A | Base type of enumeration is not explicitly specified. | diff --git a/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.qlref b/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.qlref deleted file mode 100644 index 1ed510a506..0000000000 --- a/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.testref b/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.testref new file mode 100644 index 0000000000..d7a73fd488 --- /dev/null +++ b/cpp/autosar/test/rules/A7-2-2/EnumerationUnderlyingBaseTypeNotExplicitlyDefined.testref @@ -0,0 +1 @@ +cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-2-2/test.cpp b/cpp/autosar/test/rules/A7-2-2/test.cpp deleted file mode 100644 index e55ab81c6d..0000000000 --- a/cpp/autosar/test/rules/A7-2-2/test.cpp +++ /dev/null @@ -1,8 +0,0 @@ -enum C // NON_COMPLIANT -{ c1 }; -enum class A // NON_COMPLIANT -{ a1 }; -enum class B : int // COMPLIANT -{ b1 }; -enum D : int // COMPLIANT -{ d1 }; diff --git a/cpp/autosar/test/rules/A7-2-3/EnumerationsNotDeclaredAsScopedEnumClasses.expected b/cpp/autosar/test/rules/A7-2-3/EnumerationsNotDeclaredAsScopedEnumClasses.expected index 3d3d208004..1e7b97e1f0 100644 --- a/cpp/autosar/test/rules/A7-2-3/EnumerationsNotDeclaredAsScopedEnumClasses.expected +++ b/cpp/autosar/test/rules/A7-2-3/EnumerationsNotDeclaredAsScopedEnumClasses.expected @@ -1,2 +1,2 @@ | test.cpp:1:6:1:6 | C | Enum C is not a scoped enum. | -| test.cpp:11:6:11:6 | D | Enum D is not a scoped enum. | +| test.cpp:21:6:21:6 | D | Enum D is not a scoped enum. | diff --git a/cpp/autosar/test/rules/A7-2-3/test.cpp b/cpp/autosar/test/rules/A7-2-3/test.cpp index 3a9dd414a3..76e3a9d78d 100644 --- a/cpp/autosar/test/rules/A7-2-3/test.cpp +++ b/cpp/autosar/test/rules/A7-2-3/test.cpp @@ -1,12 +1,24 @@ enum C // NON_COMPLIANT -{ c1 }; +{ + c1 +}; enum class A // COMPLIANT - Violates a different rule (A7-2-2) -{ a1 }; +{ + a1 +}; enum struct A1 // COMPLIANT - Violates a different rule (A7-2-2) -{ a12 }; +{ + a12 +}; enum class B : int // COMPLIANT -{ b1 }; +{ + b1 +}; enum struct B1 : int // COMPLIANT -{ b12 }; +{ + b12 +}; enum D : int // NON_COMPLIANT -{ d1 }; \ No newline at end of file +{ + d1 +}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-2-4/NoneFirstOrAllEnumeratorsNotInitialized.expected b/cpp/autosar/test/rules/A7-2-4/NoneFirstOrAllEnumeratorsNotInitialized.expected index dd75f69bff..f4ce14aac2 100644 --- a/cpp/autosar/test/rules/A7-2-4/NoneFirstOrAllEnumeratorsNotInitialized.expected +++ b/cpp/autosar/test/rules/A7-2-4/NoneFirstOrAllEnumeratorsNotInitialized.expected @@ -1,4 +1,4 @@ -| test.cpp:9:12:9:13 | A2 | Neither none, the first, or all enumerated constants are initialized in this enumeration. | -| test.cpp:33:14:33:15 | B3 | Neither none, the first, or all enumerated constants are initialized in this enumeration. | -| test.cpp:45:6:45:6 | D | Neither none, the first, or all enumerated constants are initialized in this enumeration. | -| test.cpp:53:6:53:7 | E1 | Neither none, the first, or all enumerated constants are initialized in this enumeration. | +| test.cpp:13:12:13:13 | A2 | Neither none, the first, or all enumerated constants are initialized in this enumeration. | +| test.cpp:47:14:47:15 | B3 | Neither none, the first, or all enumerated constants are initialized in this enumeration. | +| test.cpp:63:6:63:6 | D | Neither none, the first, or all enumerated constants are initialized in this enumeration. | +| test.cpp:75:6:75:7 | E1 | Neither none, the first, or all enumerated constants are initialized in this enumeration. | diff --git a/cpp/autosar/test/rules/A7-2-4/test.cpp b/cpp/autosar/test/rules/A7-2-4/test.cpp index 943a7944cb..51f7df642c 100644 --- a/cpp/autosar/test/rules/A7-2-4/test.cpp +++ b/cpp/autosar/test/rules/A7-2-4/test.cpp @@ -1,55 +1,79 @@ enum class A // COMPLIANT -{ a1 }; +{ + a1 +}; enum class A1 // COMPLIANT -{ a1 = 0, +{ + a1 = 0, a2 = 1, - a3 = 2 }; + a3 = 2 +}; enum class A2 // NON_COMPLIANT -{ a1, +{ + a1, a2 = 0, - a3 }; + a3 +}; enum class A3 // COMPLIANT -{ a1 = 0, +{ + a1 = 0, a2, - a3 }; + a3 +}; enum class B : int // COMPLIANT -{ b1 }; +{ + b1 +}; enum class B1 : int // COMPLIANT -{ b1, +{ + b1, b2, - b3 }; + b3 +}; enum class B2 : int // COMPLIANT -{ b1 = 0, +{ + b1 = 0, b2, - b3 }; + b3 +}; int f() { enum class B3 : int // NON_COMPLIANT - { b1, + { + b1, b2 = 0, - b3 = 0 }; + b3 = 0 + }; return 0; } enum C // COMPLIANT -{ c1, - c2 }; +{ + c1, + c2 +}; enum D // NON_COMPLIANT -{ d1, - d2 = 0 }; +{ + d1, + d2 = 0 +}; enum E : int // COMPLIANT -{ e1, - e2 }; +{ + e1, + e2 +}; enum E1 : int // NON_COMPLIANT -{ e11, - e22 = 0 }; \ No newline at end of file +{ + e11, + e22 = 0 +}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.expected b/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.expected deleted file mode 100644 index ea0f998533..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:42:6:42:7 | declaration of f1 | Definition for 'f1' is not available for unqualified lookup because it is declared after $@ | test.cpp:39:12:39:13 | using f1 | using-declaration | diff --git a/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.qlref b/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.qlref deleted file mode 100644 index 0fe94e847c..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.testref b/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.testref new file mode 100644 index 0000000000..7a5ae74d2e --- /dev/null +++ b/cpp/autosar/test/rules/A7-3-1/DefinitionNotConsideredForUnqualifiedLookup.testref @@ -0,0 +1 @@ +cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.expected b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.expected deleted file mode 100644 index 85ede1ada2..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:16:8:16:9 | declaration of f1 | Declaration for member 'f1' hides non-overridable inherited member function $@ | test.cpp:7:8:7:9 | declaration of f1 | f1 | diff --git a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.qlref b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.qlref deleted file mode 100644 index d94c3c0b0a..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.testref b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.testref new file mode 100644 index 0000000000..2fb9608ee8 --- /dev/null +++ b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedNonOverridableMemberFunction.testref @@ -0,0 +1 @@ +cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.expected b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.expected deleted file mode 100644 index c2f8ee1f40..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.expected +++ /dev/null @@ -1,5 +0,0 @@ -| test.cpp:18:8:18:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:9:16:9:17 | declaration of f2 | f2 | -| test.cpp:18:8:18:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:11:16:11:17 | declaration of f2 | f2 | -| test.cpp:23:8:23:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:9:16:9:17 | declaration of f2 | f2 | -| test.cpp:23:8:23:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:10:16:10:17 | declaration of f2 | f2 | -| test.cpp:23:8:23:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:11:16:11:17 | declaration of f2 | f2 | diff --git a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.qlref b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.qlref deleted file mode 100644 index 57d16c4e90..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-3-1/HiddenInheritedOverridableMemberFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.testref b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.testref new file mode 100644 index 0000000000..e768ced8d3 --- /dev/null +++ b/cpp/autosar/test/rules/A7-3-1/HiddenInheritedOverridableMemberFunction.testref @@ -0,0 +1 @@ +cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-3-1/test.cpp b/cpp/autosar/test/rules/A7-3-1/test.cpp deleted file mode 100644 index 7aaeb26567..0000000000 --- a/cpp/autosar/test/rules/A7-3-1/test.cpp +++ /dev/null @@ -1,62 +0,0 @@ -struct S1 { - int i; -}; - -class C1 { -public: - void f1(int); - - virtual void f2(int); - virtual void f2(double); - virtual void f2(S1); -}; - -class C2 : public C1 { -public: - void f1(double); // NON_COMPLIANT - - void f2(double) override; // NON_COMPLIANT -}; - -class C3 : public C1 { -public: - void f2(char *); // NON_COMPLIANT -}; - -class C4 : public C1 { -public: - using C1::f1; - void f1(double); // COMPLIANT - - using C1::f2; - void f2(double) override; // COMPLIANT -}; - -namespace ns1 { -void f1(int); -} - -using ns1::f1; - -namespace ns1 { -void f1(double); // NON_COMPLIANT -} - -void f1() { - C2 l1; - l1.f1(0); // calls C2::f1(double) instead of C1::f1(int) - l1.f2(0); // calls C2::f2(double) instead of C1::f2(int) - // S1 s1; - // l1.f2(s1); Won't compile because there is no suitable conversion fro S1 to - // double. - C1 &l2{l1}; - l2.f1(0); // calls C1::f1(int) - - C4 l3; - l3.f1(0); // calls C1::f1(int) - l3.f1(0.0); // calls C3::f1(double) - l3.f2(0); // calls C1::f2(int) - l3.f2(0.0); // calls C3::f2(double) - S1 l4; - l3.f2(l4); // calls C1:f2(S1) -} diff --git a/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.qlref b/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.qlref deleted file mode 100644 index 286e62bd18..0000000000 --- a/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-4-1/AsmDeclarationUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.testref b/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.testref new file mode 100644 index 0000000000..f643f6a9c7 --- /dev/null +++ b/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-5-1/InvalidFunctionReturnType.expected b/cpp/autosar/test/rules/A7-5-1/InvalidFunctionReturnType.expected index b6d9490803..3287ba88d1 100644 --- a/cpp/autosar/test/rules/A7-5-1/InvalidFunctionReturnType.expected +++ b/cpp/autosar/test/rules/A7-5-1/InvalidFunctionReturnType.expected @@ -1,2 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (InvalidFunctionReturnType.ql:27,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (InvalidFunctionReturnType.ql:27,23-31) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (InvalidFunctionReturnType.ql:27,51-59) | test.cpp:5:3:5:11 | return ... | Function test_refconst_return returns a reference or a pointer to $@ that is passed by reference to const. | test.cpp:4:44:4:44 | x | parameter | | test.cpp:8:3:8:14 | return ... | Function test_ptrconst_return returns a reference or a pointer to $@ that is passed by reference to const. | test.cpp:7:44:7:44 | x | parameter | diff --git a/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.qlref b/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.qlref deleted file mode 100644 index 10fccea7f7..0000000000 --- a/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A7-5-2/RecursiveFunctions.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.testref b/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.testref new file mode 100644 index 0000000000..f459a29bf1 --- /dev/null +++ b/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A8-4-11/SmartPointerAsParameterWithoutLifetimeSemantics.expected b/cpp/autosar/test/rules/A8-4-11/SmartPointerAsParameterWithoutLifetimeSemantics.expected index b751d81835..2ce56fdce9 100644 --- a/cpp/autosar/test/rules/A8-4-11/SmartPointerAsParameterWithoutLifetimeSemantics.expected +++ b/cpp/autosar/test/rules/A8-4-11/SmartPointerAsParameterWithoutLifetimeSemantics.expected @@ -1,3 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (SmartPointerAsParameterWithoutLifetimeSemantics.ql:47,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (SmartPointerAsParameterWithoutLifetimeSemantics.ql:56,5-13) | test.cpp:7:41:7:43 | up1 | Function $@ takes smart pointer parameter 'up1' but does not implement any lifetime-affecting operations. | test.cpp:7:6:7:18 | smart_ptr_get | smart_ptr_get | | test.cpp:16:53:16:55 | sp1 | Function $@ takes smart pointer parameter 'sp1' but does not implement any lifetime-affecting operations. | test.cpp:16:6:16:29 | smart_ptr_ref_assign_ref | smart_ptr_ref_assign_ref | | test.cpp:28:55:28:57 | sp1 | Function $@ takes smart pointer parameter 'sp1' but does not implement any lifetime-affecting operations. | test.cpp:28:6:28:31 | smart_ptr_ref_noncompliant | smart_ptr_ref_noncompliant | diff --git a/cpp/autosar/test/rules/A8-4-12/UniquePtrPassedToFunctionWithImproperSemantics.expected b/cpp/autosar/test/rules/A8-4-12/UniquePtrPassedToFunctionWithImproperSemantics.expected index a01b93335d..0a8ead4af8 100644 --- a/cpp/autosar/test/rules/A8-4-12/UniquePtrPassedToFunctionWithImproperSemantics.expected +++ b/cpp/autosar/test/rules/A8-4-12/UniquePtrPassedToFunctionWithImproperSemantics.expected @@ -1,3 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UniquePtrPassedToFunctionWithImproperSemantics.ql:41,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UniquePtrPassedToFunctionWithImproperSemantics.ql:51,5-13) | test.cpp:13:55:13:56 | v1 | Parameter of type std::unique_ptr passed as lvalue reference but not used to modify underlying object. | | test.cpp:17:47:17:48 | v1 | Parameter of type std::unique_ptr passed as lvalue reference but not used to modify underlying object. | | test.cpp:22:27:22:28 | v1 | Parameter of type std::unique_ptr passed as lvalue reference but not used to modify underlying object. | diff --git a/cpp/autosar/test/rules/A8-4-5/test.cpp b/cpp/autosar/test/rules/A8-4-5/test.cpp index 0bb81d7851..65468aab22 100644 --- a/cpp/autosar/test/rules/A8-4-5/test.cpp +++ b/cpp/autosar/test/rules/A8-4-5/test.cpp @@ -48,7 +48,7 @@ class TestClass4 { // template type template -T test_forward(X &&... f) { // forward parameter +T test_forward(X &&...f) { // forward parameter return T{std::forward(f)...}; // COMPLIANT } diff --git a/cpp/autosar/test/rules/A8-4-6/test.cpp b/cpp/autosar/test/rules/A8-4-6/test.cpp index 489ac5d498..bc01e083b0 100644 --- a/cpp/autosar/test/rules/A8-4-6/test.cpp +++ b/cpp/autosar/test/rules/A8-4-6/test.cpp @@ -10,8 +10,8 @@ class TestClass2 { explicit TestClass2(const std::vector &v); }; // template type -template T make(X &&... x) { // forward param - return T{std::forward(x)...}; // COMPLIANT +template T make(X &&...x) { // forward param + return T{std::forward(x)...}; // COMPLIANT } // template type template T make2(U &&x) { // forward param diff --git a/cpp/autosar/test/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.expected b/cpp/autosar/test/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.expected index c89e65db90..9c21ae0a14 100644 --- a/cpp/autosar/test/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.expected +++ b/cpp/autosar/test/rules/A8-4-7/InParametersForCheapToCopyTypesNotPassedByValue.expected @@ -1 +1 @@ -| test.cpp:20:19:20:21 | f5a | Parameter f5a is the trivially copyable type const S1 but it is passed by reference instead of by value. | +| test.cpp:22:19:22:21 | f5a | Parameter 'f5a' is the trivially copyable type $@ but it is passed by reference instead of by value. | file://:0:0:0:0 | const S1 | const S1 | diff --git a/cpp/autosar/test/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.expected b/cpp/autosar/test/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.expected index 92e3a439e7..3a3c791577 100644 --- a/cpp/autosar/test/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.expected +++ b/cpp/autosar/test/rules/A8-4-7/InParametersForNotCheapToCopyTypesNotPassedByReference.expected @@ -1,2 +1,3 @@ -| test.cpp:23:12:23:14 | f8a | Parameter f8a is the trivially non-copyable type $@ but it is passed by value instead of by reference. | test.cpp:14:8:14:9 | S2 | S2 | -| test.cpp:27:13:27:16 | f12a | Parameter f12a is the trivially non-copyable type $@ but it is passed by value instead of by reference. | test.cpp:14:8:14:9 | S2 | S2 | +| test.cpp:25:12:25:14 | f8a | Parameter 'f8a' is the trivially non-copyable type $@ but it is passed by value instead of by reference. | test.cpp:16:8:16:9 | S2 | S2 | +| test.cpp:29:13:29:16 | f12a | Parameter 'f12a' is the trivially non-copyable type $@ but it is passed by value instead of by reference. | test.cpp:16:8:16:9 | S2 | S2 | +| test.cpp:70:13:70:16 | f17a | Parameter 'f17a' is the trivially non-copyable type $@ but it is passed by value instead of by reference. | test.cpp:60:8:60:9 | S4 | S4 | diff --git a/cpp/autosar/test/rules/A8-4-7/test.cpp b/cpp/autosar/test/rules/A8-4-7/test.cpp index 70829ef907..48b574805e 100644 --- a/cpp/autosar/test/rules/A8-4-7/test.cpp +++ b/cpp/autosar/test/rules/A8-4-7/test.cpp @@ -1,5 +1,7 @@ +#include #include #include +#include void f1(std::uint8_t f1a) {} // COMPLIANT void f2(std::uint16_t f2a) {} // COMPLIANT @@ -37,4 +39,43 @@ inline S1 Value(size_t n, const char *data) {} // COMPLIANT struct A { int n; A(const A &a) : n(a.n) {} // COMPLIANT user-defined copy ctor + A(const A &&other_a); // COMPLIANT user-defined move ctor }; + +class C1 {}; + +class C2 : public C1 { +public: + C2 &operator=(const C2 &); // COMPLIANT +}; + +void f13(double f13a) {} // COMPLIANT +void f14(const double f14a) {} // COMPLIANT + +struct S3 { + int x; + S3() : x(0) {} // COMPLIANT +}; + +struct S4 { + ~S4() {} // non-trivial destructor +}; + +struct S5 { + const int y; + S5(int value) : y(value) {} +}; + +void f15(S3 f15a) {} // COMPLIANT +void f17(S4 f17a) {} // NON_COMPLIANT (S4 has a non-trivial destructor) +void f18(S5 f18a) {} // COMPLIANT + +#include +class A8_4_7 { +public: + std::array values; +}; +void fp_reported_in_82( + const A8_4_7 &a847) noexcept { // COMPLIANT - larger than 2 words + std::cout << a847.values[0] << std::endl; +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A8-4-8/OutputParametersUsed.expected b/cpp/autosar/test/rules/A8-4-8/OutputParametersUsed.expected index 6b0df8d0dd..221def5a42 100644 --- a/cpp/autosar/test/rules/A8-4-8/OutputParametersUsed.expected +++ b/cpp/autosar/test/rules/A8-4-8/OutputParametersUsed.expected @@ -1,5 +1,6 @@ -| test.cpp:5:22:5:24 | str | Out parameter str that is modified before being read. | -| test.cpp:16:14:16:14 | i | Out parameter i that is modified before being read. | -| test.cpp:21:14:21:14 | i | Out parameter i that is modified before being read. | -| test.cpp:29:12:29:12 | a | Out parameter a that is modified before being read. | -| test.cpp:33:12:33:12 | a | Out parameter a that is modified before being read. | +| test.cpp:5:22:5:24 | str | Out parameter 'str' used. | +| test.cpp:8:22:8:24 | str | Out parameter 'str' used. | +| test.cpp:16:14:16:14 | i | Out parameter 'i' used. | +| test.cpp:21:14:21:14 | i | Out parameter 'i' used. | +| test.cpp:29:12:29:12 | a | Out parameter 'a' used. | +| test.cpp:33:12:33:12 | a | Out parameter 'a' used. | diff --git a/cpp/autosar/test/rules/A8-4-8/test.cpp b/cpp/autosar/test/rules/A8-4-8/test.cpp index e41a61704b..fd2e5e8763 100644 --- a/cpp/autosar/test/rules/A8-4-8/test.cpp +++ b/cpp/autosar/test/rules/A8-4-8/test.cpp @@ -5,7 +5,7 @@ void f(int &i) { // COMPLIANT void f1(std::string &str) { // NON_COMPLIANT str = "replacement"; } -void f2(std::string &str) { // COMPLIANT +void f2(std::string &str) { // NON_COMPLIANT str += "suffix"; } @@ -37,3 +37,41 @@ void f7(A &a) { // NON_COMPLIANT void f8(int i) { // COMPLIANT i += 1; } + +constexpr A &operator|=( + A &lhs, + const A &rhs) noexcept { // COMPLIANT - non-member user defined assignment + // operators are considered an exception. + return lhs; +} + +enum class byte : unsigned char {}; +constexpr byte & +operator|(const byte &lhs, + const byte &rhs) noexcept { // COMPLIANT - parameters are const + // qualified references + return lhs | rhs; +} +constexpr byte &operator|=( + byte &lhs, + const byte rhs) noexcept { // COMPLIANT - non-member user defined assignment + // operators are considered an exception. + lhs = (lhs | rhs); + return lhs; +} + +#include +std::ostream &operator<<(std::ostream &os, + const byte &obj) { // COMPLIANT - insertion operators + // are considered an exception. + std::ostream other; + os = other; // simulate modification + return os; +} + +std::istream &operator>>(std::istream &is, + byte &obj) { // COMPLIANT - extraction operators are + // considered an exception. + obj = static_cast('a'); // simulate modification + return is; +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.expected b/cpp/autosar/test/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.expected index e3cfa71bb7..25fe77d9a5 100644 --- a/cpp/autosar/test/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.expected +++ b/cpp/autosar/test/rules/A8-4-9/InOutParametersDeclaredAsTNotModified.expected @@ -1,3 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (InOutParametersDeclaredAsTNotModified.ql:50,7-15) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (InOutParametersDeclaredAsTNotModified.ql:64,7-15) | test.cpp:4:13:4:13 | i | In-out parameter i that is not written to. | | test.cpp:7:22:7:24 | str | In-out parameter str that is not read from. | | test.cpp:18:14:18:14 | i | In-out parameter i that is not read from. | diff --git a/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.qlref b/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.qlref deleted file mode 100644 index eb351d9e36..0000000000 --- a/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A8-5-4/ConfusingUseOfInitializerListConstructors.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.testref b/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.testref new file mode 100644 index 0000000000..49b73d06a9 --- /dev/null +++ b/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.testref @@ -0,0 +1 @@ +cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.expected b/cpp/autosar/test/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.expected index 04c1f35a45..70892c12c8 100644 --- a/cpp/autosar/test/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.expected +++ b/cpp/autosar/test/rules/A9-3-1/ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.expected @@ -1,3 +1,6 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql:73,3-11) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql:73,23-31) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ReturnsNonConstRawPointersOrReferencesToPrivateOrProtectedData.ql:73,46-54) | test.cpp:20:8:20:12 | getB2 | Member function A::getB2 $@ a non-const raw pointer or reference to a private or protected $@. | test.cpp:20:25:20:25 | b | returns | test.cpp:54:7:54:7 | b | field | | test.cpp:22:8:22:12 | getB3 | Member function A::getB3 $@ a non-const raw pointer or reference to a private or protected $@. | test.cpp:22:25:22:26 | & ... | returns | test.cpp:54:7:54:7 | b | field | | test.cpp:24:8:24:13 | getB33 | Member function A::getB33 $@ a non-const raw pointer or reference to a private or protected $@. | test.cpp:26:12:26:13 | bb | returns | test.cpp:54:7:54:7 | b | field | diff --git a/cpp/autosar/test/rules/M0-1-1/UnreachableCode.expected b/cpp/autosar/test/rules/M0-1-1/UnreachableCode.expected deleted file mode 100644 index 4b8ee61b16..0000000000 --- a/cpp/autosar/test/rules/M0-1-1/UnreachableCode.expected +++ /dev/null @@ -1,5 +0,0 @@ -| test.cpp:14:3:14:12 | declaration | This statement in function $@ is unreachable. | test.cpp:12:5:12:21 | test_after_return | test_after_return | -| test.cpp:18:10:19:12 | { ... } | This statement in function $@ is unreachable. | test.cpp:17:5:17:27 | test_constant_condition | test_constant_condition | -| test.cpp:26:10:27:12 | { ... } | This statement in function $@ is unreachable. | test.cpp:25:24:25:24 | f | f | -| test.cpp:47:12:48:14 | { ... } | This statement in function $@ is unreachable. | test.cpp:46:7:46:8 | h1 | h1 | -| test.cpp:52:12:53:14 | { ... } | This statement in function $@ is unreachable. | test.cpp:51:7:51:8 | h2 | h2 | diff --git a/cpp/autosar/test/rules/M0-1-1/UnreachableCode.qlref b/cpp/autosar/test/rules/M0-1-1/UnreachableCode.qlref deleted file mode 100644 index 449e3bbd42..0000000000 --- a/cpp/autosar/test/rules/M0-1-1/UnreachableCode.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M0-1-1/UnreachableCode.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-1/UnreachableCode.testref b/cpp/autosar/test/rules/M0-1-1/UnreachableCode.testref new file mode 100644 index 0000000000..7e55dbcd9b --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-1/UnreachableCode.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unreachablecode/UnreachableCode.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-1/test.cpp b/cpp/autosar/test/rules/M0-1-1/test.cpp deleted file mode 100644 index 715947b5be..0000000000 --- a/cpp/autosar/test/rules/M0-1-1/test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -void test_switch(int p1) { - int l1 = 0; - switch (p1) { - l1 = p1; // NON_COMPLIANT[FALSE_NEGATIVE] - case 1: - break; - default: - break; - } -} - -int test_after_return() { - return 0; - int l1 = 0; // NON_COMPLIANT - function has returned by this point -} - -int test_constant_condition() { - if (0) { // NON_COMPLIANT - return 1; - } else { // COMPLIANT - return 2; - } -} - -template int f() { - if (0) { // NON_COMPLIANT - block is unreachable in all instances - return 3; - } - if (T::isVal()) { // COMPLIANT - block is reachable in at least one template - // instantiation, so we do not flag it as dead code - return 2; - } - - return 1; // COMPLIANT - block is reachable in the uninstantiated template -} - -class A { -public: - static bool isVal() { return true; } -}; - -void test_f() { f(); } - -template class B { -public: - int h1(T t) { - if (0) { // NON_COMPLIANT - block is unreachable in all instances - return 3; - } - } - int h2() { - if (0) { // NON_COMPLIANT - block is unreachable in all instances - return 3; - } - } -}; - -class C {}; - -void test_template() { - B b1; - A a; - b1.h1(a); - b1.h2(); - B b2; - C c; - b2.h1(c); - b2.h2(); -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-10.1/UnusedFunction.expected b/cpp/autosar/test/rules/M0-1-10.1/UnusedFunction.expected new file mode 100644 index 0000000000..3f58065520 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-10.1/UnusedFunction.expected @@ -0,0 +1 @@ +| test.cpp:23:14:23:26 | uncalled_func | Function uncalled_func is never called. | diff --git a/cpp/autosar/test/rules/M0-1-10.1/UnusedFunction.qlref b/cpp/autosar/test/rules/M0-1-10.1/UnusedFunction.qlref new file mode 100644 index 0000000000..519660f289 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-10.1/UnusedFunction.qlref @@ -0,0 +1 @@ +rules/M0-1-10/UnusedFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-10.1/test.cpp b/cpp/autosar/test/rules/M0-1-10.1/test.cpp new file mode 100644 index 0000000000..5b9c68a827 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-10.1/test.cpp @@ -0,0 +1,39 @@ +#include + +namespace mains { +static std::int32_t var; + +// @brief namespace_func +static void +namespace_func(void) noexcept { // COMPLIANT: Called from "main" below. + mains::var = -1; + return; +} +} // namespace mains + +std::int32_t func2() // COMPLIANT: Called from func1 +{ + return mains::var + 20; +} + +std::int32_t func1() { // COMPLIANT: Called from main + return mains::var + func2(); // func2 called here. +} + +std::int32_t uncalled_func() // NON_COMPLIANT: Not called. +{ + return mains::var + func1(); // func1 called here. +} + +// @brief main +// @return exit code +std::int32_t main(void) { + std::int32_t ret{0}; + try { + ret = func1(); // func1 called here. + mains::var += ret; + } catch (...) { + mains::namespace_func(); // namespace_func called here. + } + return ret; +} diff --git a/cpp/autosar/test/rules/M0-1-10/UnusedFunction.expected b/cpp/autosar/test/rules/M0-1-10/UnusedFunction.expected index d9ab0d38ac..912e2104e8 100644 --- a/cpp/autosar/test/rules/M0-1-10/UnusedFunction.expected +++ b/cpp/autosar/test/rules/M0-1-10/UnusedFunction.expected @@ -10,4 +10,5 @@ | test.cpp:50:5:50:6 | i3 | Function C::i3 is never called. | | test.cpp:51:8:51:9 | i4 | Function C::i4 is never called. | | test.cpp:52:15:52:16 | i5 | Function C::i5 is never called. | -| test.cpp:69:17:69:18 | g4 | Function g4 is never called. | +| test.cpp:79:6:79:21 | anUnusedFunction | Function anUnusedFunction is never called. | +| test.cpp:113:17:113:18 | g4 | Function g4 is never called. | diff --git a/cpp/autosar/test/rules/M0-1-10/UnusedSplMemberFunction.expected b/cpp/autosar/test/rules/M0-1-10/UnusedSplMemberFunction.expected new file mode 100644 index 0000000000..e2bf0acc79 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-10/UnusedSplMemberFunction.expected @@ -0,0 +1,2 @@ +| test.cpp:71:5:71:16 | ANestedClass | Special member function ANestedClass is never called. | +| test.cpp:82:5:82:22 | AnotherNestedClass | Special member function AnotherNestedClass is never called from a main function or entry point. | diff --git a/cpp/autosar/test/rules/M0-1-10/UnusedSplMemberFunction.qlref b/cpp/autosar/test/rules/M0-1-10/UnusedSplMemberFunction.qlref new file mode 100644 index 0000000000..899f00fda1 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-10/UnusedSplMemberFunction.qlref @@ -0,0 +1 @@ +rules/M0-1-10/UnusedSplMemberFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-10/test.cpp b/cpp/autosar/test/rules/M0-1-10/test.cpp index 748d2196ef..e1a19abf24 100644 --- a/cpp/autosar/test/rules/M0-1-10/test.cpp +++ b/cpp/autosar/test/rules/M0-1-10/test.cpp @@ -52,6 +52,50 @@ template class C { inline void i5() {} // NON_COMPLIANT - never used in any instantiation }; +#include "test.hpp" +#include + +template +constexpr bool aConstExprFunc() noexcept { // COMPLIANT + static_assert(std::is_trivially_copy_constructible() && + std::is_trivially_copy_constructible(), + "assert"); + return true; +} + +template class AClass { T anArr[val]; }; + +void aCalledFunc1() // COMPLIANT +{ + struct ANestedClass { + ANestedClass() noexcept(false) { // COMPLIANT: False Positive! + static_cast(0); + } + }; + static_assert(std::is_trivially_copy_constructible>(), + "Must be trivially copy constructible"); +} + +void anUnusedFunction() // NON_COMPLIANT +{ + struct AnotherNestedClass { + AnotherNestedClass() noexcept(false) { // NON_COMPLAINT + static_cast(0); + } + }; + AnotherNestedClass d; +} + +void aCalledFunc2() // COMPLIANT +{ + struct YetAnotherNestedClass { + YetAnotherNestedClass() noexcept(false) { + static_cast(0); + } // COMPLIANT + }; + YetAnotherNestedClass d; +}; + int main() { // COMPLIANT - this is a main like function which acts as an entry // point f3(); @@ -88,8 +132,37 @@ int main() { // COMPLIANT - this is a main like function which acts as an entry c1.getAT(); S s; c2.i1(s); + + int aVar; + aConstExprFunc(); + aCalledFunc1(); + aCalledFunc2(); } class M { public: M(const M &) = delete; // COMPLIANT - ignore if deleted -}; \ No newline at end of file +}; + +#include +int called_from_google_test_function( + int a_param) // COMPLIANT - called from TEST +{ + int something = a_param; + something++; + return something; +} + +TEST(sample_test, + called_from_google_test_function) // COMPLIANT - Google Test function +{ + bool pass = false; + if (called_from_google_test_function(0) >= 10) + pass = true; + struct a_nested_class_in_gtest { + a_nested_class_in_gtest() noexcept(false) { + static_cast(0); + } // COMPLIANT + }; + static_assert(std::is_trivially_copy_constructible(), + "assert"); +} diff --git a/cpp/autosar/test/rules/M0-1-10/test.hpp b/cpp/autosar/test/rules/M0-1-10/test.hpp new file mode 100644 index 0000000000..a2da990951 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-10/test.hpp @@ -0,0 +1,4 @@ +template +constexpr T aCalledFuncInHeader(T value) noexcept { // COMPLIANT + return static_cast(value); +} diff --git a/cpp/autosar/test/rules/M0-1-2/InfeasiblePath.expected b/cpp/autosar/test/rules/M0-1-2/InfeasiblePath.expected index a7ea70d28f..b5528014d1 100644 --- a/cpp/autosar/test/rules/M0-1-2/InfeasiblePath.expected +++ b/cpp/autosar/test/rules/M0-1-2/InfeasiblePath.expected @@ -2,13 +2,10 @@ | test.cpp:7:7:7:22 | ... <= ... | The false path is infeasible because a (max value: 4294967295) is always less than or equal to 4294967295 (minimum value: 4294967295). | | test.cpp:15:7:15:13 | ... < ... | The false path is infeasible because l1 (max value: 2) is always less than l2 (minimum value: 10). | | test.cpp:19:9:19:14 | ... < ... | The false path is infeasible because a (max value: 1) is always less than l2 (minimum value: 10). | -| test.cpp:33:7:33:7 | 0 | The true path is infeasible because this expression consists of constants which evaluate to false. | -| test.cpp:33:7:33:7 | 0 | The true path is infeasible because this expression consists of constants which evaluate to false. | -| test.cpp:33:7:33:7 | 0 | The true path is infeasible because this expression consists of constants which evaluate to false. | -| test.cpp:36:7:36:14 | call to isVal | The false path is infeasible because this expression consists of constants which evaluate to true. | -| test.cpp:36:7:36:14 | call to isVal | The false path is infeasible because this expression consists of constants which evaluate to true. | -| test.cpp:43:7:43:15 | call to isVal2 | The false path is infeasible because this expression consists of constants which evaluate to true. | -| test.cpp:43:7:43:15 | call to isVal2 | The true path is infeasible because this expression consists of constants which evaluate to false. | +| test.cpp:33:7:33:7 | 0 | The path is unreachable in a template. | | test.cpp:77:9:77:14 | ... < ... | The true path is infeasible because 0 (max value: 0) is always less than or equal to a (minimum value: 0). | | test.cpp:80:9:80:15 | ... >= ... | The false path is infeasible because 0 (max value: 0) is always less than or equal to a (minimum value: 0). | | test.cpp:86:9:86:14 | ... < ... | The true path is infeasible because 0 (max value: 0) is always less than or equal to a (minimum value: 0). | +| test.cpp:117:7:117:7 | 0 | The path is unreachable in a template. | +| test.cpp:123:7:123:8 | ! ... | The path is unreachable in a template. | +| test.cpp:137:7:137:12 | ... > ... | The path is unreachable in a template. | diff --git a/cpp/autosar/test/rules/M0-1-2/test.cpp b/cpp/autosar/test/rules/M0-1-2/test.cpp index f75c52ea52..f36cbc790d 100644 --- a/cpp/autosar/test/rules/M0-1-2/test.cpp +++ b/cpp/autosar/test/rules/M0-1-2/test.cpp @@ -33,14 +33,14 @@ template int f() { if (0) { // NON_COMPLIANT - true path is infeasible in all circumstances return 3; } - if (T::isVal()) { // COMPLIANT[FALSE_POSITIVE] - `isVal` is `true` for all + if (T::isVal()) { // COMPLIANT - `isVal` is `true` for all // visible instantiations, but in the uninstantiated // template both paths are feasible. This represents that // this is template dependent, so we consider it compliant return 2; } - if (T::isVal2()) { // COMPLIANT[FALSE_POSITIVE] - `isVal2` is either true or + if (T::isVal2()) { // COMPLIANT - `isVal2` is either true or // false return 2; } @@ -73,7 +73,7 @@ void test_break(int a) { return; } void test_infeasible_break(unsigned int a) { - while (true) { // NON_COMPLIANT(FALSE_NEGATIVE) + while (true) { // NON_COMPLIANT[FALSE_NEGATIVE] if (a < 0U) // NON_COMPLIANT - the comparison is always false break; @@ -99,3 +99,47 @@ void test_loop(int a) { a++; } } + +template int foo() { + if (x) { // COMPLIANT - block is reachable in the one of the instantiated + // template + return 1; + } + return 0; // COMPLIANT - block is reachable in the uninstantiated template +} + +void test() { + foo(); + foo(); +} + +template int template_infeasible_true_path() { + if (0) { // NON_COMPLIANT - true path is infeasible in all circumstances + return 3; + } +} + +template int template_infeasible_false_path() { + if (!0) { + return 3; + } + return 1; // NON_COMPLIANT - false path is infeasible in all circumstances +} + +void test_infeasible_instantiates() { + template_infeasible_true_path(); + template_infeasible_true_path(); + template_infeasible_false_path(); + template_infeasible_false_path(); +} + +template int template_infeasible_relation() { + if (i > -1) { // NON_COMPLIANT - true path is infeasible in all circumstances + return 3; + } +} + +void test_infeasible_relation() { + template_infeasible_relation<0>(); + template_infeasible_relation<1>(); +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.expected b/cpp/autosar/test/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.expected index 8ee5d76bfa..97c3d17a84 100644 --- a/cpp/autosar/test/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.expected +++ b/cpp/autosar/test/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.expected @@ -1,4 +1,4 @@ -| test_global_or_namespace.cpp:3:5:3:6 | g3 | Variable g3 is unused. | -| test_global_or_namespace.cpp:18:4:18:4 | a | Variable a is unused. | -| test_global_or_namespace.cpp:26:5:26:6 | x3 | Variable N1::x3 is unused. | -| test_global_or_namespace.cpp:36:5:36:5 | a | Variable N1::a is unused. | +| test_global_or_namespace.cpp:3:5:3:6 | g3 | Variable 'g3' is unused. | +| test_global_or_namespace.cpp:18:4:18:4 | a | Variable 'a' is unused. | +| test_global_or_namespace.cpp:26:5:26:6 | x3 | Variable 'N1::x3' is unused. | +| test_global_or_namespace.cpp:36:5:36:5 | a | Variable 'N1::a' is unused. | diff --git a/cpp/autosar/test/rules/M0-1-3/UnusedLocalVariable.expected b/cpp/autosar/test/rules/M0-1-3/UnusedLocalVariable.expected index 0d6f7de28b..19317d1d0d 100644 --- a/cpp/autosar/test/rules/M0-1-3/UnusedLocalVariable.expected +++ b/cpp/autosar/test/rules/M0-1-3/UnusedLocalVariable.expected @@ -1,6 +1,6 @@ -| test.cpp:7:7:7:7 | y | Local variable y in test_simple is not used. | -| test.cpp:14:13:14:13 | y | Local variable y in test_const is not used. | -| test.cpp:17:7:17:7 | z | Local variable z in test_const is not used. | -| test.cpp:23:5:23:5 | t | Local variable t in f1 is not used. | -| test.cpp:23:5:23:5 | t | Local variable t in f1 is not used. | -| test.cpp:44:6:44:6 | a | Local variable a in test_side_effect_init is not used. | +| test.cpp:7:7:7:7 | y | Local variable 'y' in 'test_simple' is not used. | +| test.cpp:15:7:15:7 | z | Local variable 'z' in 'test_const' is not used. | +| test.cpp:21:5:21:5 | t | Local variable 't' in 'f1' is not used. | +| test.cpp:21:5:21:5 | t | Local variable 't' in 'f1' is not used. | +| test.cpp:42:6:42:6 | a | Local variable 'a' in 'test_side_effect_init' is not used. | +| test.cpp:89:5:89:5 | t | Local variable 't' in 'template_function' is not used. | diff --git a/cpp/autosar/test/rules/M0-1-3/UnusedMemberVariable.expected b/cpp/autosar/test/rules/M0-1-3/UnusedMemberVariable.expected index 14e0cb42ee..e424945d5b 100644 --- a/cpp/autosar/test/rules/M0-1-3/UnusedMemberVariable.expected +++ b/cpp/autosar/test/rules/M0-1-3/UnusedMemberVariable.expected @@ -1,4 +1,4 @@ -| test_member.cpp:4:7:4:8 | m1 | Member variable m1 is unused. | -| test_member.cpp:17:9:17:11 | pad | Member variable pad is unused. | -| test_member.cpp:19:9:19:11 | sm2 | Member variable sm2 is unused. | -| test_member.cpp:31:7:31:8 | m1 | Member variable m1 is unused. | +| test_member.cpp:4:7:4:8 | m1 | Member variable 'm1' is unused. | +| test_member.cpp:17:9:17:11 | pad | Member variable 'pad' is unused. | +| test_member.cpp:19:9:19:11 | sm2 | Member variable 'sm2' is unused. | +| test_member.cpp:31:7:31:8 | m1 | Member variable 'm1' is unused. | diff --git a/cpp/autosar/test/rules/M0-1-3/test.cpp b/cpp/autosar/test/rules/M0-1-3/test.cpp index 7729371e5e..5c9c4a3413 100644 --- a/cpp/autosar/test/rules/M0-1-3/test.cpp +++ b/cpp/autosar/test/rules/M0-1-3/test.cpp @@ -11,9 +11,7 @@ int test_simple() { int test_const() { const int x = 1; // COMPLIANT - used below - const int y = 2; // COMPLIANT[FALSE_POSITIVE] - used in array initialization, - // but the database does not contain sufficient information - // for this case + const int y = 2; // COMPLIANT - used in array initialization, int z[y]; // NON_COMPLIANT - never used return x; } @@ -44,4 +42,74 @@ void test_side_effect_init() { LA a; // NON_COMPLIANT - no constructor called LC c; // COMPLIANT - constructor called which is considered to potentially // have side effects -} \ No newline at end of file +} + +#include +#include +template class CharBuffer { +public: + int member[t]; + CharBuffer() : member{0} {} +}; + +int test_constexpr_in_template_inst() { + constexpr int line_length = 1024U; // COMPLIANT - used in template inst. + // of buffer. + CharBuffer buffer{}; + return buffer.member[0]; +} + +enum DataType : unsigned char { + int8, + int16, +}; + +template int test_constexpr_in_static_assert() { + const std::array lldts{int8}; + const std::array llams{int16}; + constexpr std::size_t mssu = 64 * 1024; // COMPLIANT - used in static assert. + static_assert((sizeof(lldts) + sizeof(llams)) <= mssu, "assert"); + return 0; +} + +int baz() { + test_constexpr_in_static_assert(); + return 0; +} + +template extern constexpr bool all_of_v = true; // COMPLIANT + +template +extern constexpr bool all_of_v = + B1 &&all_of_v; // COMPLIANT + +void test_template_variable() { all_of_v; } + +template void template_function() { + T t; // NON_COMPLIANT - t is never used + T t2; // COMPLIANT - t is used + t2.test(); // Call may not be resolved in uninstantiated template +} + +class ClassT { +public: + void test() {} +}; + +void test_template_function() { template_function(); } + +int foo() { + constexpr int arrayDim = 10; // COMPLIANT - used in array size below + static int array[arrayDim]{}; + return array[4]; +} + +template static T another_templ_function() { return T(); } + +template +static T another_templ_function(const First &first, const Rest &...rest) { + return first + + another_templ_function(rest...); // COMPLIANT - 'rest' is used here +} + +static int templ_fnc2() { return another_templ_function(1, 2, 3, 4, 5); } diff --git a/cpp/autosar/test/rules/M0-1-3/test_global_or_namespace.cpp b/cpp/autosar/test/rules/M0-1-3/test_global_or_namespace.cpp index 92bb667c45..524830f1b4 100644 --- a/cpp/autosar/test/rules/M0-1-3/test_global_or_namespace.cpp +++ b/cpp/autosar/test/rules/M0-1-3/test_global_or_namespace.cpp @@ -41,4 +41,22 @@ void test_ns() { x2 = 1; } m1(); // ignore dead code in macros } // namespace N1 -int test_access_variable() { return N1::x5; } \ No newline at end of file +int test_access_variable() { return N1::x5; } + +template struct C1 { + int array[t]; // COMPLIANT +}; + +constexpr int g5 = 1; // COMPLIANT - used as template parameter + +namespace ns1 { +constexpr int m1 = 1; // COMPLIANT - used a template parameter +} + +void test_fp_reported_in_384() { + struct C1 l1; + struct C1 l2; + + l1.array[0] = 1; + l2.array[0] = 1; +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-3/test_member.cpp b/cpp/autosar/test/rules/M0-1-3/test_member.cpp index 8c0ded8b4e..7aff9a4232 100644 --- a/cpp/autosar/test/rules/M0-1-3/test_member.cpp +++ b/cpp/autosar/test/rules/M0-1-3/test_member.cpp @@ -47,4 +47,18 @@ void test_d() { d.getT(); } +template struct C1 { + int array[t]; // COMPLIANT +}; + +struct C2 { + static constexpr int m1 = 1; // COMPLIANT - used as template parameter +}; + +void test_fp_reported_in_384() { + struct C1 l1; + + l1.array[0] = 1; +} + } // namespace test \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected b/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected index f4309e7a4d..bfa053b318 100644 --- a/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected +++ b/cpp/autosar/test/rules/M0-1-4/SingleUseMemberPODVariable.expected @@ -1,9 +1,9 @@ -| test_global_or_namespace.cpp:16:7:16:7 | x | Member POD variable x in GA is only $@. | test_global_or_namespace.cpp:38:6:38:6 | x | used once | -| test_global_or_namespace.cpp:54:7:54:7 | x | Member POD variable x in N1A is only $@. | test_global_or_namespace.cpp:76:6:76:6 | x | used once | -| test_member.cpp:5:7:5:8 | m2 | Member POD variable m2 in A is only $@. | test_member.cpp:9:21:9:25 | constructor init of field m2 | used once | -| test_member.cpp:6:7:6:8 | m3 | Member POD variable m3 in A is only $@. | test_member.cpp:10:23:10:24 | m3 | used once | -| test_member.cpp:7:7:7:8 | m4 | Member POD variable m4 in A is only $@. | test_member.cpp:14:23:14:24 | m4 | used once | -| test_member.cpp:18:9:18:11 | sm1 | Member POD variable sm1 in s1 is only $@. | test_member.cpp:23:6:23:8 | sm1 | used once | -| test_member.cpp:36:7:36:8 | m1 | Member POD variable m1 in C is only $@. | test_member.cpp:39:21:39:22 | m1 | used once | -| test_member.cpp:37:7:37:8 | m2 | Member POD variable m2 in C is only $@. | test_member.cpp:46:5:46:6 | m2 | used once | -| test_member.cpp:55:5:55:6 | m3 | Member POD variable m3 in E is only $@. | test_member.cpp:56:27:56:32 | constructor init of field m3 | used once | +| test_global_or_namespace.cpp:16:7:16:7 | x | Member POD variable 'x' in 'GA' is only $@. | test_global_or_namespace.cpp:38:6:38:6 | x | used once | +| test_global_or_namespace.cpp:54:7:54:7 | x | Member POD variable 'x' in 'N1A' is only $@. | test_global_or_namespace.cpp:76:6:76:6 | x | used once | +| test_member.cpp:5:7:5:8 | m2 | Member POD variable 'm2' in 'A' is only $@. | test_member.cpp:9:21:9:25 | constructor init of field m2 | used once | +| test_member.cpp:6:7:6:8 | m3 | Member POD variable 'm3' in 'A' is only $@. | test_member.cpp:10:23:10:24 | m3 | used once | +| test_member.cpp:7:7:7:8 | m4 | Member POD variable 'm4' in 'A' is only $@. | test_member.cpp:14:23:14:24 | m4 | used once | +| test_member.cpp:18:9:18:11 | sm1 | Member POD variable 'sm1' in 's1' is only $@. | test_member.cpp:23:6:23:8 | sm1 | used once | +| test_member.cpp:36:7:36:8 | m1 | Member POD variable 'm1' in 'C' is only $@. | test_member.cpp:39:21:39:22 | m1 | used once | +| test_member.cpp:37:7:37:8 | m2 | Member POD variable 'm2' in 'C' is only $@. | test_member.cpp:46:5:46:6 | m2 | used once | +| test_member.cpp:55:5:55:6 | m3 | Member POD variable 'm3' in 'E' is only $@. | test_member.cpp:56:27:56:32 | constructor init of field m3 | used once | diff --git a/cpp/autosar/test/rules/M0-1-4/test.cpp b/cpp/autosar/test/rules/M0-1-4/test.cpp index 81391e444a..74771c9ea2 100644 --- a/cpp/autosar/test/rules/M0-1-4/test.cpp +++ b/cpp/autosar/test/rules/M0-1-4/test.cpp @@ -1,5 +1,5 @@ /** Test cases for `SingleUseLocalPODVariable.ql` */ - +#include class A {}; class B { @@ -30,4 +30,11 @@ void test_templates() { f1(); // Triggers a NON_COMPLIANT case in f1(), because B is a POD type f1(); // Does not trigger a NON_COMPLIANT case in f1(), because C is not a // POD type -} \ No newline at end of file +} + +class C1 { + static constexpr int used{2}; // COMPLIANT + int test_use() { return used; } + static constexpr int size{3}; // COMPLIANT + std::array array{false, false}; // size is used here +}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-4/test_member.cpp b/cpp/autosar/test/rules/M0-1-4/test_member.cpp index a43ee5d799..b82987c8a6 100644 --- a/cpp/autosar/test/rules/M0-1-4/test_member.cpp +++ b/cpp/autosar/test/rules/M0-1-4/test_member.cpp @@ -72,4 +72,62 @@ void test_e() { // Ensure that the template E is fully instantiated e2.getT(); } +void test_fp_reported_in_388() { + struct s1 { + int m1; // COMPLIANT + }; + + s1 l1 = {1}; // m1 is used here + l1.m1; +} + +void test_array_initialized_members() { + struct s1 { + int m1; // COMPLIANT + }; + + struct s1 l1[] = { + {.m1 = 1}, + {.m1 = 2}, + }; + + l1[0].m1; +} + +void test_indirect_assigned_members(void *opaque) { + struct s1 { + int m1; // COMPLIANT + }; + + struct s1 *p = (struct s1 *)opaque; + p->m1; + + struct s2 { + int m1; // COMPLIANT + }; + + char buffer[sizeof(struct s2) + 8] = {0}; + struct s2 *l2 = (struct s2 *)&buffer[8]; + l2->m1; +} + +void test_external_assigned_members(void (*fp)(unsigned char *)) { + + struct s1 { + int m1; // COMPLIANT + }; + + struct s1 l1; + fp((unsigned char *)&l1); + l1.m1; + + struct s2 { + int m1; // COMPLIANT + }; + + struct s2 (*copy_init)(); + struct s2 l2 = copy_init(); + l2.m1; +} + } // namespace test \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-9/DeadCode.expected b/cpp/autosar/test/rules/M0-1-9/DeadCode.expected deleted file mode 100644 index 43d7fd8492..0000000000 --- a/cpp/autosar/test/rules/M0-1-9/DeadCode.expected +++ /dev/null @@ -1,14 +0,0 @@ -| test.cpp:14:3:14:27 | declaration | This statement is dead code. | -| test.cpp:15:3:15:12 | ExprStmt | This statement is dead code. | -| test.cpp:16:3:16:12 | ExprStmt | This statement is dead code. | -| test.cpp:18:3:20:3 | if (...) ... | This statement is dead code. | -| test.cpp:30:3:31:3 | if (...) ... | This statement is dead code. | -| test.cpp:33:3:33:4 | { ... } | This statement is dead code. | -| test.cpp:34:3:36:3 | { ... } | This statement is dead code. | -| test.cpp:50:6:51:3 | { ... } | This statement is dead code. | -| test.cpp:61:46:62:3 | { ... } | This statement is dead code. | -| test.cpp:65:3:65:8 | ExprStmt | This statement is dead code. | -| test.cpp:67:3:67:21 | ExprStmt | This statement is dead code. | -| test.cpp:69:3:70:3 | try { ... } | This statement is dead code. | -| test.cpp:70:17:71:3 | { ... } | This statement is dead code. | -| test.cpp:76:17:77:3 | { ... } | This statement is dead code. | diff --git a/cpp/autosar/test/rules/M0-1-9/DeadCode.qlref b/cpp/autosar/test/rules/M0-1-9/DeadCode.qlref deleted file mode 100644 index 6ca1df3082..0000000000 --- a/cpp/autosar/test/rules/M0-1-9/DeadCode.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M0-1-9/DeadCode.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-9/DeadCode.testref b/cpp/autosar/test/rules/M0-1-9/DeadCode.testref new file mode 100644 index 0000000000..86593f7058 --- /dev/null +++ b/cpp/autosar/test/rules/M0-1-9/DeadCode.testref @@ -0,0 +1 @@ +cpp/common/test/rules/deadcode/DeadCode.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-1-9/test.cpp b/cpp/autosar/test/rules/M0-1-9/test.cpp deleted file mode 100644 index 8dc51c2114..0000000000 --- a/cpp/autosar/test/rules/M0-1-9/test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -int may_have_side_effects(); -int no_side_effects(int x) { return 1 + 2; } -int no_side_effects_nondeterministic(); - -int test_dead_code(int x) { - int live1 = may_have_side_effects(), - live2 = may_have_side_effects(); // COMPLIANT - int live3 = 0, - live4 = may_have_side_effects(); // COMPLIANT - int live5 = 0, live6 = 0; // COMPLIANT - live5 = 1; // COMPLIANT - live6 = 2; // COMPLIANT - - int dead1 = 0, dead2 = 0; // NON_COMPLIANT - dead1 = 1; // NON_COMPLIANT - useless assignment - dead2 = 1; // NON_COMPLIANT - useless assignment - - if (false) { // NON_COMPLIANT - dead2 = 10; // Only used in dead or unreachable code - } - - if (true) { // COMPLIANT - may_have_side_effects(); - } - - if (may_have_side_effects()) { // COMPLIANT - may_have_side_effects(); - } - - if (true) { // NON_COMPLIANT - } - - {} // NON_COMPLIANT - { // NON_COMPLIANT - 1 + 2; - } - - { // COMPLIANT - may_have_side_effects(); - } - - do { // COMPLIANT - may_have_side_effects(); - } while (may_have_side_effects()); - - do { // COMPLIANT - may_have_side_effects(); - } while (may_have_side_effects()); - - do { // NON_COMPLIANT - } while (no_side_effects_nondeterministic()); - - while (may_have_side_effects()) { // COMPLIANT - may_have_side_effects(); - } - - while (may_have_side_effects()) { // COMPLIANT - may_have_side_effects(); - } - - while (no_side_effects_nondeterministic()) { // NON_COMPLIANT - } - - may_have_side_effects(); // COMPLIANT - 1 + 2; // NON_COMPLIANT - - no_side_effects(x); // NON_COMPLIANT - - try { // NON_COMPLIANT - } catch (...) { // NON_COMPLIANT - } - - try { - may_have_side_effects(); - } catch (int i) { // COMPLIANT - } catch (...) { // NON_COMPLIANT - } - - static_assert(1); // COMPLIANT - - return live5 + live6; // COMPLIANT -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-2-1/DoNotPassAliasedPointerToParam.testref b/cpp/autosar/test/rules/M0-2-1/DoNotPassAliasedPointerToParam.testref new file mode 100644 index 0000000000..2c64dedd45 --- /dev/null +++ b/cpp/autosar/test/rules/M0-2-1/DoNotPassAliasedPointerToParam.testref @@ -0,0 +1 @@ +cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-2-1/test.cpp b/cpp/autosar/test/rules/M0-2-1/test.cpp index e5848e2752..3329f12824 100644 --- a/cpp/autosar/test/rules/M0-2-1/test.cpp +++ b/cpp/autosar/test/rules/M0-2-1/test.cpp @@ -51,4 +51,4 @@ void internal_shift() { void separate_access() { UnionSecret_t hash1, hash2; hash2.diff.suffix = hash1.fnv.suffix; // COMPLIANT, different union. -} \ No newline at end of file +} diff --git a/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.expected b/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.expected deleted file mode 100644 index 76cbcebed0..0000000000 --- a/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:16:3:16:8 | call to remove | Return value is not tested for errors. | diff --git a/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.qlref b/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.qlref deleted file mode 100644 index 3cfea1dc31..0000000000 --- a/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M0-3-2/FunctionErroneousReturnValueNotTested.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.testref b/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.testref new file mode 100644 index 0000000000..50847523ce --- /dev/null +++ b/cpp/autosar/test/rules/M0-3-2/FunctionErroneousReturnValueNotTested.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.qlref b/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.qlref deleted file mode 100644 index 208baa8d08..0000000000 --- a/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.testref b/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.testref new file mode 100644 index 0000000000..fe57c50fe3 --- /dev/null +++ b/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.testref @@ -0,0 +1 @@ +cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.qlref b/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.qlref deleted file mode 100644 index 4235959d77..0000000000 --- a/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.testref b/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.testref new file mode 100644 index 0000000000..596f74b010 --- /dev/null +++ b/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.testref @@ -0,0 +1 @@ +cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.expected b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.expected deleted file mode 100644 index c117f6d9ed..0000000000 --- a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:16:5:16:5 | m | Use of identifier that also exists in a base class that is not fully qualified. | -| test.cpp:17:5:17:5 | call to g | Use of identifier that also exists in a base class that is not fully qualified. | -| test.cpp:19:20:19:20 | g | Use of identifier that also exists in a base class that is not fully qualified. | diff --git a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.qlref b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.qlref deleted file mode 100644 index f0e2ebd711..0000000000 --- a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.testref b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.testref new file mode 100644 index 0000000000..ad5590bc1f --- /dev/null +++ b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThis.testref @@ -0,0 +1 @@ +cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.expected b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.expected deleted file mode 100644 index c117f6d9ed..0000000000 --- a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:16:5:16:5 | m | Use of identifier that also exists in a base class that is not fully qualified. | -| test.cpp:17:5:17:5 | call to g | Use of identifier that also exists in a base class that is not fully qualified. | -| test.cpp:19:20:19:20 | g | Use of identifier that also exists in a base class that is not fully qualified. | diff --git a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.qlref b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.qlref deleted file mode 100644 index 442eb62675..0000000000 --- a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.testref b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.testref new file mode 100644 index 0000000000..f7ff9100a6 --- /dev/null +++ b/cpp/autosar/test/rules/M14-6-1/NameNotReferredUsingAQualifiedIdOrThisAudit.testref @@ -0,0 +1 @@ +cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M14-6-1/test.cpp b/cpp/autosar/test/rules/M14-6-1/test.cpp deleted file mode 100644 index 7358a5e5e0..0000000000 --- a/cpp/autosar/test/rules/M14-6-1/test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -typedef int TYPE; -void g(); -void g1(); -int m; - -template class B { -public: - typedef T TYPE; - void g(); - int m; -}; - -template class A : B { -public: - void m1() { - m = 0; // NON_COMPLIANT - g(); // NON_COMPLIANT - TYPE t = 0; // NON_COMPLIANT[FALSE_NEGATIVE] - void (*p)() = &g; // NON_COMPLIANT - } - void m2() { - ::m = 0; // COMPLIANT - ::g(); // COMPLIANT - ::TYPE t1 = 0; // COMPLIANT - B::m = 0; // COMPLIANT - this->m = 0; // COMPLIANT - this->g(); // COMPLIANT - void (B::*p)() = &B::g; // COMPLIANT - typename B::TYPE t2 = 0; // COMPLIANT - g1(); // COMPLIANT, identifier not found in B - } -}; - -void f() { - A a; - a.m1(); - a.m2(); -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.qlref b/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.qlref deleted file mode 100644 index 3643376e59..0000000000 --- a/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M15-1-3/EmptyThrowOutsideCatch.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.testref b/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.testref new file mode 100644 index 0000000000..f3c961d8f1 --- /dev/null +++ b/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.testref @@ -0,0 +1 @@ +cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.expected b/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.expected index 594463345d..9f07d10900 100644 --- a/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.expected +++ b/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.expected @@ -1,2 +1,4 @@ -| test.cpp:22:1:22:18 | #if DBLWRAPUSES(X) | The macro $@ expands to 'defined' | test.cpp:18:1:18:22 | #define BADDEF defined | BADDEF | +| test.cpp:22:1:22:18 | #if DBLWRAPUSES(X) | The macro $@ expands to 'defined' | test.cpp:21:1:21:24 | #define DBLWRAPUSES USES | DBLWRAPUSES | | test.cpp:26:1:26:16 | #if BADDEFTWO(X) | The macro $@ expands to 'defined' | test.cpp:25:1:25:31 | #define BADDEFTWO(X) defined(X) | BADDEFTWO | +| test.cpp:29:1:29:16 | #if BADDEFTWO(Y) | The macro $@ expands to 'defined' | test.cpp:25:1:25:31 | #define BADDEFTWO(X) defined(X) | BADDEFTWO | +| test.cpp:42:1:42:11 | #if WRAPPER | The macro $@ expands to 'defined' | test.cpp:40:1:40:35 | #define WRAPPER X < Y \|\| defined(z) | WRAPPER | diff --git a/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.expected b/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.expected index 73df12d247..69cdd9e644 100644 --- a/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.expected +++ b/cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.expected @@ -2,4 +2,4 @@ | test.cpp:9:1:9:19 | #elif defined X < Y | Use of defined with non-standard form: X < Y. | | test.cpp:13:1:13:18 | #if defined(X > Y) | Use of defined with non-standard form: X > Y. | | test.cpp:14:1:14:20 | #elif defined(X < Y) | Use of defined with non-standard form: X < Y. | -| test.cpp:34:1:34:47 | #if defined(X) \|\| defined _Y_ + X && defined(Y) | Use of defined with non-standard form: _Y_ + X. | +| test.cpp:37:1:37:47 | #if defined(X) \|\| defined _Y_ + X && defined(Y) | Use of defined with non-standard form: _Y_ + X. | diff --git a/cpp/autosar/test/rules/M16-1-1/test.cpp b/cpp/autosar/test/rules/M16-1-1/test.cpp index fa1087f431..c7e9f91fdd 100644 --- a/cpp/autosar/test/rules/M16-1-1/test.cpp +++ b/cpp/autosar/test/rules/M16-1-1/test.cpp @@ -26,10 +26,18 @@ #if BADDEFTWO(X) // NON_COMPLIANT #endif +#if BADDEFTWO(Y) // NON_COMPLIANT +#endif + // clang-format off #if defined (X) || (defined(_Y_)) // COMPLIANT // clang-format on #endif #if defined(X) || defined _Y_ + X && defined(Y) // NON_COMPLIANT +#endif + +#define WRAPPER X < Y || defined(z) + +#if WRAPPER // NON_COMPLIANT #endif \ No newline at end of file diff --git a/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.qlref b/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.qlref deleted file mode 100644 index c2a9f2c7c3..0000000000 --- a/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.testref b/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.testref new file mode 100644 index 0000000000..87f68653c8 --- /dev/null +++ b/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M17-0-5/test.cpp b/cpp/autosar/test/rules/M17-0-5/test.cpp deleted file mode 100644 index 1b40f79640..0000000000 --- a/cpp/autosar/test/rules/M17-0-5/test.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -int test_jmps() { - jmp_buf env; - int val; - - val = setjmp(env); - if (val) { - return (val); - } - - longjmp(env, 101); - - longjmp(env, 102); - - val = setjmp(env); - - return 0; -} diff --git a/cpp/autosar/test/rules/M18-0-5/test.cpp b/cpp/autosar/test/rules/M18-0-5/test.cpp index 521326bef7..37ea1f6415 100644 --- a/cpp/autosar/test/rules/M18-0-5/test.cpp +++ b/cpp/autosar/test/rules/M18-0-5/test.cpp @@ -1,4 +1,4 @@ -#include +#include void test_unbounded_str_funs() { char str1[] = "Sample string"; diff --git a/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.qlref b/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.qlref deleted file mode 100644 index a69e18549f..0000000000 --- a/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M18-2-1/MacroOffsetofUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.testref b/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.testref new file mode 100644 index 0000000000..022fef6071 --- /dev/null +++ b/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.qlref b/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.qlref deleted file mode 100644 index 445ccd5bd4..0000000000 --- a/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M18-7-1/CsignalFunctionsUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.testref b/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.testref new file mode 100644 index 0000000000..a09406a932 --- /dev/null +++ b/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.expected b/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.expected deleted file mode 100644 index fda560380f..0000000000 --- a/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:6:8:6:19 | type mention | Use of type 'std::sig_atomic_t'. | -| test.cpp:10:3:10:14 | type mention | Use of type 'sig_atomic_t'. | diff --git a/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.qlref b/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.qlref deleted file mode 100644 index 34c83d741a..0000000000 --- a/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M18-7-1/CsignalTypesUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.testref b/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.testref new file mode 100644 index 0000000000..3d398d799b --- /dev/null +++ b/cpp/autosar/test/rules/M18-7-1/CsignalTypesUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalEscape.expected b/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalEscape.expected index 17a0016fec..41ebcf7629 100644 --- a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalEscape.expected +++ b/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalEscape.expected @@ -1,6 +1,6 @@ | test.cpp:3:3:3:8 | 10 | This literal contains the non-zero octal escape code \\012. | | test.cpp:4:3:4:8 | 44 | This literal contains the non-zero octal escape code \\054. | | test.cpp:5:3:5:9 | 3129 | This literal contains the non-zero octal escape code \\014. | -| test.cpp:10:3:10:8 | \n | This literal contains the non-zero octal escape code \\012. | -| test.cpp:11:3:11:8 | , | This literal contains the non-zero octal escape code \\054. | -| test.cpp:12:3:12:9 | \u000c9 | This literal contains the non-zero octal escape code \\014. | +| test.cpp:7:3:7:8 | \n | This literal contains the non-zero octal escape code \\012. | +| test.cpp:8:3:8:8 | , | This literal contains the non-zero octal escape code \\054. | +| test.cpp:9:3:9:9 | \u000c9 | This literal contains the non-zero octal escape code \\014. | diff --git a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.expected b/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.expected deleted file mode 100644 index 8109c107a5..0000000000 --- a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:7:3:7:5 | 10 | Non zero octal literal 012. | -| test.cpp:8:3:8:5 | 44 | Non zero octal literal 054. | diff --git a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.qlref b/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.qlref deleted file mode 100644 index 67900e54f7..0000000000 --- a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M2-13-2/UseOfNonZeroOctalLiteral.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.testref b/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.testref new file mode 100644 index 0000000000..97c466a866 --- /dev/null +++ b/cpp/autosar/test/rules/M2-13-2/UseOfNonZeroOctalLiteral.testref @@ -0,0 +1 @@ +cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-13-2/test.cpp b/cpp/autosar/test/rules/M2-13-2/test.cpp index a89809d68c..3c7fba30dd 100644 --- a/cpp/autosar/test/rules/M2-13-2/test.cpp +++ b/cpp/autosar/test/rules/M2-13-2/test.cpp @@ -3,9 +3,6 @@ void test_non_zero_octal() { '\012'; // NON_COMPLIANT '\054'; // NON_COMPLIANT '\0149'; // NON_COMPLIANT - 0; // COMPLIANT - octal literal zero permitted - 012; // NON_COMPLIANT - 054; // NON_COMPLIANT "\0"; // COMPLIANT - octal zero escape sequence permitted "\012"; // NON_COMPLIANT "\054"; // NON_COMPLIANT diff --git a/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.expected b/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.expected deleted file mode 100644 index 56dce901dd..0000000000 --- a/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:3:3:3:12 | 4294967295 | Hex literal is an unsigned integer but does not include a 'U' suffix. | diff --git a/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.qlref b/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.qlref deleted file mode 100644 index ffb71066d5..0000000000 --- a/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M2-13-3/MissingUSuffix.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.testref b/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.testref new file mode 100644 index 0000000000..9133a84ce4 --- /dev/null +++ b/cpp/autosar/test/rules/M2-13-3/MissingUSuffix.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-13-3/test.cpp b/cpp/autosar/test/rules/M2-13-3/test.cpp deleted file mode 100644 index e5b3abfa47..0000000000 --- a/cpp/autosar/test/rules/M2-13-3/test.cpp +++ /dev/null @@ -1,5 +0,0 @@ -void test_unsigned_literals_without_suffix() { - 0xFFFFFFFFU; // COMPLIANT - literal explicitly marked as unsigned - 0xFFFFFFFF; // NON_COMPLIANT - literal is too large for a signed int, so has - // type unsigned int -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.qlref b/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.qlref deleted file mode 100644 index 3f146ebeaf..0000000000 --- a/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M2-7-1/SlashStarUsedWithinACStyleComment.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.testref b/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.testref new file mode 100644 index 0000000000..971b1953f7 --- /dev/null +++ b/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.testref @@ -0,0 +1 @@ +cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.qlref b/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.qlref deleted file mode 100644 index 7d97c146c9..0000000000 --- a/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M27-0-1/CstdioFunctionsUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.testref b/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.testref new file mode 100644 index 0000000000..5f8b3d8a9a --- /dev/null +++ b/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.qlref b/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.qlref deleted file mode 100644 index 20bf876eba..0000000000 --- a/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M27-0-1/CstdioMacrosUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.testref b/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.testref new file mode 100644 index 0000000000..a1ba376c3b --- /dev/null +++ b/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.expected b/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.expected deleted file mode 100644 index 8f3971fa2a..0000000000 --- a/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.expected +++ /dev/null @@ -1,7 +0,0 @@ -| test.cpp:4:8:4:11 | type mention | Use of type 'std::FILE'. | -| test.cpp:6:8:6:13 | type mention | Use of type 'std::fpos_t'. | -| test.cpp:20:18:20:23 | type mention | Use of type 'size_t'. | -| test.cpp:21:18:21:23 | type mention | Use of type 'size_t'. | -| test.cpp:34:3:34:6 | type mention | Use of type 'FILE'. | -| test.cpp:36:3:36:8 | type mention | Use of type 'fpos_t'. | -| test.cpp:50:14:50:19 | type mention | Use of type 'size_t'. | diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.qlref b/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.qlref deleted file mode 100644 index 10beab7eaa..0000000000 --- a/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M27-0-1/CstdioTypesUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.testref b/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.testref new file mode 100644 index 0000000000..4c08a75cfe --- /dev/null +++ b/cpp/autosar/test/rules/M27-0-1/CstdioTypesUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/test.cpp b/cpp/autosar/test/rules/M27-0-1/test.cpp deleted file mode 100644 index feb2ed476f..0000000000 --- a/cpp/autosar/test/rules/M27-0-1/test.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -void *test_cstdio_is_used() { - std::FILE *f = std::fopen("foo.txt", "r"); // NON_COMPLIANT - - std::fpos_t init_position; // NON_COMPLIANT - std::fgetpos(f, &init_position); // NON_COMPLIANT - - while (!std::feof(f)) { // NON_COMPLIANT - char c = std::fgetc(f); // NON_COMPLIANT - if (c == EOF) // NON_COMPLIANT - std::rewind(f); // NON_COMPLIANT - } - if (std::ferror(f)) { // NON_COMPLIANT - std::clearerr(f); // NON_COMPLIANT - std::fclose(f); // NON_COMPLIANT - std::perror("fgetc"); // NON_COMPLIANT - } - - std::fseek(f, (size_t)0, SEEK_SET); // NON_COMPLIANT - std::fseek(f, (size_t)0, SEEK_END); // NON_COMPLIANT - char buf[BUFSIZ]; // NON_COMPLIANT - std::fread(buf, 1, sizeof(buf), f); // NON_COMPLIANT - - std::fsetpos(f, &init_position); // NON_COMPLIANT - std::fflush(f); // NON_COMPLIANT - std::fclose(f); // NON_COMPLIANT - - std::printf("DEBUG: TMP_MAX=%d FILENAME_MAX=%d FOPEN_MAX=%d\n", TMP_MAX, - FILENAME_MAX, FOPEN_MAX); // NON_COMPLIANT - std::puts("all done!"); // NON_COMPLIANT - - // global namespace - FILE *f1 = fopen("foo.txt", "r"); // NON_COMPLIANT - - fpos_t init_position1; - fgetpos(f1, &init_position1); // NON_COMPLIANT - - while (!feof(f1)) { // NON_COMPLIANT - char c = fgetc(f1); // NON_COMPLIANT - if (c == EOF) // NON_COMPLIANT - rewind(f1); // NON_COMPLIANT - } - if (ferror(f1)) { // NON_COMPLIANT - clearerr(f1); // NON_COMPLIANT - fclose(f1); // NON_COMPLIANT - perror("fgetc"); // NON_COMPLIANT - } - - fseek(f1, (size_t)0, SEEK_SET); // NON_COMPLIANT - fread(buf, 1, sizeof(buf), f1); // NON_COMPLIANT - - fsetpos(f1, &init_position1); // NON_COMPLIANT - fflush(f1); // NON_COMPLIANT - fclose(f1); // NON_COMPLIANT - - printf("foo"); // NON_COMPLIANT - puts("all done!"); // NON_COMPLIANT - - return NULL; // NON_COMPLIANT -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-2-1/DeclarationsOfAnObjectShallHaveCompatibleTypes.expected b/cpp/autosar/test/rules/M3-2-1/DeclarationsOfAnObjectShallHaveCompatibleTypes.expected index 33f6dec68f..96cf9a6597 100644 --- a/cpp/autosar/test/rules/M3-2-1/DeclarationsOfAnObjectShallHaveCompatibleTypes.expected +++ b/cpp/autosar/test/rules/M3-2-1/DeclarationsOfAnObjectShallHaveCompatibleTypes.expected @@ -4,9 +4,11 @@ | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:7:5:7:6 | definition of a7 | The object $@ of type int is not compatible with re-declaration $@ of type LL | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:7:5:7:6 | definition of a7 | a7 | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:19:11:19:12 | declaration of a7 | a7 | | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:14:5:14:6 | definition of a2 | The object $@ of type int is not compatible with re-declaration $@ of type long | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:14:5:14:6 | definition of a2 | a2 | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:23:13:23:14 | declaration of a2 | a2 | | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:19:5:19:7 | definition of a11 | The object $@ of type int[100] is not compatible with re-declaration $@ of type int[101] | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:19:5:19:7 | definition of a11 | a11 | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:28:12:28:14 | declaration of a11 | a11 | +| test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:39:5:39:7 | definition of a13 | The object $@ of type int is not compatible with re-declaration $@ of type long | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:39:5:39:7 | definition of a13 | a13 | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:41:13:41:15 | declaration of a13 | a13 | | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:11:12:11:13 | declaration of a4 | The object $@ of type int is not compatible with re-declaration $@ of type long | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:11:12:11:13 | declaration of a4 | a4 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:4:6:4:7 | definition of a4 | a4 | | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:13:13:13:14 | declaration of a5 | The object $@ of type long is not compatible with re-declaration $@ of type int | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:13:13:13:14 | declaration of a5 | a5 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:5:5:5:6 | definition of a5 | a5 | | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:17:1:17:3 | declaration of a6 | The object $@ of type int is not compatible with re-declaration $@ of type long | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:17:1:17:3 | declaration of a6 | a6 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:6:6:6:7 | definition of a6 | a6 | | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:19:11:19:12 | declaration of a7 | The object $@ of type LL is not compatible with re-declaration $@ of type int | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:19:11:19:12 | declaration of a7 | a7 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:7:5:7:6 | definition of a7 | a7 | | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:23:13:23:14 | declaration of a2 | The object $@ of type long is not compatible with re-declaration $@ of type int | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:23:13:23:14 | declaration of a2 | a2 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:14:5:14:6 | definition of a2 | a2 | | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:28:12:28:14 | declaration of a11 | The object $@ of type int[101] is not compatible with re-declaration $@ of type int[100] | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:28:12:28:14 | declaration of a11 | a11 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:19:5:19:7 | definition of a11 | a11 | +| test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:41:13:41:15 | declaration of a13 | The object $@ of type long is not compatible with re-declaration $@ of type int | test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp:41:13:41:15 | declaration of a13 | a13 | test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp:39:5:39:7 | definition of a13 | a13 | diff --git a/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp b/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp index ad774c5995..7230036d39 100644 --- a/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp +++ b/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit1.cpp @@ -32,4 +32,9 @@ template class ClassB { }; void test3() { ClassB b; } -void test4() { ClassB b; } \ No newline at end of file +void test4() { ClassB b; } + +namespace { +int a12; // COMPLIANT +int a13; // NON_COMPLIANT +} // anonymous namespace diff --git a/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp b/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp index 58aefe0281..fb5efc92a2 100644 --- a/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp +++ b/cpp/autosar/test/rules/M3-2-1/test_declarations_of_an_object_shall_have_compatible_types_unit2.cpp @@ -34,4 +34,9 @@ template class ClassB { }; void testb_1() { ClassB b; } -void testb_2() { ClassB b; } \ No newline at end of file +void testb_2() { ClassB b; } + +namespace { +extern int a12; // COMPLIANT +extern long a13; // NON_COMPLIANT +} // anonymous namespace diff --git a/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.qlref b/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.qlref deleted file mode 100644 index b202c8c8b4..0000000000 --- a/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.testref b/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.testref new file mode 100644 index 0000000000..6695553885 --- /dev/null +++ b/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.qlref b/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.qlref deleted file mode 100644 index 052000073f..0000000000 --- a/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.testref b/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.testref new file mode 100644 index 0000000000..5b93ea365a --- /dev/null +++ b/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.testref @@ -0,0 +1 @@ +cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.qlref b/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.qlref deleted file mode 100644 index 6f6edc783a..0000000000 --- a/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.testref b/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.testref new file mode 100644 index 0000000000..f66784283e --- /dev/null +++ b/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-4-1/test.cpp b/cpp/autosar/test/rules/M3-4-1/test.cpp deleted file mode 100644 index ae3bb7b887..0000000000 --- a/cpp/autosar/test/rules/M3-4-1/test.cpp +++ /dev/null @@ -1,139 +0,0 @@ -extern void f1(int i); -extern int g1; // COMPLIANT -extern int g2; // NON_COMPLIANT; single use of a global variable -bool f2() { return g1 == 1; } -void f3() { - int j = g1; // NON_COMPLIANT - if (f2()) { - int k; // COMPLIANT - f1(j); - f1(k); - } -} - -void f4() { - int j = g1; // COMPLIANT; value of g1 changed between - // definition and use - g1 = 1; - if (f2()) { - f1(j); - } -} - -void f5() { - int j = g1; // COMPLIANT; shouldn't be moved inside loop - while (true) { - int i = g1++; - while (f2()) { - i += j; - } - - if (i % 2) - break; - } -} - -void f6() { - int j = g1; // COMPLIANT; can't moved into smaller scope -#ifdef FOO - if (g1) { - g1 = j + 1; - } -#else - if (g1) { - g1 = j + 2; - } -#endif -} - -void f7() { - int j = g1; // COMPLIANT; potentially stores previous value of - // g1 so moving this would be incorrect. - f1(1); // f1 may change the value of g1 - if (f2()) { - f1(j); - } -} - -void f8() { int i = g2; } - -void f9() { - int i; // NON_COMPLIANT - - if (f2()) { - if (f2()) { - i++; - } else { - i--; - } - } -} - -struct S1 { // NON_COMPLIANT - int i; -}; - -void f10() { S1 l1{}; } - -void f11() { - struct S2 { // COMPLIANT - int i; - } l1{}; -} - -struct S3 { - int i; -}; - -template int f12(T p); -template <> int f12(S3 p) { return p.i; } - -struct S4 { // NON_COMPLIANT; single use in function f13 - int i; -}; - -template class C1 { // COMPLIANT; used in both f13 and f14 -private: - T m1; -}; - -void f13() { C1 l1; } -void f14() { C1 l1; } - -void f15() { - int i; // COMPLIANT - - if (i == 0) { - i++; - } -} - -namespace ns1 { -int i; // NON_COMPLIANT -namespace ns2 { -int j = i + 1; -void f1() { i++; } -} // namespace ns2 -} // namespace ns1 - -void f16() { - for (int i = 0; i < 10; i++) { - int j = i + 1; // NON_COMPLIANT[FALSE_NEGATIVE]; we are not consider - // candidates inside loops. - try { - j++; - } catch (...) { - } - } -} - -void f17() { - int i; // COMPLIANT - int *ptr; - { - // Moving the declaration of i into the reduced scope will result in a - // dangling pointer - ptr = &i; - } - *ptr = 1; -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-9-3/UnderlyingBitRepresentationsOfFloatingPointValuesUsed.expected b/cpp/autosar/test/rules/M3-9-3/UnderlyingBitRepresentationsOfFloatingPointValuesUsed.expected index 9aec2314da..d0fe6416ca 100644 --- a/cpp/autosar/test/rules/M3-9-3/UnderlyingBitRepresentationsOfFloatingPointValuesUsed.expected +++ b/cpp/autosar/test/rules/M3-9-3/UnderlyingBitRepresentationsOfFloatingPointValuesUsed.expected @@ -1,2 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnderlyingBitRepresentationsOfFloatingPointValuesUsed.ql:27,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnderlyingBitRepresentationsOfFloatingPointValuesUsed.ql:36,10-18) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UnderlyingBitRepresentationsOfFloatingPointValuesUsed.ql:37,5-13) | test.cpp:5:3:5:20 | ... &= ... | Modification of bit-representation of float originated at $@ | test.cpp:4:24:4:60 | reinterpret_cast... | cast | | test.cpp:12:3:12:14 | ... &= ... | Modification of bit-representation of float originated at $@ | test.cpp:11:18:11:30 | (uint8_t *)... | cast | diff --git a/cpp/autosar/test/rules/M4-10-2/test.cpp b/cpp/autosar/test/rules/M4-10-2/test.cpp index fa91f238bb..39fa2df7ff 100644 --- a/cpp/autosar/test/rules/M4-10-2/test.cpp +++ b/cpp/autosar/test/rules/M4-10-2/test.cpp @@ -23,6 +23,5 @@ void test_nullptr() { f3(f1, nullptr); // COMPLIANT - use of nullptr f1(NULL); // COMPLIANT - use of NULL macro is compliant according to this rule // only - f1('\0'); // COMPLIANT - use of octal escape 0 is not covered by this rule ClassA a = CLASSA_INIT; // COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.expected b/cpp/autosar/test/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.expected index 1be5b7b9fc..b23be388c6 100644 --- a/cpp/autosar/test/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.expected +++ b/cpp/autosar/test/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.expected @@ -1,4 +1,16 @@ -| test.cpp:4:22:4:24 | 99 | Assignment of an non-integer type to variable $@ which is a variable with an explicitly signed char type | test.cpp:4:17:4:18 | a1 | a1 | -| test.cpp:6:20:6:22 | 99 | Assignment of an non-integer type to variable $@ which is a variable with an explicitly signed char type | test.cpp:6:15:6:16 | a3 | a3 | -| test.cpp:9:20:9:22 | 99 | Assignment of an non-integer type to variable $@ which is a variable with an explicitly signed char type | test.cpp:9:15:9:16 | a5 | a5 | -| test.cpp:12:21:12:23 | 99 | Assignment of an non-integer type to variable $@ which is a variable with an explicitly signed char type | test.cpp:12:16:12:17 | a7 | a7 | +| test.cpp:58:7:58:8 | (unsigned char)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:61:20:61:21 | (signed char)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:71:21:71:22 | (uint8_t)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:74:20:74:21 | (int8_t)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:84:9:84:11 | (unsigned char)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:87:9:87:11 | (signed char)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:97:9:97:11 | (unsigned char)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:100:9:100:11 | (signed char)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:117:7:117:10 | (unsigned char)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:122:7:122:10 | (signed char)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:137:7:137:10 | (uint8_t)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:142:7:142:10 | (int8_t)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:153:6:153:7 | (unsigned char)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:156:6:156:7 | (signed char)... | This expression of plain char type is implicitly converted to 'signed char'. | +| test.cpp:166:6:166:7 | (uint8_t)... | This expression of plain char type is implicitly converted to 'unsigned char'. | +| test.cpp:169:7:169:8 | (int8_t)... | This expression of plain char type is implicitly converted to 'signed char'. | diff --git a/cpp/autosar/test/rules/M5-0-12/test.cpp b/cpp/autosar/test/rules/M5-0-12/test.cpp index 453c37bf1e..036db12b04 100644 --- a/cpp/autosar/test/rules/M5-0-12/test.cpp +++ b/cpp/autosar/test/rules/M5-0-12/test.cpp @@ -1,16 +1,170 @@ #include -void f1() { - unsigned char a1 = 'c'; // NON_COMPLIANT - unsigned char a2 = 10; - signed char a3 = 'c'; // NON_COMPLIANT - signed char a4 = 10; +class C1 { +public: + C1(unsigned char y) : x(y) {} - std::int8_t a5 = 'c'; // NON_COMPLIANT - std::int8_t a6 = 10; +private: + unsigned char x; +}; - std::uint8_t a7 = 'c'; // NON_COMPLIANT - std::uint8_t a8 = 10; +class C2 { +public: + C2(signed char y) : x(y) {} - char a9 = 'c'; -} \ No newline at end of file +private: + signed char x; +}; + +/* Twin classes for std::uint8_t and std::int8_t */ +class C5 { +public: + C5(unsigned char y) : x(y) {} + +private: + std::uint8_t x; +}; + +class C6 { +public: + C6(signed char y) : x(y) {} + +private: + std::int8_t x; +}; + +void f1(unsigned char x) {} +void f2(signed char x) {} + +/* Twin functions for std::uint8_t and std::int8_t */ +void f9(std::uint8_t x) {} +void f10(std::int8_t x) {} + +int main() { + + /* ========== 1. Assigning a char to another char ========== */ + + /* ===== 1-1. Assigning a char to a char variable ===== */ + + unsigned char x1 = 1; + unsigned char y1 = + x1; // COMPLIANT: unsigned char assigned to an unsigned char + + signed char x2 = 1; + signed char y2 = x2; // COMPLIANT: signed char assigned to a signed char + + char x3 = 'x'; + unsigned char y3 = + x3; // NON-COMPLIANT: plain char assigned to a unsigned char + + char x4 = 'x'; + signed char y4 = x4; // NON-COMPLIANT: plain char assigned to a signed char + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t x5 = 1; + std::uint8_t y5 = x5; // COMPLIANT: std::uint8_t assigned to a std::uint8_t + + std::int8_t x6 = 1; + std::int8_t y6 = x6; // COMPLIANT: std::int8_t assigned to a std::int8_t + + char x7 = 'x'; + std::uint8_t y7 = x7; // NON-COMPLIANT: plain char assigned to a std::uint8_t + + char x8 = 'x'; + std::int8_t y8 = x8; // NON-COMPLIANT: plain char assigned to a std::int8_t + + /* ===== 1-2. Assigning a char to a char member ===== */ + + C1 c1(1); // COMPLIANT: unsigned char arg passed to an unsigned + // char member + + C2 c2(1); // COMPLIANT: signed char arg passed to a signed char + // member + + C1 c3('x'); // NON-COMPLIANT: plain char arg passed to an unsigned char + // member + + C2 c4('x'); // NON-COMPLIANT: plain char arg passed to a signed char + // member + + /* Twin cases with std::uint8_t and std::int8_t */ + C5 c5(1); // COMPLIANT: std::uint8_t arg passed to a + // std::uint8_t member + + C6 c6(1); // COMPLIANT: std::int8_t arg passed to a std::int8_t + // member + + C5 c7('x'); // NON-COMPLIANT: plain char arg passed to a + // std::uint8_t member + + C6 c8('x'); // NON-COMPLIANT: plain char arg passed to a std::int8_t + // member + + /* ========== 1-3. Assigning a char to a char through a pointer ========== */ + + unsigned char x9 = 1; + unsigned char *y9 = &x9; + unsigned char z1 = + *y9; // COMPLIANT: unsigned char assigned to an *&unsigned char + + signed char x10 = 1; + signed char *y10 = &x10; + signed char z2 = *y10; // COMPLIANT: signed char assigned to an *&signed char + + char x11 = 1; + char *y11 = &x11; + unsigned char z3 = + *y11; // NON-COMPLIANT: plain char assigned to an *&unsigned char + + char x12 = 1; + char *y12 = &x12; + signed char z4 = + *y12; // NON-COMPLIANT: plain char assigned to an *&signed char + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t x13 = 1; + std::uint8_t *y13 = &x13; + std::uint8_t z5 = + *y13; // COMPLIANT: std::uint8_t assigned to a *&std::uint8_t + + std::int8_t x14 = 1; + std::int8_t *y14 = &x14; + std::int8_t z6 = *y14; // COMPLIANT: std::int8_t assigned to an *&std::int8_t + + char x15 = 1; + char *y15 = &x15; + std::uint8_t z7 = + *y15; // NON-COMPLIANT: plain char assigned to an *&std::uint8_t + + char x16 = 1; + char *y16 = &x16; + std::int8_t z8 = + *y16; // NON-COMPLIANT: plain char assigned to an *&std::int8_t + + /* ========== 2. Passing a char argument to a char parameter ========== */ + + unsigned char a1 = 1; + f1(a1); // COMPLIANT: unsigned char arg passed to an unsigned char parameter + + signed char a2 = 1; + f2(a2); // COMPLIANT: signed char arg passed to a signed char parameter + + char a3 = 'a'; + f1(a3); // NON-COMPLIANT: plain char arg passed to an unsigned char parameter + + char a4 = 'a'; + f2(a4); // NON-COMPLIANT: plain char arg passed to a signed char parameter + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t a5 = 1; + f9(a5); // COMPLIANT: std::uint8_t arg passed to a std::uint8_t parameter + + std::int8_t a6 = 1; + f10(a6); // COMPLIANT: std::int8_t arg passed to a std::int8_t parameter + + char a7 = 'a'; + f9(a7); // NON-COMPLIANT: plain char arg passed to a std::uint8_t parameter + + char a8 = 'a'; + f10(a8); // NON-COMPLIANT: plain char arg passed to a std::int8_t parameter +} diff --git a/cpp/autosar/test/rules/M5-0-2/InsufficientUseOfParentheses.expected b/cpp/autosar/test/rules/M5-0-2/InsufficientUseOfParentheses.expected new file mode 100644 index 0000000000..ef355c7306 --- /dev/null +++ b/cpp/autosar/test/rules/M5-0-2/InsufficientUseOfParentheses.expected @@ -0,0 +1,8 @@ +| test.cpp:40:8:40:13 | ... * ... | Dependence on operator precedence rules. | +| test.cpp:41:19:41:24 | ... * ... | Dependence on operator precedence rules. | +| test.cpp:42:8:42:13 | ... * ... | Dependence on operator precedence rules. | +| test.cpp:42:17:42:22 | ... * ... | Dependence on operator precedence rules. | +| test.cpp:48:8:48:15 | ... == ... | Dependence on operator precedence rules. | +| test.cpp:49:26:49:32 | ... - ... | Dependence on operator precedence rules. | +| test.cpp:50:8:50:15 | ... == ... | Dependence on operator precedence rules. | +| test.cpp:50:24:50:30 | ... - ... | Dependence on operator precedence rules. | diff --git a/cpp/autosar/test/rules/M5-0-2/InsufficientUseOfParentheses.qlref b/cpp/autosar/test/rules/M5-0-2/InsufficientUseOfParentheses.qlref new file mode 100644 index 0000000000..733c035604 --- /dev/null +++ b/cpp/autosar/test/rules/M5-0-2/InsufficientUseOfParentheses.qlref @@ -0,0 +1 @@ +rules/M5-0-2/InsufficientUseOfParentheses.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-2/test.cpp b/cpp/autosar/test/rules/M5-0-2/test.cpp index 06dab1e64c..d028b632f9 100644 --- a/cpp/autosar/test/rules/M5-0-2/test.cpp +++ b/cpp/autosar/test/rules/M5-0-2/test.cpp @@ -31,4 +31,21 @@ void f1() { int **l7; l1 = (*l7)[l2]; // NON_COMPLIANT[FALSE_NEGATIVE] char l8 = (char)(l1 + 1); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +void test_insufficient_parentheses() { + int l1, l2, l3; + + l1 = (2 * l2) + (3 * l3); // COMPLIANT + l1 = 2 * l2 + (3 * l3); // NON_COMPLIANT + l1 = (2 * l2) + 3 * l3; // NON_COMPLIANT + l1 = 2 * l2 + 3 * l3; // NON_COMPLIANT + l1 = (2 * l2) + l3 + 1; // COMPLIANT + l1 = (l2 + 1) - (l2 + l3); // COMPLIANT + l1 = l2 + l3 + 1; // COMPLIANT + + l1 = (l2 == l3) ? l2 : (l2 - l3); // COMPLIANT + l1 = l2 == l3 ? l2 : (l2 - l3); // NON_COMPLIANT + l1 = (l2 == l3) ? l2 : l2 - l3; // NON_COMPLIANT + l1 = l2 == l3 ? l2 : l2 - l3; // NON_COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.expected b/cpp/autosar/test/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.expected index ac31001f57..806c82599d 100644 --- a/cpp/autosar/test/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.expected +++ b/cpp/autosar/test/rules/M5-0-20/BitwiseOperatorOperandsHaveDifferentUnderlyingType.expected @@ -1,21 +1,21 @@ -| test.cpp:18:3:18:6 | ... & ... | Operands of the '&' operation have different underlying types. | -| test.cpp:19:3:19:7 | ... \| ... | Operands of the '\|' operation have different underlying types. | -| test.cpp:20:3:20:7 | ... ^ ... | Operands of the '^' operation have different underlying types. | -| test.cpp:21:3:21:8 | ... << ... | Operands of the '<<' operation have different underlying types. | -| test.cpp:22:3:22:8 | ... >> ... | Operands of the '>>' operation have different underlying types. | -| test.cpp:23:3:23:8 | ... &= ... | Operands of the '&=' operation have different underlying types. | -| test.cpp:24:3:24:8 | ... \|= ... | Operands of the '\|=' operation have different underlying types. | -| test.cpp:25:3:25:8 | ... ^= ... | Operands of the '^=' operation have different underlying types. | -| test.cpp:26:3:26:9 | ... <<= ... | Operands of the '<<=' operation have different underlying types. | -| test.cpp:27:3:27:9 | ... >>= ... | Operands of the '>>=' operation have different underlying types. | -| test.cpp:45:3:45:6 | ... & ... | Operands of the '&' operation have different underlying types. | -| test.cpp:46:3:46:7 | ... \| ... | Operands of the '\|' operation have different underlying types. | -| test.cpp:47:3:47:7 | ... ^ ... | Operands of the '^' operation have different underlying types. | -| test.cpp:48:3:48:8 | ... << ... | Operands of the '<<' operation have different underlying types. | -| test.cpp:49:3:49:8 | ... >> ... | Operands of the '>>' operation have different underlying types. | -| test.cpp:50:3:50:8 | ... &= ... | Operands of the '&=' operation have different underlying types. | -| test.cpp:51:3:51:8 | ... \|= ... | Operands of the '\|=' operation have different underlying types. | -| test.cpp:52:3:52:8 | ... ^= ... | Operands of the '^=' operation have different underlying types. | -| test.cpp:53:3:53:9 | ... <<= ... | Operands of the '<<=' operation have different underlying types. | -| test.cpp:54:3:54:9 | ... >>= ... | Operands of the '>>=' operation have different underlying types. | -| test.cpp:67:3:67:14 | ... << ... | Operands of the '<<' operation have different underlying types. | +| test.cpp:18:3:18:6 | ... & ... | Operands of the '&' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:19:3:19:7 | ... \| ... | Operands of the '\|' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:20:3:20:7 | ... ^ ... | Operands of the '^' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:21:3:21:8 | ... << ... | Operands of the '<<' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:22:3:22:8 | ... >> ... | Operands of the '>>' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:23:3:23:8 | ... &= ... | Operands of the '&=' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:24:3:24:8 | ... \|= ... | Operands of the '\|=' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:25:3:25:8 | ... ^= ... | Operands of the '^=' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:26:3:26:9 | ... <<= ... | Operands of the '<<=' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:27:3:27:9 | ... >>= ... | Operands of the '>>=' operation have different underlying types 'unsigned int' and 'unsigned short'. | +| test.cpp:45:3:45:6 | ... & ... | Operands of the '&' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:46:3:46:7 | ... \| ... | Operands of the '\|' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:47:3:47:7 | ... ^ ... | Operands of the '^' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:48:3:48:8 | ... << ... | Operands of the '<<' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:49:3:49:8 | ... >> ... | Operands of the '>>' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:50:3:50:8 | ... &= ... | Operands of the '&=' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:51:3:51:8 | ... \|= ... | Operands of the '\|=' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:52:3:52:8 | ... ^= ... | Operands of the '^=' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:53:3:53:9 | ... <<= ... | Operands of the '<<=' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:54:3:54:9 | ... >>= ... | Operands of the '>>=' operation have different underlying types 'unsigned char' and 'unsigned short'. | +| test.cpp:67:3:67:14 | ... << ... | Operands of the '<<' operation have different underlying types 'int &' and 'char &'. | diff --git a/cpp/autosar/test/rules/M5-0-20/test.cpp b/cpp/autosar/test/rules/M5-0-20/test.cpp index 925a5ec6b8..2db9252445 100644 --- a/cpp/autosar/test/rules/M5-0-20/test.cpp +++ b/cpp/autosar/test/rules/M5-0-20/test.cpp @@ -71,3 +71,8 @@ void test463_2_instantiations() { char shift2 = 2; test463_2(val, shift2); } + +void test_add(char *val) { + int add = 2; + val += add; // COMPLIANT +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-21/test.cpp b/cpp/autosar/test/rules/M5-0-21/test.cpp index eb6c88c8be..0dbedd9f1f 100644 --- a/cpp/autosar/test/rules/M5-0-21/test.cpp +++ b/cpp/autosar/test/rules/M5-0-21/test.cpp @@ -45,4 +45,8 @@ void test() { u ^= u; // COMPLIANT u | 0; // COMPLIANT u |= 0; // COMPLIANT + + int *p = 0; + p += 1; // COMPLIANT + p -= 1; // COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-3/CvalueExpressionConvertedToDifferentUnderlyingType.expected b/cpp/autosar/test/rules/M5-0-3/CvalueExpressionConvertedToDifferentUnderlyingType.expected index 5782ac9849..8ce6a225dc 100644 --- a/cpp/autosar/test/rules/M5-0-3/CvalueExpressionConvertedToDifferentUnderlyingType.expected +++ b/cpp/autosar/test/rules/M5-0-3/CvalueExpressionConvertedToDifferentUnderlyingType.expected @@ -1,3 +1,7 @@ -| test.cpp:11:8:11:14 | (int16_t)... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:11:8:11:14 | ... + ... | expression | -| test.cpp:11:8:11:14 | ... + ... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:11:8:11:14 | ... + ... | expression | -| test.cpp:13:8:13:13 | ... + ... | Implicit conversion converts cvalue $@ from signed short to signed int. | test.cpp:13:8:13:13 | ... + ... | expression | +| test.cpp:12:8:12:14 | (int16_t)... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:12:8:12:14 | ... + ... | expression | +| test.cpp:12:8:12:14 | ... + ... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:12:8:12:14 | ... + ... | expression | +| test.cpp:14:8:14:13 | ... + ... | Implicit conversion converts cvalue $@ from signed short to signed int. | test.cpp:14:8:14:13 | ... + ... | expression | +| test.cpp:23:13:23:19 | (int16_t)... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:23:13:23:19 | ... + ... | expression | +| test.cpp:25:13:25:45 | (int16_t)... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:25:13:25:45 | static_cast... | expression | +| test.cpp:31:12:31:18 | (int16_t)... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:31:12:31:18 | ... + ... | expression | +| test.cpp:33:12:33:44 | (int16_t)... | Implicit conversion converts cvalue $@ from signed char to signed short. | test.cpp:33:12:33:44 | static_cast... | expression | diff --git a/cpp/autosar/test/rules/M5-0-3/test.cpp b/cpp/autosar/test/rules/M5-0-3/test.cpp index cb74512979..7275204519 100644 --- a/cpp/autosar/test/rules/M5-0-3/test.cpp +++ b/cpp/autosar/test/rules/M5-0-3/test.cpp @@ -1,4 +1,5 @@ #include + void f1() { using std::int16_t; using std::int32_t; @@ -13,4 +14,24 @@ void f1() { l3 = l2 + 1; // NON_COMPLIANT l3 = static_cast(l2) + 1; // COMPLIANT l3 = l2 + 0x01ffff; // COMPLIANT +} + +void int16_arg(std::int16_t t); + +void test_func_call() { + std::int8_t l1; + int16_arg(l1 + l1); // NON_COMPLIANT + int16_arg(static_cast(l1 + l1)); // COMPLIANT + int16_arg(static_cast(l1 + l1)); // NON_COMPLIANT +} + +std::int16_t test_return(int test) { + std::int8_t l1; + if (test > 0) { + return l1 + l1; // NON_COMPLIANT + } else if (test < 0) { + return static_cast(l1 + l1); // NON_COMPLIANT + } else { + return static_cast(l1 + l1); // COMPLIANT + } } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-7/test.cpp b/cpp/autosar/test/rules/M5-0-7/test.cpp index 36a2259028..ecbddd6750 100644 --- a/cpp/autosar/test/rules/M5-0-7/test.cpp +++ b/cpp/autosar/test/rules/M5-0-7/test.cpp @@ -18,4 +18,13 @@ void f1() { s16a = static_cast(f32a / f32b); // NON_COMPLIANT s16a = static_cast(f32a); // COMPLIANT s16a = static_cast(f32a) / f32b; // COMPLIANT +} + +void int_arg(std::int32_t i); + +std::int16_t test_args() { + float f32a; + float f32b; + int_arg(static_cast(f32a)); // COMPLIANT - f32a is not a cvalue + return static_cast(f32a); // COMPLIANT - f32a is not a cvalue } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-8/test.cpp b/cpp/autosar/test/rules/M5-0-8/test.cpp index 198bebed9f..ab785c661a 100644 --- a/cpp/autosar/test/rules/M5-0-8/test.cpp +++ b/cpp/autosar/test/rules/M5-0-8/test.cpp @@ -22,4 +22,22 @@ void f() { f64 = static_cast(1.0f + 1.0f); // NON_COMPLIANT f32 = static_cast(1.0f + 1); // COMPLIANT f64 = static_cast(1.0 + 1); // COMPLIANT; no suffix defines a double +} + +#include + +void function_args() { + std::vector v{0}; + + std::uint32_t u32{0}; + v.at(static_cast(u32)); // COMPLIANT - cast is not a cvalue + std::size_t st = + static_cast(u32); // COMPLIANT - cast is not a cvalue + v.at(st); +} + +std::size_t return_args() { + std::uint32_t u32{0}; + + return static_cast(u32); // COMPLIANT } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-0-9/ExplicitSignednessConversionOfCValue.expected b/cpp/autosar/test/rules/M5-0-9/ExplicitSignednessConversionOfCValue.expected index b2619503b3..b7fc97f99c 100644 --- a/cpp/autosar/test/rules/M5-0-9/ExplicitSignednessConversionOfCValue.expected +++ b/cpp/autosar/test/rules/M5-0-9/ExplicitSignednessConversionOfCValue.expected @@ -1,3 +1,3 @@ -| test.cpp:16:8:16:35 | static_cast... | Explicit integral conversion converts the signedness of the $@ from unsigned to signed. | test.cpp:16:28:16:34 | ... + ... | cvalue | -| test.cpp:18:8:18:40 | static_cast... | Explicit integral conversion converts the signedness of the $@ from unsigned to signed. | test.cpp:18:28:18:39 | ... + ... | cvalue | -| test.cpp:20:8:20:35 | static_cast... | Explicit integral conversion converts the signedness of the $@ from unsigned to signed. | test.cpp:20:28:20:34 | ... * ... | cvalue | +| test.cpp:20:8:20:35 | static_cast... | Explicit integral conversion converts the signedness of the $@ from unsigned to signed. | test.cpp:20:28:20:34 | ... + ... | cvalue | +| test.cpp:22:8:22:40 | static_cast... | Explicit integral conversion converts the signedness of the $@ from unsigned to signed. | test.cpp:22:28:22:39 | ... + ... | cvalue | +| test.cpp:24:8:24:35 | static_cast... | Explicit integral conversion converts the signedness of the $@ from unsigned to signed. | test.cpp:24:28:24:34 | ... * ... | cvalue | diff --git a/cpp/autosar/test/rules/M5-0-9/test.cpp b/cpp/autosar/test/rules/M5-0-9/test.cpp index b46dbc390f..7b050d24de 100644 --- a/cpp/autosar/test/rules/M5-0-9/test.cpp +++ b/cpp/autosar/test/rules/M5-0-9/test.cpp @@ -1,4 +1,8 @@ #include + +void signed_arg(std::uint32_t s); +void unsigned_arg(std::uint32_t u); + void f() { using std::int16_t; using std::int32_t; @@ -22,4 +26,7 @@ void f() { i16 = static_cast(i16 / i8); // NON_COMPLIANT i8 = static_cast(u8) + static_cast(u8); // COMPLIANT + + unsigned(static_cast(i32)); // COMPLIANT - i32 is not a cvalue + signed(static_cast(u32)); // COMPLIANT - u32 is not a cvalue } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.expected b/cpp/autosar/test/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.expected index f693f1a4be..75b3a45304 100644 --- a/cpp/autosar/test/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.expected +++ b/cpp/autosar/test/rules/M5-14-1/RightHandOperandOfALogicalAndOperatorsContainSideEffects.expected @@ -1,3 +1,4 @@ | test.cpp:15:7:15:14 | ... \|\| ... | The $@ may have a side effect that is not always evaluated. | test.cpp:15:12:15:14 | ... ++ | right-hand operand | | test.cpp:18:7:18:21 | ... \|\| ... | The $@ may have a side effect that is not always evaluated. | test.cpp:18:13:18:20 | ... == ... | right-hand operand | | test.cpp:21:7:21:15 | ... \|\| ... | The $@ may have a side effect that is not always evaluated. | test.cpp:21:12:21:13 | call to f1 | right-hand operand | +| test.cpp:40:7:40:41 | ... \|\| ... | The $@ may have a side effect that is not always evaluated. | test.cpp:40:26:40:26 | call to operator== | right-hand operand | diff --git a/cpp/autosar/test/rules/M5-14-1/test.cpp b/cpp/autosar/test/rules/M5-14-1/test.cpp index 2833b4fd44..76fd08c2ca 100644 --- a/cpp/autosar/test/rules/M5-14-1/test.cpp +++ b/cpp/autosar/test/rules/M5-14-1/test.cpp @@ -23,4 +23,20 @@ void f3(bool b) { if (b || f2()) { // COMPLIANT, f2 has local side-effects } +} + +int g1 = 0; +int f4() { return g1++; } +int f5() { return 1; } + +#include + +void f6() { + if (1 && sizeof(f4())) { + } // COMPLIANT - sizeof operands not evaluated + if (1 &&noexcept(f4()) &&noexcept(f4())) { + } // COMPLIANT - noexcept operands not evaluated + + if (1 || (typeid(f5()) == typeid(f4()))) { + } // NON_COMPLIANT - typeid operands not evaluated, but the ==operator is } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.expected b/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.expected deleted file mode 100644 index 89bf61f701..0000000000 --- a/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.expected +++ /dev/null @@ -1,16 +0,0 @@ -| test.cpp:16:7:16:50 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:17:7:17:18 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:23:7:23:19 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:24:7:24:19 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:30:7:30:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:31:7:31:17 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:35:7:35:39 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:36:7:36:39 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:43:7:43:56 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:44:7:44:20 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:51:7:51:19 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:52:7:52:19 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:58:7:58:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:59:7:59:17 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:63:7:63:45 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | -| test.cpp:64:7:64:45 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | diff --git a/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.qlref b/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.qlref deleted file mode 100644 index 5531830cbc..0000000000 --- a/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.testref b/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.testref new file mode 100644 index 0000000000..9d56f5d242 --- /dev/null +++ b/cpp/autosar/test/rules/M5-19-1/ConstantUnsignedIntegerExpressionsWrapAround.testref @@ -0,0 +1 @@ +cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-19-1/test.cpp b/cpp/autosar/test/rules/M5-19-1/test.cpp deleted file mode 100644 index 215c99bb30..0000000000 --- a/cpp/autosar/test/rules/M5-19-1/test.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -template constexpr T constexpr_min() { - return std::numeric_limits::min(); -} - -template constexpr T constexpr_max() { - return std::numeric_limits::max(); -} - -void test_signed_int() { - unsigned int a; - a = 1 + 1; // COMPLIANT - a = 0 - 1; // COMPLIANT - a = std::numeric_limits::min() - 1; // NON_COMPLIANT - a = UINT_MAX + 1; // NON_COMPLIANT - - const unsigned int const_min = std::numeric_limits::min(); - const unsigned int const_max = UINT_MAX; - a = const_min + 1; // COMPLIANT - a = const_max - 1; // COMPLIANT - a = const_min - 1; // NON_COMPLIANT - a = const_max + 1; // NON_COMPLIANT - -#define UNDERFLOW(x) (std::numeric_limits::min() - (x)) -#define OVERFLOW(x) (UINT_MAX + (x)) - a = UNDERFLOW(0); // COMPLIANT - a = OVERFLOW(0); // COMPLIANT - a = UNDERFLOW(1); // NON_COMPLIANT - a = OVERFLOW(1); // NON_COMPLIANT - - a = constexpr_min() + 1; // COMPLIANT - a = constexpr_max() - 1; // COMPLIANT - a = constexpr_min() - 1; // NON_COMPLIANT - a = constexpr_max() + 1; // NON_COMPLIANT -} - -void test_long_long() { - unsigned long long a; - a = 1 + 1; // COMPLIANT - a = 0 - 1; // COMPLIANT - a = std::numeric_limits::min() - 1; // NON_COMPLIANT - a = ULLONG_MAX + 1; // NON_COMPLIANT - - const unsigned long long const_min = - std::numeric_limits::min(); - const unsigned long long const_max = ULLONG_MAX; - a = const_min + 1; // COMPLIANT - a = const_max - 1; // COMPLIANT - a = const_min - 1; // NON_COMPLIANT - a = const_max + 1; // NON_COMPLIANT - -#define UNDERFLOW(x) (std::numeric_limits::min() - (x)) -#define OVERFLOW(x) (ULLONG_MAX + (x)) - a = UNDERFLOW(0); // COMPLIANT - a = OVERFLOW(0); // COMPLIANT - a = UNDERFLOW(1); // NON_COMPLIANT - a = OVERFLOW(1); // NON_COMPLIANT - - a = constexpr_min() + 1; // COMPLIANT - a = constexpr_max() - 1; // COMPLIANT - a = constexpr_min() - 1; // NON_COMPLIANT - a = constexpr_max() + 1; // NON_COMPLIANT -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-10/test.cpp b/cpp/autosar/test/rules/M5-2-10/test.cpp index bcd9d6bde0..01b84b9028 100644 --- a/cpp/autosar/test/rules/M5-2-10/test.cpp +++ b/cpp/autosar/test/rules/M5-2-10/test.cpp @@ -6,4 +6,6 @@ void f1() { ++l1; // COMPLIANT --l2; // COMPLIANT l3 = l1 * l2; + int *p; + *p++; // COMPLIANT - * is not an arithmetic operator } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.qlref b/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.qlref deleted file mode 100644 index 3a513b4cbe..0000000000 --- a/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.testref b/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.testref new file mode 100644 index 0000000000..06f2ec8fbb --- /dev/null +++ b/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.testref @@ -0,0 +1 @@ +cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.expected b/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.expected deleted file mode 100644 index 63c33f26d7..0000000000 --- a/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:2:3:2:34 | reinterpret_cast<..(*)(..)>... | Cast converting a pointer to function. | -| test.cpp:3:3:3:30 | reinterpret_cast... | Cast converting a pointer to function. | diff --git a/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.qlref b/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.qlref deleted file mode 100644 index 7f4d4c1161..0000000000 --- a/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M5-2-6/CastNotConvertPointerToFunction.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.testref b/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.testref new file mode 100644 index 0000000000..e7bde2ea08 --- /dev/null +++ b/cpp/autosar/test/rules/M5-2-6/CastNotConvertPointerToFunction.testref @@ -0,0 +1 @@ +cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-6/test.cpp b/cpp/autosar/test/rules/M5-2-6/test.cpp deleted file mode 100644 index ac14351b00..0000000000 --- a/cpp/autosar/test/rules/M5-2-6/test.cpp +++ /dev/null @@ -1,4 +0,0 @@ -void f(int) { - reinterpret_cast(&f); // NON_COMPLIANT - reinterpret_cast(&f); // NON_COMPLIANT -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.expected b/cpp/autosar/test/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.expected index e64af14ff5..10ca64a558 100644 --- a/cpp/autosar/test/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.expected +++ b/cpp/autosar/test/rules/M5-3-1/EachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.expected @@ -1,3 +1,3 @@ -| test.cpp:10:8:10:8 | 0 | bool operator called with a non-bool operand of type int. | -| test.cpp:12:7:12:7 | 0 | bool operator called with a non-bool operand of type int. | -| test.cpp:12:13:12:17 | ... + ... | bool operator called with a non-bool operand of type int. | +| test.cpp:10:8:10:8 | 0 | Call to bool operator with a non-bool operand of type 'int'. | +| test.cpp:12:7:12:7 | 0 | Call to bool operator with a non-bool operand of type 'int'. | +| test.cpp:12:13:12:17 | ... + ... | Call to bool operator with a non-bool operand of type 'int'. | diff --git a/cpp/autosar/test/rules/M5-3-1/test.cpp b/cpp/autosar/test/rules/M5-3-1/test.cpp index 9098e4e40e..4bda4c6682 100644 --- a/cpp/autosar/test/rules/M5-3-1/test.cpp +++ b/cpp/autosar/test/rules/M5-3-1/test.cpp @@ -25,4 +25,13 @@ template class A { void f() { A a; a.test1(); -} \ No newline at end of file +} + +template constexpr bool some_variable_template_v = false; +template <> constexpr bool some_variable_template_v = true; + +template +void template_with_no_except() noexcept(some_variable_template_v && + true) { // COMPLIANT +} +void test_template() { template_with_no_except(); } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.qlref b/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.qlref deleted file mode 100644 index 37d8a72ce5..0000000000 --- a/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.testref b/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.testref new file mode 100644 index 0000000000..bd12c39fbd --- /dev/null +++ b/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.testref @@ -0,0 +1 @@ +cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-2/test.cpp b/cpp/autosar/test/rules/M5-3-2/test.cpp deleted file mode 100644 index a731c3e43f..0000000000 --- a/cpp/autosar/test/rules/M5-3-2/test.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* The unary minus operator shall not be applied to an expression -whose underlying type is unsigned -*/ -#include - -int K; -void f() { - - std::uint16_t a = -K; // COMPLIANT - std::int16_t b = -a; // NON_COMPLIANT - std::uint64_t c = K; // COMPLIANTt - std::int64_t d = -c; // NON_COMPLIANT -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.expected b/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.expected deleted file mode 100644 index 3c2131f056..0000000000 --- a/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:6:5:6:13 | operator& | The unary & operator overloaded. | diff --git a/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.qlref b/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.qlref deleted file mode 100644 index 9e6cb1d0f8..0000000000 --- a/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M5-3-3/UnaryOperatorOverloaded.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.testref b/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.testref new file mode 100644 index 0000000000..1f2a126671 --- /dev/null +++ b/cpp/autosar/test/rules/M5-3-3/UnaryOperatorOverloaded.testref @@ -0,0 +1 @@ +cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-3-3/test.cpp b/cpp/autosar/test/rules/M5-3-3/test.cpp deleted file mode 100644 index 6c65066d11..0000000000 --- a/cpp/autosar/test/rules/M5-3-3/test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -/* The unary & operator shall not be overloaded */ -// do not defined it at all -class A - -{ - A operator&(); // NON_COMPLIANT -}; diff --git a/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.qlref b/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.qlref deleted file mode 100644 index 4ee6239a13..0000000000 --- a/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-3-1/LoopCompoundCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.testref b/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.testref new file mode 100644 index 0000000000..84dc7caf76 --- /dev/null +++ b/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.qlref b/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.qlref deleted file mode 100644 index eff312aa30..0000000000 --- a/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-3-1/SwitchCompoundCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.testref b/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.testref new file mode 100644 index 0000000000..f02b02ba85 --- /dev/null +++ b/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.expected b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.expected deleted file mode 100644 index 7716b684af..0000000000 --- a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:22:3:26:3 | if (...) ... | The $@ if statement does not terminate with an else construct. | test.cpp:24:10:26:3 | if (...) ... | if...else | -| test.cpp:42:5:46:5 | if (...) ... | The $@ if statement does not terminate with an else construct. | test.cpp:44:12:46:5 | if (...) ... | if...else | -| test.cpp:56:3:66:3 | if (...) ... | The $@ if statement does not terminate with an else construct. | test.cpp:58:10:66:3 | if (...) ... | if...else | diff --git a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.qlref b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.qlref deleted file mode 100644 index 28420bff1a..0000000000 --- a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-2/IfElseTerminationCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.testref b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.testref new file mode 100644 index 0000000000..d7ca04a26e --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-2/test.cpp b/cpp/autosar/test/rules/M6-4-2/test.cpp deleted file mode 100644 index 8689771d8e..0000000000 --- a/cpp/autosar/test/rules/M6-4-2/test.cpp +++ /dev/null @@ -1,67 +0,0 @@ -void test_ifelse_valid(int expression) { - int i = 3; - int j = 4; - int k; - if (expression > 0) { // GOOD - k = i + j; - } else if (expression < 100) { - k = i - j; - } else { - k = j * j; - } -} - -void test_ifelse_mix_validity(int expression) { - int i = 4; - int j = 7; - int k; - - if (expression > 0) { // GOOD - k = i * i; - } - if (expression > 10) { // BAD - k = i + j; - } else if (expression < 0) { - k = i * 2; - } -} - -void test_ifelse_nested_invalid(int expression) { - int i = 5; - int j = 7; - int k; - - if (expression > 0) { // GOOD - k = i * i * i; - } else { - k = i * j; - } - if (expression > 10) { // GOOD - k = i; - } else if (expression < 0) { - if (expression < -10) { // BAD - k = 5 + j; - } else if (expression < -20) { - k = i * 3; - } - } else { - k = 3; - } -} - -void test_ifelse_nested_valid(int expression) { - int i = 3; - int j = 1; - int k; - if (expression > 10) { // BAD - k = i + j; - } else if (expression < 0) { - if (i > 3) { // GOOD - k = j; - } else if (i < 10) { - k = i % 3; - } else { - i = i % 2; - } - } -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.expected b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.expected deleted file mode 100644 index 58fffdcdbb..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:6:3:28:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.cpp:6:3:28:3 | switch (...) ... | Switch | diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.qlref b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.qlref deleted file mode 100644 index 6cd5c459fb..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-3/SwitchDoesNotStartWithCase.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.testref b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.testref new file mode 100644 index 0000000000..4dd98ccfb9 --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.testref @@ -0,0 +1 @@ +cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.expected b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.expected deleted file mode 100644 index 6985297c15..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:51:3:57:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:51:3:57:3 | switch (...) ... | Switch | test.cpp:52:3:52:9 | case ...: | case | -| test.cpp:61:3:70:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:61:3:70:3 | switch (...) ... | Switch | test.cpp:62:3:62:10 | case ...: | case | -| test.cpp:76:3:82:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:76:3:82:3 | switch (...) ... | Switch | test.cpp:77:3:77:9 | case ...: | case | diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.qlref b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.qlref deleted file mode 100644 index d71f5220bc..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-3/SwitchStatementNotWellFormed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.testref b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.testref new file mode 100644 index 0000000000..7695dc2772 --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/test.cpp b/cpp/autosar/test/rules/M6-4-3/test.cpp deleted file mode 100644 index 4156053457..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/test.cpp +++ /dev/null @@ -1,92 +0,0 @@ -void f(int y); - -void test_caseclausenotfirst_invalid(int expression) { - int i = 5; - int j; - switch (expression) { - start: - expression = 4; // NON_COMPLIANT - first statement must be case clause - case 1: - if (i > 4) { - j = 3; - } - i = 3; - break; - case 2: - if (i % 2 == 0) { - j = 1; - } - break; - case 3: - if (i % 2 == 1) { - j = 8; - } - break; - default: - j = 5; - break; - } -} -void test_switch_caseclausefirst_valid(int expression) { - int i = 5; - int j; - switch (expression) { - case 2: - if (i % 2 == 0) { - j = 1; - } - break; - case 3: - if (i % 2 == 1) { - j = 8; - } - break; - default: - j = 5; - break; - } -} - -void test_notwellformedswitch_expr(int expression) { - switch (expression) { - case 1: - int y = expression + 1; // NON_COMPLIANT - `DeclStmt` whose parent - // statementis the switch body - f(y); - break; - } -} -void test_notwellformedswitch_jmp(int expression) { - int y = 2; - switch (expression) { - case 10: - f(y); - goto end; // NON_COMPLIANT - `JumpStmt` whose parent statement is the - // switch - // body - - case 2: - break; - } -end: - expression = 3; -} - -void test_notwellformedswitch_labelStmt(int expression) { - switch (expression) { - case 1: - start: - expression = 4; // NON_COMPLIANT - `LabelStmt` whose parent statement is the - // switch body - break; - } -} - -void test_emptyfallthrough(int expression) { - switch (expression) { - case 1: // COMPLIANT - default: - expression = 0; - break; - } -} diff --git a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.expected b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.expected deleted file mode 100644 index c9fbbdcb35..0000000000 --- a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:9:5:9:11 | case ...: | Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement. | test.cpp:6:3:17:3 | switch (...) ... | switch | test.cpp:9:5:9:11 | case ...: | case | -| test.cpp:36:5:36:11 | case ...: | Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement. | test.cpp:23:3:43:3 | switch (...) ... | switch | test.cpp:36:5:36:11 | case ...: | case | -| test.cpp:75:5:75:11 | case ...: | Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement. | test.cpp:73:3:78:3 | switch (...) ... | switch | test.cpp:75:5:75:11 | case ...: | case | diff --git a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.qlref b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.qlref deleted file mode 100644 index ad5727e1dc..0000000000 --- a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-4/NestedCaseInSwitch.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.testref b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.testref new file mode 100644 index 0000000000..6c5434b0e2 --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-4/test.cpp b/cpp/autosar/test/rules/M6-4-4/test.cpp deleted file mode 100644 index 6ccd440de3..0000000000 --- a/cpp/autosar/test/rules/M6-4-4/test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -void f(); - -void test_switch_nested_case_invalid(int expression) { - int i = 5; - int j; - switch (expression) { - case 1: // BAD - if (i > 4) { - case 2: - j = 3; - break; - } - break; - default: - j = 5; - break; - } -} - -void test_switch_nested_case_invalid_2(int expression) { - int i = 5; - int j; - switch (expression) { - case 1: - if (i > 4) { - j = 3; - } - break; - case 2: - if (i % 2 == 0) { - j = 1; - } - case 3: - if (i % 2 == 1) { - j = 8; - case 4: // BAD - j++; - } - break; - default: - j = 5; - break; - } -} - -void test_switch_valid(int expression) { - - int i = 5; - int j; - switch (expression) { - case 1: - if (i > 4) { - j = 3; - } - break; - case 2: - if (i % 2 == 0) { - j = 1; - } - break; - case 3: - if (i % 2 == 1) { - j = 8; - } - break; - default: - j = 5; - break; - } -} - -void test_singlecase_invalid(int expression) { - switch (expression) { - { - case 1: - f(); - } - } -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-5/test.cpp b/cpp/autosar/test/rules/M6-4-5/test.cpp index 84abf45278..9ba65c8e7b 100644 --- a/cpp/autosar/test/rules/M6-4-5/test.cpp +++ b/cpp/autosar/test/rules/M6-4-5/test.cpp @@ -40,6 +40,6 @@ void test_switchclause_termination2(int expression) { k = i; } default: // NON_COMPLIANT - ; + ; } } diff --git a/cpp/autosar/test/rules/M6-5-3/LoopCounterModifiedWithinStatement.expected b/cpp/autosar/test/rules/M6-5-3/LoopCounterModifiedWithinStatement.expected index a6988586f0..4643298e3a 100644 --- a/cpp/autosar/test/rules/M6-5-3/LoopCounterModifiedWithinStatement.expected +++ b/cpp/autosar/test/rules/M6-5-3/LoopCounterModifiedWithinStatement.expected @@ -2,3 +2,4 @@ | test.cpp:25:35:25:35 | x | Loop counters should not be modified within a statement in a for loop. | | test.cpp:36:5:36:5 | x | Loop counters should not be modified within a statement in a for loop. | | test.cpp:43:9:43:9 | i | Loop counters should not be modified within a statement in a for loop. | +| test.cpp:93:15:93:15 | i | Loop counters should not be modified within a statement in a for loop. | diff --git a/cpp/autosar/test/rules/M6-5-3/test.cpp b/cpp/autosar/test/rules/M6-5-3/test.cpp index a534e6ba8e..a41ba8a22d 100644 --- a/cpp/autosar/test/rules/M6-5-3/test.cpp +++ b/cpp/autosar/test/rules/M6-5-3/test.cpp @@ -43,3 +43,54 @@ void test_loop_counter_mod_in_side_effect() { inc(i); // NON_COMPLIANT - modifies `i` } } + +void test_loop_counter_reference_mod_in_condition() { + auto loop = [](int &i) { + for (; (i++ < 10); i++) { // NON_COMPLIANT + } + }; + int i = 0; + loop(i); +} + +void test_loop_counter_reference_mod() { + auto loop = [](int &i) { + for (; i < 10; i++) { // COMPLIANT + } + }; + int i = 0; + loop(i); +} + +void test_loop_const_reference() { + auto loop = []([[maybe_unused]] int const &i) { + for (int i = 0; i < 10; i++) { // COMPLIANT + } + }; + int i = 0; + loop(i); +} + +void test_loop_counter_reference_mod_in_statement() { + auto loop = [](int &i) { + for (; (i < 10); i++) { + i++; // NON_COMPLIANT + } + }; + int i = 0; + loop(i); +} + +int const_reference(int const &i) { return i; } + +int reference(int &i) { return i; } + +int copy(int i) { return i; } + +void test_pass_argument_by() { + for (int i = 0; i < 10; i++) { + const_reference(i); // COMPLIANT + reference(i); // NON_COMPLIANT + copy(i); // COMPLIANT + } +} diff --git a/cpp/autosar/test/rules/M6-5-5/LoopControlVariableModifiedInLoopExpression.expected b/cpp/autosar/test/rules/M6-5-5/LoopControlVariableModifiedInLoopExpression.expected index 2404bb0302..c79fc6abc6 100644 --- a/cpp/autosar/test/rules/M6-5-5/LoopControlVariableModifiedInLoopExpression.expected +++ b/cpp/autosar/test/rules/M6-5-5/LoopControlVariableModifiedInLoopExpression.expected @@ -1 +1,2 @@ | test.cpp:24:8:24:15 | testFlag | Loop control variable testFlag is modified in the loop update expression. | +| test.cpp:47:12:47:12 | y | Loop control variable y is modified in the loop update expression. | diff --git a/cpp/autosar/test/rules/M6-5-5/test.cpp b/cpp/autosar/test/rules/M6-5-5/test.cpp index 8957964514..ae4ab47855 100644 --- a/cpp/autosar/test/rules/M6-5-5/test.cpp +++ b/cpp/autosar/test/rules/M6-5-5/test.cpp @@ -24,3 +24,27 @@ void test_loop_control_variable_modified_in_expression() { testFlag = updateFlagWithIncrement(++x)) { // NON_COMPLIANT } } + +#include + +void test_const_refs(std::vector v) { + std::vector::iterator first = v.begin(); + std::vector::iterator last = v.end(); + // call to operator!= passes a const reference to first + for (; first != last; first++) { // COMPLIANT + } +} + +void update(std::vector::iterator &f, const int &x, int &y) {} + +void test_const_refs_update(std::vector v) { + std::vector::iterator last = v.end(); + int x = 0; + int y = 0; + // call to operator!= passes a const reference to first + for (std::vector::iterator first = v.begin(); first != last; update( + first, x, // COMPLIANT - first is a loop counter, so can be modified + y)) { // NON_COMPLIANT - y is modified and is not a loop counter + first + 1; + } +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.expected b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.expected deleted file mode 100644 index 58df2b5ec1..0000000000 --- a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.expected +++ /dev/null @@ -1,4 +0,0 @@ -| test.cpp:7:3:7:11 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:3:1:3:4 | label ...: | bad | -| test.cpp:21:3:21:11 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:17:1:17:4 | label ...: | bad | -| test.cpp:24:3:24:13 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:15:1:15:6 | label ...: | sobad | -| test.cpp:31:3:31:11 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:29:1:29:4 | label ...: | bad | diff --git a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.qlref b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.qlref deleted file mode 100644 index d66c789012..0000000000 --- a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-6-2/GotoStatementJumpCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.testref b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.testref new file mode 100644 index 0000000000..b4f807e8e2 --- /dev/null +++ b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-6-2/test.cpp b/cpp/autosar/test/rules/M6-6-2/test.cpp deleted file mode 100644 index e942077efd..0000000000 --- a/cpp/autosar/test/rules/M6-6-2/test.cpp +++ /dev/null @@ -1,32 +0,0 @@ -void test_goto_jump_forward_back() { - int i = 5; -bad: - if (i < 10) { - goto good; // GOOD - } - goto bad; // BAD - -good: - i++; -} - -void test_goto_mix_validity() { - int i = 5; -sobad: - i = i * i; -bad: - if (i < 10) { - goto good; // GOOD - } - goto bad; // BAD -good: - i++; - goto sobad; // BAD -} - -void test_goto_jumpsameline_invalid() { - int i = 3; -bad: - i = 4; - goto bad; // BAD -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.qlref b/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.qlref deleted file mode 100644 index f2ec336eec..0000000000 --- a/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M7-3-1/GlobalNamespaceMembershipViolation.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.testref b/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.testref new file mode 100644 index 0000000000..8f71738005 --- /dev/null +++ b/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.testref @@ -0,0 +1 @@ +cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.qlref b/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.qlref deleted file mode 100644 index 36bc86bb79..0000000000 --- a/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.testref b/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.testref new file mode 100644 index 0000000000..e149f3a33b --- /dev/null +++ b/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.expected b/cpp/autosar/test/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.expected index 23bc5f6560..350c1f0cdc 100644 --- a/cpp/autosar/test/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.expected +++ b/cpp/autosar/test/rules/M7-3-6/UsingDeclarationsUsedInHeaderFiles.expected @@ -1 +1,2 @@ | test.h:4:1:4:21 | using namespace std | Using directive or declaration used in a header file test.h. | +| test.h:18:3:18:21 | using namespace std | Using directive or declaration used in a header file test.h. | diff --git a/cpp/autosar/test/rules/M7-3-6/test.h b/cpp/autosar/test/rules/M7-3-6/test.h index 537e8ff3be..1286de2cf9 100644 --- a/cpp/autosar/test/rules/M7-3-6/test.h +++ b/cpp/autosar/test/rules/M7-3-6/test.h @@ -7,11 +7,15 @@ namespace my_namespace { int MY_CONST = 0; }; -int f() { +void f() { using my_namespace::MY_CONST; // COMPLIANT - function scope int x = MY_CONST; } +void test_fn_reported_in_400() { + using namespace std; // NON_COMPLIANT - only using declarations are exempted + // in function scope. +} #endif \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-4-1/UsageOfAssemblerNotDocumented.qlref b/cpp/autosar/test/rules/M7-4-1/UsageOfAssemblerNotDocumented.qlref deleted file mode 100644 index 164f0c5b29..0000000000 --- a/cpp/autosar/test/rules/M7-4-1/UsageOfAssemblerNotDocumented.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M7-4-1/UsageOfAssemblerNotDocumented.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-4-1/UsageOfAssemblerNotDocumented.testref b/cpp/autosar/test/rules/M7-4-1/UsageOfAssemblerNotDocumented.testref new file mode 100644 index 0000000000..ea9ce384ea --- /dev/null +++ b/cpp/autosar/test/rules/M7-4-1/UsageOfAssemblerNotDocumented.testref @@ -0,0 +1 @@ +cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.qlref b/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.qlref deleted file mode 100644 index 4cb410e095..0000000000 --- a/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M7-5-1/FunctionReturnAutomaticVarCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.testref b/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.testref new file mode 100644 index 0000000000..45dbffde00 --- /dev/null +++ b/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M7-5-1/test.cpp b/cpp/autosar/test/rules/M7-5-1/test.cpp deleted file mode 100644 index bc4fbf8f1d..0000000000 --- a/cpp/autosar/test/rules/M7-5-1/test.cpp +++ /dev/null @@ -1,35 +0,0 @@ -int *test_ptr_return_f2() { - int x = 100; - return &x; // NON_COMPLIANT -} - -int *test_ptr_return_f3(int y) { return &y; } // NON_COMPLIANT - -int &test_ref_return_f2() { - int x = 100; - return x; // NON_COMPLIANT -} - -int &test_ref_return_f3(int y) { return y; } // NON_COMPLIANT - -int *test_ptr_return_f4() { - static int x = 1; - return &x; // COMPLIANT -} - -int test_return_f2() { - int x = 100; - return x; // COMPLIANT -} -template void t1(T &x, T &y) { - if (x > y) - x = y; // NON_COMPLIANT[FALSE NEGATIVE] - else - x = -y; // NON_COMPLIANT[FALSE NEGATIVE] -} - -void test_templatefunction_return() { - int j = 2; - int k = 3; - t1(j, k); -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.qlref b/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.qlref deleted file mode 100644 index 2703512673..0000000000 --- a/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M8-0-1/MultipleGlobalOrMemberDeclarators.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.testref b/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.testref new file mode 100644 index 0000000000..434cb47456 --- /dev/null +++ b/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.testref @@ -0,0 +1 @@ +cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.qlref b/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.qlref deleted file mode 100644 index 2375201bf3..0000000000 --- a/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M8-0-1/MultipleLocalDeclarators.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.testref b/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.testref new file mode 100644 index 0000000000..be7c9ac352 --- /dev/null +++ b/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.testref @@ -0,0 +1 @@ +cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-0-1/test.cpp b/cpp/autosar/test/rules/M8-0-1/test.cpp deleted file mode 100644 index 12db420603..0000000000 --- a/cpp/autosar/test/rules/M8-0-1/test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -int g1, g2; // NON_COMPLIANT -int g3; // COMPLIANT - -namespace n1 { -int n_v1, n_v2; // NON_COMPLIANT -int n_v3; // COMPLIANT -} // namespace n1 - -void f() { - int l1, l2; // NON_COMPLIANT - int l3; // COMPLIANT -} - -class ClassA { - int m1, m2; // NON_COMPLIANT - int m3; // COMPLIANT -}; diff --git a/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.expected b/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.expected deleted file mode 100644 index b5cdd76a2b..0000000000 --- a/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:16:8:16:8 | f | $@ does not have the same default parameters as $@ | test.cpp:16:8:16:8 | f | overriding function | test.cpp:4:16:4:16 | f | overridden function | -| test.cpp:21:8:21:8 | f | $@ does not have the same default parameters as $@ | test.cpp:21:8:21:8 | f | overriding function | test.cpp:4:16:4:16 | f | overridden function | diff --git a/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.qlref b/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.qlref deleted file mode 100644 index ae0c1df157..0000000000 --- a/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.testref b/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.testref new file mode 100644 index 0000000000..7e06403515 --- /dev/null +++ b/cpp/autosar/test/rules/M8-3-1/VirtualFunctionParametersUseTheSameDefaultArguments.testref @@ -0,0 +1 @@ +cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-4-4/test.cpp b/cpp/autosar/test/rules/M8-4-4/test.cpp index aaf5ed055f..a1cd147aa8 100644 --- a/cpp/autosar/test/rules/M8-4-4/test.cpp +++ b/cpp/autosar/test/rules/M8-4-4/test.cpp @@ -25,7 +25,7 @@ void test_function_identifier_f2() { template static void Log(const F kF) {} -template static void LogFatal(const As &... rest) { +template static void LogFatal(const As &...rest) { Log([](const std::string &s) -> void {}); // COMPLIANT } diff --git a/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.expected b/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.expected deleted file mode 100644 index 6e72470cf2..0000000000 --- a/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.expected +++ /dev/null @@ -1,5 +0,0 @@ -| test.cpp:41:16:41:19 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 0 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | -| test.cpp:41:22:41:25 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 1 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | -| test.cpp:41:28:41:31 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 2 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | -| test.cpp:41:34:41:37 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 3 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | -| test.cpp:48:15:48:18 | {...} | Missing braces on aggregate literal of type $@ which is assigned to field $@. | test.cpp:6:10:6:10 | struct | struct | test.cpp:9:5:9:8 | m_s1 | m_s1 | diff --git a/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.qlref b/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.qlref deleted file mode 100644 index fb3df26b1e..0000000000 --- a/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M8-5-2/UseInitBracesToMatchTypeStructure.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.testref b/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.testref new file mode 100644 index 0000000000..06c190cf67 --- /dev/null +++ b/cpp/autosar/test/rules/M8-5-2/UseInitBracesToMatchTypeStructure.testref @@ -0,0 +1 @@ +cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-5-2/test.cpp b/cpp/autosar/test/rules/M8-5-2/test.cpp index b3a75e92ee..9c5efb726d 100644 --- a/cpp/autosar/test/rules/M8-5-2/test.cpp +++ b/cpp/autosar/test/rules/M8-5-2/test.cpp @@ -55,4 +55,13 @@ void test() { Bar b2{{0}}; // NON_COMPLIANT - missing explicit init, nested zero init StructNested n{}; // COMPLIANT StructNested n1 = {}; // COMPLIANT -} \ No newline at end of file +} + +#include +template bool all_of(std::initializer_list); + +template constexpr bool all_of(Args... args) noexcept { + return all_of({args...}); // COMPLIANT - explicitly initialized via varargs +} + +void test_all_of() { all_of(true, false, false); } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M9-3-1/ConstMemberFunctionReturnsNonConstPointer.expected b/cpp/autosar/test/rules/M9-3-1/ConstMemberFunctionReturnsNonConstPointer.expected index ee9652f505..af7e9efc36 100644 --- a/cpp/autosar/test/rules/M9-3-1/ConstMemberFunctionReturnsNonConstPointer.expected +++ b/cpp/autosar/test/rules/M9-3-1/ConstMemberFunctionReturnsNonConstPointer.expected @@ -1,3 +1,5 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstMemberFunctionReturnsNonConstPointer.ql:53,7-15) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (ConstMemberFunctionReturnsNonConstPointer.ql:55,7-15) | test.cpp:8:8:8:11 | getA | Const member function returns a pointer to class data $@. | test.cpp:3:8:3:8 | a | a | | test.cpp:9:8:9:11 | getB | Const member function returns a pointer to class data $@. | test.cpp:4:8:4:8 | b | b | | test.cpp:11:6:11:12 | getThis | Const member function returns a pointer to class data $@. | test.cpp:11:36:11:39 | this | this | diff --git a/cpp/autosar/test/rules/M9-3-3/test.cpp b/cpp/autosar/test/rules/M9-3-3/test.cpp index 033414a315..27e44c9a4b 100644 --- a/cpp/autosar/test/rules/M9-3-3/test.cpp +++ b/cpp/autosar/test/rules/M9-3-3/test.cpp @@ -161,3 +161,64 @@ class Z22 : Z1 { void f2() final {} // COMPLIANT void f3() { this->a = 100; } // COMPLIANT }; + +template class Array { +public: + T &back(); + +private: + T data[128]; + unsigned int size; +}; + +template class U> class Stack { +public: + T &Top() { + return this->data.back(); + } // COMPLIANT[FALSE_NEGATIVE|TRUE_NEGATIVE] - exception not specified in the + // standard, we opt to not raise an issue because the template can be both + // compliant and non-compliant depending on instantiations. +private: + U data; +}; + +using IntVectorStack = Stack; + +void test_template() { + IntVectorStack s; + + int i = s.Top(); +} + +class Z3 { + void f(int) = delete; // COMPLIANT +}; + +class Z4 { +public: + int values[128]; + template + void fill(const T &val) { // COMPLIANT[FALSE_NEGATIVE|TRUE_NEGATIVE] - + // exception not specified in the + // standard, we opt to not raise an issue because the template can be both + // compliant and non-compliant depending on instantiations. + for (auto &elem : values) { + elem = val; + } + } + constexpr int &front() noexcept { return values[0]; } // COMPLIANT +}; + +void fp_reported_in_381() { + // added to test template initialization effects/lack thereof + Z4 z; + int i = z.front(); + z.fill(i); +} + +class ZZ { +public: + template + void fp_616(const T &val) { + } // COMPLIANT - ignore uninstantiated templates for static also +}; diff --git a/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.expected b/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.expected deleted file mode 100644 index 26b9aac563..0000000000 --- a/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:2:14:2:14 | x | A named bit-field with signed integral type should have at least 2 bits of storage | diff --git a/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.qlref b/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.qlref deleted file mode 100644 index cdb9677f5f..0000000000 --- a/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.testref b/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.testref new file mode 100644 index 0000000000..5dd7991a37 --- /dev/null +++ b/cpp/autosar/test/rules/M9-6-4/NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit.testref @@ -0,0 +1 @@ +cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M9-6-4/test.cpp b/cpp/autosar/test/rules/M9-6-4/test.cpp deleted file mode 100644 index d3939b71ee..0000000000 --- a/cpp/autosar/test/rules/M9-6-4/test.cpp +++ /dev/null @@ -1,8 +0,0 @@ -struct S { - signed int x : 1; // NON-COMPLIANT - signed int y : 5; // COMPLIANT - signed int z : 7; // COMPLIANT - signed int : 0; // COMPLIANT - signed int : 1; // COMPLIANT - signed int : 2; // COMPLIANT -}; \ No newline at end of file diff --git a/cpp/cert/src/codeql-pack.lock.yml b/cpp/cert/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/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/cpp/cert/src/codeql-suites/cert-cpp-default.qls b/cpp/cert/src/codeql-suites/cert-cpp-default.qls new file mode 100644 index 0000000000..e9211246b1 --- /dev/null +++ b/cpp/cert/src/codeql-suites/cert-cpp-default.qls @@ -0,0 +1,9 @@ +- description: CERT C++ 2016 (Default) +- qlpack: codeql/cert-cpp-coding-standards +- include: + kind: + - problem + - path-problem +- exclude: + tags contain: + - external/cert/default-disabled diff --git a/cpp/cert/src/codeql-suites/cert-cpp-l1.qls b/cpp/cert/src/codeql-suites/cert-cpp-l1.qls new file mode 100644 index 0000000000..d96def2456 --- /dev/null +++ b/cpp/cert/src/codeql-suites/cert-cpp-l1.qls @@ -0,0 +1,12 @@ +- description: CERT C++ 2016 Level 1 Rules (Priority 12 - Priority 27) +- qlpack: codeql/cert-cpp-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/cpp/cert/src/codeql-suites/cert-cpp-l2.qls b/cpp/cert/src/codeql-suites/cert-cpp-l2.qls new file mode 100644 index 0000000000..b08cb07536 --- /dev/null +++ b/cpp/cert/src/codeql-suites/cert-cpp-l2.qls @@ -0,0 +1,12 @@ +- description: CERT C++ 2016 Level 2 Rules (Priority 6 - Priority 9) +- qlpack: codeql/cert-cpp-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/cpp/cert/src/codeql-suites/cert-cpp-l3.qls b/cpp/cert/src/codeql-suites/cert-cpp-l3.qls new file mode 100644 index 0000000000..ca621c96ab --- /dev/null +++ b/cpp/cert/src/codeql-suites/cert-cpp-l3.qls @@ -0,0 +1,12 @@ +- description: CERT C++ 2016 Level 3 Rules (Priority 1 - Priority 4) +- qlpack: codeql/cert-cpp-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/cpp/cert/src/codeql-suites/cert-cpp-single-translation-unit.qls b/cpp/cert/src/codeql-suites/cert-cpp-single-translation-unit.qls new file mode 100644 index 0000000000..2f09815e0d --- /dev/null +++ b/cpp/cert/src/codeql-suites/cert-cpp-single-translation-unit.qls @@ -0,0 +1,11 @@ +- description: CERT C++ 2016 (Single Translation Unit) +- qlpack: codeql/cert-cpp-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - scope/single-translation-unit +- exclude: + tags contain: + - external/cert/default-disabled diff --git a/cpp/cert/src/codeql-suites/cert-default.qls b/cpp/cert/src/codeql-suites/cert-default.qls index 6ec40af9b9..66599b60fb 100644 --- a/cpp/cert/src/codeql-suites/cert-default.qls +++ b/cpp/cert/src/codeql-suites/cert-default.qls @@ -1,9 +1,2 @@ -- description: CERT C++ 2016 (Default) -- qlpack: cert-cpp-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-cpp-default.qls instead" +- import: codeql-suites/cert-cpp-default.qls \ No newline at end of file diff --git a/cpp/cert/src/codeql-suites/cert-single-translation-unit.qls b/cpp/cert/src/codeql-suites/cert-single-translation-unit.qls index ca1315d6ff..4966648394 100644 --- a/cpp/cert/src/codeql-suites/cert-single-translation-unit.qls +++ b/cpp/cert/src/codeql-suites/cert-single-translation-unit.qls @@ -1,11 +1,2 @@ -- description: CERT C++ 2016 (Single Translation Unit) -- qlpack: cert-cpp-coding-standards -- include: - kind: - - problem - - path-problem - tags contain: - - scope/single-translation-unit -- exclude: - tags contain: - - external/cert/default-disabled \ No newline at end of file +- description: "DEPRECATED - CERT C++ 2016 (Single Translation Unit) - use cert-cpp-single-translation-unit.qls instead" +- import: codeql-suites/cert-cpp-single-translation-unit.qls \ No newline at end of file diff --git a/cpp/cert/src/qlpack.yml b/cpp/cert/src/qlpack.yml index 85e1db0ce7..e842352f1c 100644 --- a/cpp/cert/src/qlpack.yml +++ b/cpp/cert/src/qlpack.yml @@ -1,4 +1,9 @@ -name: cert-cpp-coding-standards -version: 2.9.0 +name: codeql/cert-cpp-coding-standards +version: 2.49.0-dev +description: CERT C++ 2016 suites: codeql-suites -libraryPathDependencies: common-cpp-coding-standards +license: MIT +default-suite-file: codeql-suites/cert-cpp-default.qls +dependencies: + codeql/cpp-all: 4.0.3 + codeql/common-cpp-coding-standards: '*' diff --git a/cpp/cert/src/rules/CON50-CPP/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql b/cpp/cert/src/rules/CON50-CPP/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql index f14dad7091..53f362e275 100644 --- a/cpp/cert/src/rules/CON50-CPP/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql +++ b/cpp/cert/src/rules/CON50-CPP/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con50-cpp * 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.cpp.cert import codingstandards.cpp.rules.donotallowamutextogooutofscopewhilelocked.DoNotAllowAMutexToGoOutOfScopeWhileLocked -class DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery { +class DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery +{ DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery() { this = ConcurrencyPackage::doNotAllowAMutexToGoOutOfScopeWhileLockedQuery() } diff --git a/cpp/cert/src/rules/CON50-CPP/DoNotDestroyAMutexWhileItIsLocked.ql b/cpp/cert/src/rules/CON50-CPP/DoNotDestroyAMutexWhileItIsLocked.ql index 2f2f5a6cdb..c15dfca5fc 100644 --- a/cpp/cert/src/rules/CON50-CPP/DoNotDestroyAMutexWhileItIsLocked.ql +++ b/cpp/cert/src/rules/CON50-CPP/DoNotDestroyAMutexWhileItIsLocked.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/con50-cpp * 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/cpp/cert/src/rules/CON51-CPP/EnsureActivelyHeldLocksAreReleasedOnExceptionalConditions.ql b/cpp/cert/src/rules/CON51-CPP/EnsureActivelyHeldLocksAreReleasedOnExceptionalConditions.ql index df17ec9a27..ac09d41c42 100644 --- a/cpp/cert/src/rules/CON51-CPP/EnsureActivelyHeldLocksAreReleasedOnExceptionalConditions.ql +++ b/cpp/cert/src/rules/CON51-CPP/EnsureActivelyHeldLocksAreReleasedOnExceptionalConditions.ql @@ -10,6 +10,11 @@ * @tags external/cert/id/con51-cpp * 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 */ diff --git a/cpp/cert/src/rules/CON52-CPP/PreventBitFieldAccessFromMultipleThreads.ql b/cpp/cert/src/rules/CON52-CPP/PreventBitFieldAccessFromMultipleThreads.ql index 49d5309113..9ca1a89525 100644 --- a/cpp/cert/src/rules/CON52-CPP/PreventBitFieldAccessFromMultipleThreads.ql +++ b/cpp/cert/src/rules/CON52-CPP/PreventBitFieldAccessFromMultipleThreads.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con52-cpp * 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/cpp/cert/src/rules/CON53-CPP/DeadlockByLockingInPredefinedOrder.ql b/cpp/cert/src/rules/CON53-CPP/DeadlockByLockingInPredefinedOrder.ql index 3989464f70..d83b3d520b 100644 --- a/cpp/cert/src/rules/CON53-CPP/DeadlockByLockingInPredefinedOrder.ql +++ b/cpp/cert/src/rules/CON53-CPP/DeadlockByLockingInPredefinedOrder.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con53-cpp * 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.cpp.cert import codingstandards.cpp.rules.preventdeadlockbylockinginpredefinedorder.PreventDeadlockByLockingInPredefinedOrder -class DeadlockByLockingInPredefinedOrderQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery { +class DeadlockByLockingInPredefinedOrderQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery +{ DeadlockByLockingInPredefinedOrderQuery() { this = ConcurrencyPackage::deadlockByLockingInPredefinedOrderQuery() } diff --git a/cpp/cert/src/rules/CON54-CPP/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql b/cpp/cert/src/rules/CON54-CPP/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql index 5584b7bec2..84255dbfc7 100644 --- a/cpp/cert/src/rules/CON54-CPP/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql +++ b/cpp/cert/src/rules/CON54-CPP/WrapFunctionsThatCanSpuriouslyWakeUpInLoop.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con54-cpp * 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/cpp/cert/src/rules/CON55-CPP/PreserveSafetyWhenUsingConditionVariables.ql b/cpp/cert/src/rules/CON55-CPP/PreserveSafetyWhenUsingConditionVariables.ql index faec80c67d..d4f43c7d09 100644 --- a/cpp/cert/src/rules/CON55-CPP/PreserveSafetyWhenUsingConditionVariables.ql +++ b/cpp/cert/src/rules/CON55-CPP/PreserveSafetyWhenUsingConditionVariables.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con55-cpp * 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.cpp.cert import codingstandards.cpp.rules.preservesafetywhenusingconditionvariables.PreserveSafetyWhenUsingConditionVariables -class PreserveSafetyWhenUsingConditionVariablesQuery extends PreserveSafetyWhenUsingConditionVariablesSharedQuery { +class PreserveSafetyWhenUsingConditionVariablesQuery extends PreserveSafetyWhenUsingConditionVariablesSharedQuery +{ PreserveSafetyWhenUsingConditionVariablesQuery() { this = ConcurrencyPackage::preserveSafetyWhenUsingConditionVariablesQuery() } diff --git a/cpp/cert/src/rules/CON56-CPP/DoNotSpeculativelyLockALockedNonRecursiveMutex.ql b/cpp/cert/src/rules/CON56-CPP/DoNotSpeculativelyLockALockedNonRecursiveMutex.ql index 6259ff67d3..67edf2fc22 100644 --- a/cpp/cert/src/rules/CON56-CPP/DoNotSpeculativelyLockALockedNonRecursiveMutex.ql +++ b/cpp/cert/src/rules/CON56-CPP/DoNotSpeculativelyLockALockedNonRecursiveMutex.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con56-cpp * correctness * concurrency + * 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 */ @@ -19,8 +24,8 @@ import codingstandards.cpp.Concurrency from LockProtectedControlFlowNode n where not isExcluded(n, ConcurrencyPackage::doNotSpeculativelyLockALockedNonRecursiveMutexQuery()) and - // problematic nodes are ones where a lock is active and there is an attempt to - // call a speculative locking function + // problematic nodes are ones where a lock is active and there is an attempt + // to call a speculative locking function n.(MutexFunctionCall).isSpeculativeLock() and not n.(MutexFunctionCall).isRecursive() and n.getAProtectingLock() = n.(MutexFunctionCall).getLock() diff --git a/cpp/cert/src/rules/CON56-CPP/LockedALockedNonRecursiveMutexAudit.ql b/cpp/cert/src/rules/CON56-CPP/LockedALockedNonRecursiveMutexAudit.ql index 87000ecfb3..09ec2fa3d5 100644 --- a/cpp/cert/src/rules/CON56-CPP/LockedALockedNonRecursiveMutexAudit.ql +++ b/cpp/cert/src/rules/CON56-CPP/LockedALockedNonRecursiveMutexAudit.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/con56-cpp * correctness * concurrency + * 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 */ @@ -19,8 +24,8 @@ import codingstandards.cpp.Concurrency from LockProtectedControlFlowNode n where not isExcluded(n, ConcurrencyPackage::lockedALockedNonRecursiveMutexAuditQuery()) and - // problematic nodes are ones where a lock is active and there is an attempt to - // call a speculative locking function + // problematic nodes are ones where a lock is active and there is an attempt + // to call a speculative locking function n.(MutexFunctionCall).isSpeculativeLock() and not n.(MutexFunctionCall).isRecursive() select n, "(Audit) Attempt to speculatively lock a non-recursive mutex while it is $@.", diff --git a/cpp/cert/src/rules/CTR50-CPP/ContainerAccessWithoutRangeCheckCert.ql b/cpp/cert/src/rules/CTR50-CPP/ContainerAccessWithoutRangeCheckCert.ql index a64e8fca2c..e5565ccbbb 100644 --- a/cpp/cert/src/rules/CTR50-CPP/ContainerAccessWithoutRangeCheckCert.ql +++ b/cpp/cert/src/rules/CTR50-CPP/ContainerAccessWithoutRangeCheckCert.ql @@ -10,6 +10,11 @@ * @tags external/cert/id/ctr50-cpp * 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 */ diff --git a/cpp/cert/src/rules/CTR51-CPP/UsesValidContainerElementAccess.ql b/cpp/cert/src/rules/CTR51-CPP/UsesValidContainerElementAccess.ql index 2163412435..0652f065cb 100644 --- a/cpp/cert/src/rules/CTR51-CPP/UsesValidContainerElementAccess.ql +++ b/cpp/cert/src/rules/CTR51-CPP/UsesValidContainerElementAccess.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/ctr51-cpp * 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 */ diff --git a/cpp/cert/src/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql b/cpp/cert/src/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql index d60227d2c8..b022869136 100644 --- a/cpp/cert/src/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql +++ b/cpp/cert/src/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/ctr52-cpp * 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 */ @@ -80,16 +85,7 @@ where iteratorCreationCall = outputContainer.getAnIteratorFunctionCall() and iteratorCreationCall = c.getOutputIteratorSource() | - // Guarded by a bounds check that ensures our destination is larger than "some" value - exists( - GuardCondition guard, ContainerAccessWithoutRangeCheck::ContainerSizeCall sizeCall, - boolean branch - | - globalValueNumber(sizeCall.getQualifier()) = - globalValueNumber(iteratorCreationCall.getQualifier()) and - guard.controls(c.getBasicBlock(), branch) and - relOpWithSwapAndNegate(guard, sizeCall, _, Greater(), _, branch) - ) + sizeCompareBoundsChecked(iteratorCreationCall, c) or // Container created with sufficient size for the input exists(ContainerAccessWithoutRangeCheck::ContainerConstructorCall outputIteratorConstructor | diff --git a/cpp/cert/src/rules/CTR53-CPP/UseValidIteratorRanges.ql b/cpp/cert/src/rules/CTR53-CPP/UseValidIteratorRanges.ql index d0afb7754c..1512a7fd99 100644 --- a/cpp/cert/src/rules/CTR53-CPP/UseValidIteratorRanges.ql +++ b/cpp/cert/src/rules/CTR53-CPP/UseValidIteratorRanges.ql @@ -8,12 +8,18 @@ * @problem.severity error * @tags external/cert/id/ctr53-cpp * 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.cpp.cert import codingstandards.cpp.Iterators +import semmle.code.cpp.dataflow.DataFlow predicate startEndArgumentsDoNotPointToTheSameContainer( IteratorRangeFunctionCall fc, Expr arg, string reason diff --git a/cpp/cert/src/rules/CTR54-CPP/DoNotSubtractIteratorsForDifferentContainers.ql b/cpp/cert/src/rules/CTR54-CPP/DoNotSubtractIteratorsForDifferentContainers.ql index f47f9db201..2401bcbf54 100644 --- a/cpp/cert/src/rules/CTR54-CPP/DoNotSubtractIteratorsForDifferentContainers.ql +++ b/cpp/cert/src/rules/CTR54-CPP/DoNotSubtractIteratorsForDifferentContainers.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/ctr54-cpp * 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/cpp/cert/src/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.ql b/cpp/cert/src/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.ql index 8f2aec6e7d..c6ea2c4518 100644 --- a/cpp/cert/src/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.ql +++ b/cpp/cert/src/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.ql @@ -8,30 +8,93 @@ * @problem.severity error * @tags external/cert/id/ctr55-cpp * 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.cpp.cert import codingstandards.cpp.Iterators +import semmle.code.cpp.controlflow.Dominance +import semmle.code.cpp.dataflow.DataFlow -from ContainerIteratorAccess it +/** + * Models a call to an iterator's `operator+` + */ +class AdditionOperatorFunctionCall extends AdditiveOperatorFunctionCall { + AdditionOperatorFunctionCall() { this.getTarget().hasName("operator+") } +} + +/** + * There exists a calculation for the reference one passed the end of some container + * An example derivation is: + * `end = begin() + size()` + */ +Expr getDerivedReferenceToOnePassedTheEndElement(Expr containerReference) { + exists( + ContainerAccessWithoutRangeCheck::ContainerSizeCall size, + ContainerAccessWithoutRangeCheck::ContainerBeginCall begin, AdditionOperatorFunctionCall calc + | + result = calc + | + DataFlow::localFlow(DataFlow::exprNode(size), DataFlow::exprNode(calc.getAChild+())) and + DataFlow::localFlow(DataFlow::exprNode(begin), DataFlow::exprNode(calc.getAChild+())) and + //make sure its the same container providing its size as giving the begin + globalValueNumber(begin.getQualifier()) = globalValueNumber(size.getQualifier()) and + containerReference = begin.getQualifier() + ) +} + +/** + * a wrapper predicate for a couple of types of permitted end bounds checks + */ +Expr getReferenceToOnePassedTheEndElement(Expr containerReference) { + //a container end access - v.end() + result instanceof ContainerAccessWithoutRangeCheck::ContainerEndCall and + containerReference = result.(FunctionCall).getQualifier() + or + result = getDerivedReferenceToOnePassedTheEndElement(containerReference) +} + +/** + * some guard exists like: `iterator != end` + * where a relevant`.end()` call flowed into end + */ +predicate isUpperBoundEndCheckedIteratorAccess(IteratorSource source, ContainerIteratorAccess it) { + exists( + Expr referenceToOnePassedTheEndElement, BasicBlock basicBlockOfIteratorAccess, + GuardCondition upperBoundCheck, ContainerIteratorAccess checkedIteratorAccess, + Expr containerReferenceFromEndGuard + | + //sufficient end guard + referenceToOnePassedTheEndElement = + getReferenceToOnePassedTheEndElement(containerReferenceFromEndGuard) and + //guard controls the access + upperBoundCheck.controls(basicBlockOfIteratorAccess, _) and + basicBlockOfIteratorAccess.contains(it) and + //guard is comprised of end check and an iterator access + DataFlow::localFlow(DataFlow::exprNode(referenceToOnePassedTheEndElement), + DataFlow::exprNode(upperBoundCheck.getChild(_))) and + upperBoundCheck.getChild(_) = checkedIteratorAccess and + //make sure its the same iterator being checked in the guard as accessed + checkedIteratorAccess.getOwningContainer() = it.getOwningContainer() and + //if its the end call itself (or its parts), make sure its the same container providing its end as giving the iterator + globalValueNumber(containerReferenceFromEndGuard) = globalValueNumber(source.getQualifier()) and + // and the guard call we match must be after the assignment call (to avoid valid guards protecting new iterator accesses further down) + source.getASuccessor*() = upperBoundCheck + ) +} + +from ContainerIteratorAccess it, IteratorSource source where not isExcluded(it, IteratorsPackage::doNotUseAnAdditiveOperatorOnAnIteratorQuery()) and it.isAdditiveOperation() and not exists(RangeBasedForStmt fs | fs.getUpdate().getAChild*() = it) and - // we get the neraby assignment - not exists(STLContainer c, FunctionCall nearbyAssigningIteratorCall, FunctionCall guardCall | - nearbyAssigningIteratorCall = it.getANearbyAssigningIteratorCall() and - // we look for calls to size or end - (guardCall = c.getACallToSize() or guardCall = c.getAnIteratorEndFunctionCall()) and - // such that the call to size is before this - // access - guardCall = it.getAPredecessor*() and - // and it uses the same qualifier as the one we were just assigned - nearbyAssigningIteratorCall.getQualifier().(VariableAccess).getTarget() = - guardCall.getQualifier().(VariableAccess).getTarget() and - // and the size call we match must be after the assignment call - nearbyAssigningIteratorCall.getASuccessor*() = guardCall - ) + source = it.getANearbyAssigningIteratorCall() and + not isUpperBoundEndCheckedIteratorAccess(source, it) and + not sizeCompareBoundsChecked(source, it) select it, "Increment of iterator may overflow since its bounds are not checked." diff --git a/cpp/cert/src/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.ql b/cpp/cert/src/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.ql index 50d0b68944..b4ac267225 100644 --- a/cpp/cert/src/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.ql +++ b/cpp/cert/src/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.ql @@ -8,13 +8,18 @@ * @problem.severity warning * @tags external/cert/id/ctr56-cpp * correctness + * 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.cpp.cert import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import NonFinalClassToPointerArithmeticExprFlow::PathGraph class ArrayAccessOrPointerArith extends Expr { ArrayAccessOrPointerArith() { @@ -38,12 +43,8 @@ class AddressOfPointerCreation extends ClassPointerCreation, AddressOfExpr { AddressOfPointerCreation() { this.getAnOperand().getUnderlyingType() instanceof Class } } -class NonFinalClassToPointerArithmeticExprConfig extends DataFlow::Configuration { - NonFinalClassToPointerArithmeticExprConfig() { - this = "NonFinalClassToPointerArithmeticExprConfig" - } - - override predicate isSource(DataFlow::Node source) { +module NonFinalClassToPointerArithmeticExprConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(Class c | source.asExpr() instanceof ClassPointerCreation and source.asExpr().getUnderlyingType().(PointerType).getBaseType() = c @@ -52,17 +53,21 @@ class NonFinalClassToPointerArithmeticExprConfig extends DataFlow::Configuration ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(ArrayAccessOrPointerArith e | e.getAnOperand() = sink.asExpr()) } } +module NonFinalClassToPointerArithmeticExprFlow = + DataFlow::Global; + from - ArrayAccessOrPointerArith e, Class clz, Variable v, DataFlow::PathNode source, - DataFlow::PathNode sink + ArrayAccessOrPointerArith e, Class clz, Variable v, + NonFinalClassToPointerArithmeticExprFlow::PathNode source, + NonFinalClassToPointerArithmeticExprFlow::PathNode sink where not isExcluded(e, PointersPackage::doNotUsePointerArithmeticOnPolymorphicObjectsQuery()) and - any(NonFinalClassToPointerArithmeticExprConfig c).hasFlowPath(source, sink) and + NonFinalClassToPointerArithmeticExprFlow::flowPath(source, sink) and v.getAnAssignedValue() = source.getNode().asExpr() and ( e.(PointerArithmeticOperation).getAnOperand() = sink.getNode().asExpr() diff --git a/cpp/cert/src/rules/CTR57-CPP/ProvideAValidOrderingPredicate.ql b/cpp/cert/src/rules/CTR57-CPP/ProvideAValidOrderingPredicate.ql index f28409bfc9..950ecd0c46 100644 --- a/cpp/cert/src/rules/CTR57-CPP/ProvideAValidOrderingPredicate.ql +++ b/cpp/cert/src/rules/CTR57-CPP/ProvideAValidOrderingPredicate.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/ctr57-cpp * correctness + * 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 */ diff --git a/cpp/cert/src/rules/CTR58-CPP/PredicateFunctionObjectsShouldNotBeMutable.ql b/cpp/cert/src/rules/CTR58-CPP/PredicateFunctionObjectsShouldNotBeMutable.ql index ab56f399a8..304b532b79 100644 --- a/cpp/cert/src/rules/CTR58-CPP/PredicateFunctionObjectsShouldNotBeMutable.ql +++ b/cpp/cert/src/rules/CTR58-CPP/PredicateFunctionObjectsShouldNotBeMutable.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/ctr58-cpp * correctness + * external/cert/severity/low + * external/cert/likelihood/likely + * external/cert/remediation-cost/high + * external/cert/priority/p3 + * external/cert/level/l3 * external/cert/obligation/rule */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.predicatefunctionobjectsshouldnotbemutable.PredicateFunctionObjectsShouldNotBeMutable -class PredicateFunctionObjectsShouldNotBeMutableQuery extends PredicateFunctionObjectsShouldNotBeMutableSharedQuery { +class PredicateFunctionObjectsShouldNotBeMutableQuery extends PredicateFunctionObjectsShouldNotBeMutableSharedQuery +{ PredicateFunctionObjectsShouldNotBeMutableQuery() { this = SideEffects2Package::predicateFunctionObjectsShouldNotBeMutableQuery() } diff --git a/cpp/cert/src/rules/DCL50-CPP/DoNotDefineACStyleVariadicFunction.ql b/cpp/cert/src/rules/DCL50-CPP/DoNotDefineACStyleVariadicFunction.ql index 368f154e22..b24988823c 100644 --- a/cpp/cert/src/rules/DCL50-CPP/DoNotDefineACStyleVariadicFunction.ql +++ b/cpp/cert/src/rules/DCL50-CPP/DoNotDefineACStyleVariadicFunction.ql @@ -9,6 +9,11 @@ * correctness * security * scope/single-translation-unit + * 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/cpp/cert/src/rules/DCL51-CPP/EnumeratorReusesReservedName.ql b/cpp/cert/src/rules/DCL51-CPP/EnumeratorReusesReservedName.ql index 074ae6ebfc..3f8ea668dd 100644 --- a/cpp/cert/src/rules/DCL51-CPP/EnumeratorReusesReservedName.ql +++ b/cpp/cert/src/rules/DCL51-CPP/EnumeratorReusesReservedName.ql @@ -10,6 +10,11 @@ * maintainability * readability * 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/cpp/cert/src/rules/DCL51-CPP/FunctionReusesReservedName.ql b/cpp/cert/src/rules/DCL51-CPP/FunctionReusesReservedName.ql index 8cae916a9a..74d683a0cb 100644 --- a/cpp/cert/src/rules/DCL51-CPP/FunctionReusesReservedName.ql +++ b/cpp/cert/src/rules/DCL51-CPP/FunctionReusesReservedName.ql @@ -10,6 +10,11 @@ * maintainability * readability * 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/cpp/cert/src/rules/DCL51-CPP/ObjectReusesReservedName.ql b/cpp/cert/src/rules/DCL51-CPP/ObjectReusesReservedName.ql index 03e1ef7264..fabf036198 100644 --- a/cpp/cert/src/rules/DCL51-CPP/ObjectReusesReservedName.ql +++ b/cpp/cert/src/rules/DCL51-CPP/ObjectReusesReservedName.ql @@ -10,6 +10,11 @@ * maintainability * readability * 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/cpp/cert/src/rules/DCL51-CPP/RedefiningOfStandardLibraryName.ql b/cpp/cert/src/rules/DCL51-CPP/RedefiningOfStandardLibraryName.ql index 974b231c26..3aaf5d37cb 100644 --- a/cpp/cert/src/rules/DCL51-CPP/RedefiningOfStandardLibraryName.ql +++ b/cpp/cert/src/rules/DCL51-CPP/RedefiningOfStandardLibraryName.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/dcl51-cpp * 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/cpp/cert/src/rules/DCL51-CPP/ReuseOfReservedIdentifier.ql b/cpp/cert/src/rules/DCL51-CPP/ReuseOfReservedIdentifier.ql index b32bdf70ba..583a768d22 100644 --- a/cpp/cert/src/rules/DCL51-CPP/ReuseOfReservedIdentifier.ql +++ b/cpp/cert/src/rules/DCL51-CPP/ReuseOfReservedIdentifier.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/dcl51-cpp * 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/cpp/cert/src/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.ql b/cpp/cert/src/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.ql index 4b37f3ec96..c85a7536e9 100644 --- a/cpp/cert/src/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.ql +++ b/cpp/cert/src/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/dcl51-cpp * 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 */ @@ -33,6 +38,8 @@ predicate isGeneratedByUserMacro(Declaration d) { from Locatable l, string s where not isExcluded(l, NamingPackage::useOfDoubleUnderscoreReservedPrefixQuery()) and + //exclude uses of __func__, which are modelled as LocalVariable declarations + not l.(LocalVariable).getName() = "__func__" and ( exists(Macro m | l = m and isReservedMacroPrefix(m) and s = m.getName()) or diff --git a/cpp/cert/src/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.ql b/cpp/cert/src/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.ql index f7dddb4d99..81036f6f57 100644 --- a/cpp/cert/src/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.ql +++ b/cpp/cert/src/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.ql @@ -10,14 +10,19 @@ * maintainability * readability * 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.cpp.cert -import codingstandards.cpp.UserDefinedLiteral +import codingstandards.cpp.UserDefinedLiteral as udl -from UserDefinedLiteral udl +from udl::UserDefinedLiteral udl where not isExcluded(udl, NamingPackage::useOfReservedLiteralSuffixIdentifierQuery()) and not udl.hasCompliantSuffix() diff --git a/cpp/cert/src/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.ql b/cpp/cert/src/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.ql index cf47ad444d..ed57351d6a 100644 --- a/cpp/cert/src/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.ql +++ b/cpp/cert/src/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/dcl51-cpp * 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 */ @@ -53,5 +58,8 @@ where isGeneratedByUserMacro(d) ) ) - ) + ) and + // Ignore compiler generated functions and variables + not l.(Function).isCompilerGenerated() and + not l.(Variable).isCompilerGenerated() select l, "Name $@ uses the reserved prefix '_'.", l, s diff --git a/cpp/cert/src/rules/DCL53-CPP/LocalConstructorInitializedObjectHidesIdentifier.ql b/cpp/cert/src/rules/DCL53-CPP/LocalConstructorInitializedObjectHidesIdentifier.ql index 237ebbe985..f576144c46 100644 --- a/cpp/cert/src/rules/DCL53-CPP/LocalConstructorInitializedObjectHidesIdentifier.ql +++ b/cpp/cert/src/rules/DCL53-CPP/LocalConstructorInitializedObjectHidesIdentifier.ql @@ -9,6 +9,11 @@ * @problem.severity warning * @tags external/cert/id/dcl53-cpp * 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 */ @@ -20,6 +25,6 @@ from UserVariable v, UserVariable hidden where not isExcluded(v, ScopePackage::localConstructorInitializedObjectHidesIdentifierQuery()) and v.getInitializer().getExpr() instanceof ConstructorCall and - hides(hidden, v) + hidesStrict(hidden, v) select v, "The declaration declares variable " + v.getName() + " that hides $@", hidden, hidden.getName() diff --git a/cpp/cert/src/rules/DCL53-CPP/LocalFunctionDeclaration.ql b/cpp/cert/src/rules/DCL53-CPP/LocalFunctionDeclaration.ql index 3f91530c84..45aa70dc31 100644 --- a/cpp/cert/src/rules/DCL53-CPP/LocalFunctionDeclaration.ql +++ b/cpp/cert/src/rules/DCL53-CPP/LocalFunctionDeclaration.ql @@ -8,12 +8,16 @@ * @problem.severity warning * @tags external/cert/id/dcl53-cpp * 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.cpp.cert -import codingstandards.cpp.Scope class LocalUserFunctionDeclarationEntry extends FunctionDeclarationEntry { DeclStmt ds; diff --git a/cpp/cert/src/rules/DCL54-CPP/SingularOverloadOfMemoryFunction.ql b/cpp/cert/src/rules/DCL54-CPP/SingularOverloadOfMemoryFunction.ql index 7f419397ee..8f168e90c8 100644 --- a/cpp/cert/src/rules/DCL54-CPP/SingularOverloadOfMemoryFunction.ql +++ b/cpp/cert/src/rules/DCL54-CPP/SingularOverloadOfMemoryFunction.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/dcl54-cpp * 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 */ diff --git a/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.md b/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.md index a4f9891f53..29231d4809 100644 --- a/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.md +++ b/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.md @@ -310,7 +310,7 @@ Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/cpluspl ## Implementation notes -None +The rule does not detect cases where fields may have uninitialized padding but are initialized via an initializer. ## References diff --git a/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.ql b/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.ql index 68b4ae5e3c..85b72afaeb 100644 --- a/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.ql +++ b/cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.ql @@ -8,15 +8,21 @@ * @problem.severity error * @tags external/cert/id/dcl55-cpp * 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.cpp.cert -import codingstandards.cpp.trustboundary.UninitializedField +import codingstandards.cpp.rules.informationleakageacrossboundaries.InformationLeakageAcrossBoundaries -from Element e, string alertMessage -where - not isExcluded(e, UninitializedPackage::informationLeakageAcrossTrustBoundariesQuery()) and - uninitializedFieldQuery(e, alertMessage) -select e, alertMessage +class InformationLeakageAcrossTrustBoundariesQuery extends InformationLeakageAcrossBoundariesSharedQuery +{ + InformationLeakageAcrossTrustBoundariesQuery() { + this = UninitializedPackage::informationLeakageAcrossTrustBoundariesQuery() + } +} diff --git a/cpp/cert/src/rules/DCL56-CPP/CyclesDuringStaticObjectInit.ql b/cpp/cert/src/rules/DCL56-CPP/CyclesDuringStaticObjectInit.ql index 1ad411427f..4eb94f3d1d 100644 --- a/cpp/cert/src/rules/DCL56-CPP/CyclesDuringStaticObjectInit.ql +++ b/cpp/cert/src/rules/DCL56-CPP/CyclesDuringStaticObjectInit.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/dcl56-cpp * correctness * maintainability + * 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/cpp/cert/src/rules/DCL57-CPP/DoNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions.ql b/cpp/cert/src/rules/DCL57-CPP/DoNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions.ql index 951169abe5..6f625fd308 100644 --- a/cpp/cert/src/rules/DCL57-CPP/DoNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions.ql +++ b/cpp/cert/src/rules/DCL57-CPP/DoNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/dcl57-cpp * 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/cpp/cert/src/rules/DCL58-CPP/ModificationOfTheStandardNamespaces.ql b/cpp/cert/src/rules/DCL58-CPP/ModificationOfTheStandardNamespaces.ql index d8a33090ba..81242bc0f4 100644 --- a/cpp/cert/src/rules/DCL58-CPP/ModificationOfTheStandardNamespaces.ql +++ b/cpp/cert/src/rules/DCL58-CPP/ModificationOfTheStandardNamespaces.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/dcl58-cpp * correctness + * 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 */ @@ -16,7 +21,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.nonstandardentitiesinstandardnamespaces.NonStandardEntitiesInStandardNamespaces -class ModificationOfTheStandardNamespacesQuery extends NonStandardEntitiesInStandardNamespacesSharedQuery { +class ModificationOfTheStandardNamespacesQuery extends NonStandardEntitiesInStandardNamespacesSharedQuery +{ ModificationOfTheStandardNamespacesQuery() { this = ScopePackage::modificationOfTheStandardNamespacesQuery() } diff --git a/cpp/cert/src/rules/DCL59-CPP/UnnamedNamespaceInHeaderFile.ql b/cpp/cert/src/rules/DCL59-CPP/UnnamedNamespaceInHeaderFile.ql index 57dae96f09..2b8b364c7d 100644 --- a/cpp/cert/src/rules/DCL59-CPP/UnnamedNamespaceInHeaderFile.ql +++ b/cpp/cert/src/rules/DCL59-CPP/UnnamedNamespaceInHeaderFile.ql @@ -10,6 +10,11 @@ * @problem.severity error * @tags external/cert/id/dcl59-cpp * 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/cpp/cert/src/rules/DCL60-CPP/OneDefinitionRuleNotObeyed.ql b/cpp/cert/src/rules/DCL60-CPP/OneDefinitionRuleNotObeyed.ql index 7908609cc6..84e63a9569 100644 --- a/cpp/cert/src/rules/DCL60-CPP/OneDefinitionRuleNotObeyed.ql +++ b/cpp/cert/src/rules/DCL60-CPP/OneDefinitionRuleNotObeyed.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/dcl60-cpp * correctness + * external/cert/severity/high + * external/cert/likelihood/unlikely + * external/cert/remediation-cost/high + * external/cert/priority/p3 + * external/cert/level/l3 * external/cert/obligation/rule */ diff --git a/cpp/cert/src/rules/ERR50-CPP/ConditionVariablePostConditionFailedCert.ql b/cpp/cert/src/rules/ERR50-CPP/ConditionVariablePostConditionFailedCert.ql index d183bfa446..40a884fc5a 100644 --- a/cpp/cert/src/rules/ERR50-CPP/ConditionVariablePostConditionFailedCert.ql +++ b/cpp/cert/src/rules/ERR50-CPP/ConditionVariablePostConditionFailedCert.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/err50-cpp * correctness * external/cert/audit + * 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.cpp.cert import codingstandards.cpp.rules.conditionvariablepostconditionfailed.ConditionVariablePostConditionFailed -class ConditionVariablePostConditionFailedCertQuery extends ConditionVariablePostConditionFailedSharedQuery { +class ConditionVariablePostConditionFailedCertQuery extends ConditionVariablePostConditionFailedSharedQuery +{ ConditionVariablePostConditionFailedCertQuery() { this = Exceptions1Package::conditionVariablePostConditionFailedCertQuery() } diff --git a/cpp/cert/src/rules/ERR50-CPP/ExitHandlerThrowsExceptionCert.ql b/cpp/cert/src/rules/ERR50-CPP/ExitHandlerThrowsExceptionCert.ql index 9c312672e7..548b7b4b94 100644 --- a/cpp/cert/src/rules/ERR50-CPP/ExitHandlerThrowsExceptionCert.ql +++ b/cpp/cert/src/rules/ERR50-CPP/ExitHandlerThrowsExceptionCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err50-cpp * 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/cpp/cert/src/rules/ERR50-CPP/ExplicitAbruptTerminationCert.ql b/cpp/cert/src/rules/ERR50-CPP/ExplicitAbruptTerminationCert.ql index ddee05aecf..4fe89c634d 100644 --- a/cpp/cert/src/rules/ERR50-CPP/ExplicitAbruptTerminationCert.ql +++ b/cpp/cert/src/rules/ERR50-CPP/ExplicitAbruptTerminationCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err50-cpp * 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/cpp/cert/src/rules/ERR50-CPP/JoinableThreadCopiedOrDestroyedCert.ql b/cpp/cert/src/rules/ERR50-CPP/JoinableThreadCopiedOrDestroyedCert.ql index 015a5ffede..2036ff2f46 100644 --- a/cpp/cert/src/rules/ERR50-CPP/JoinableThreadCopiedOrDestroyedCert.ql +++ b/cpp/cert/src/rules/ERR50-CPP/JoinableThreadCopiedOrDestroyedCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err50-cpp * 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/cpp/cert/src/rules/ERR50-CPP/RethrowNestedWithoutCaptureCert.ql b/cpp/cert/src/rules/ERR50-CPP/RethrowNestedWithoutCaptureCert.ql index 088cfe93b0..05d04de99e 100644 --- a/cpp/cert/src/rules/ERR50-CPP/RethrowNestedWithoutCaptureCert.ql +++ b/cpp/cert/src/rules/ERR50-CPP/RethrowNestedWithoutCaptureCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err50-cpp * 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/cpp/cert/src/rules/ERR51-CPP/HandleAllExceptions.ql b/cpp/cert/src/rules/ERR51-CPP/HandleAllExceptions.ql index 2811815821..f8447d4af5 100644 --- a/cpp/cert/src/rules/ERR51-CPP/HandleAllExceptions.ql +++ b/cpp/cert/src/rules/ERR51-CPP/HandleAllExceptions.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err51-cpp * 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/cpp/cert/src/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.ql b/cpp/cert/src/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.ql index ff7fc47dd6..6c9cb2e436 100644 --- a/cpp/cert/src/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.ql +++ b/cpp/cert/src/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.ql @@ -8,22 +8,18 @@ * @tags external/cert/id/err52-cpp * correctness * scope/single-translation-unit + * 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.cpp.cert +import codingstandards.cpp.rules.donotusesetjmporlongjmpshared.DoNotUseSetjmpOrLongjmpShared -predicate isLongJumpCall(FunctionCall fc) { fc.getTarget().hasGlobalOrStdName("longjmp") } - -predicate isSetJumpCall(MacroInvocation mi) { mi.getMacroName() = "setjmp" } - -from Element jmp, string callType -where - not isExcluded(jmp, BannedFunctionsPackage::doNotUseSetjmpOrLongjmpQuery()) and - ( - isLongJumpCall(jmp) and callType = "longjmp function" - or - isSetJumpCall(jmp) and callType = "setjmp macro" - ) -select jmp, "Use of banned " + callType + "." +class DoNotUseSetjmpOrLongjmpQuery extends DoNotUseSetjmpOrLongjmpSharedSharedQuery { + DoNotUseSetjmpOrLongjmpQuery() { this = BannedFunctionsPackage::doNotUseSetjmpOrLongjmpQuery() } +} diff --git a/cpp/cert/src/rules/ERR53-CPP/DestroyedValueReferencedInConstructorDestructorCatchBlock.ql b/cpp/cert/src/rules/ERR53-CPP/DestroyedValueReferencedInConstructorDestructorCatchBlock.ql index b1709d2340..8587a73c33 100644 --- a/cpp/cert/src/rules/ERR53-CPP/DestroyedValueReferencedInConstructorDestructorCatchBlock.ql +++ b/cpp/cert/src/rules/ERR53-CPP/DestroyedValueReferencedInConstructorDestructorCatchBlock.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/err53-cpp * 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 */ @@ -16,7 +21,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.destroyedvaluereferencedindestructorcatchblock.DestroyedValueReferencedInDestructorCatchBlock -class DestroyedValueReferencedInConstructorDestructorCatchBlockQuery extends DestroyedValueReferencedInDestructorCatchBlockSharedQuery { +class DestroyedValueReferencedInConstructorDestructorCatchBlockQuery extends DestroyedValueReferencedInDestructorCatchBlockSharedQuery +{ DestroyedValueReferencedInConstructorDestructorCatchBlockQuery() { this = Exceptions2Package::destroyedValueReferencedInDestructorCatchBlockQuery() } diff --git a/cpp/cert/src/rules/ERR54-CPP/CatchBlockShadowingCert.ql b/cpp/cert/src/rules/ERR54-CPP/CatchBlockShadowingCert.ql index 8c4c5b5f06..c3e0aeb2f5 100644 --- a/cpp/cert/src/rules/ERR54-CPP/CatchBlockShadowingCert.ql +++ b/cpp/cert/src/rules/ERR54-CPP/CatchBlockShadowingCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err54-cpp * correctness + * external/cert/severity/medium + * external/cert/likelihood/likely + * external/cert/remediation-cost/low + * external/cert/priority/p18 + * external/cert/level/l1 * external/cert/obligation/rule */ diff --git a/cpp/cert/src/rules/ERR55-CPP/HonorExceptionSpecifications.ql b/cpp/cert/src/rules/ERR55-CPP/HonorExceptionSpecifications.ql index 7d433e2480..4f35d3cd93 100644 --- a/cpp/cert/src/rules/ERR55-CPP/HonorExceptionSpecifications.ql +++ b/cpp/cert/src/rules/ERR55-CPP/HonorExceptionSpecifications.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/err55-cpp * correctness + * 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/cpp/cert/src/rules/ERR56-CPP/GuaranteeExceptionSafety.ql b/cpp/cert/src/rules/ERR56-CPP/GuaranteeExceptionSafety.ql index b027d02e3f..5831a7f404 100644 --- a/cpp/cert/src/rules/ERR56-CPP/GuaranteeExceptionSafety.ql +++ b/cpp/cert/src/rules/ERR56-CPP/GuaranteeExceptionSafety.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/err56-cpp * correctness + * 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 */ diff --git a/cpp/cert/src/rules/ERR57-CPP/DoNotLeakResourcesWhenHandlingExceptions.ql b/cpp/cert/src/rules/ERR57-CPP/DoNotLeakResourcesWhenHandlingExceptions.ql index e283ca8e95..6180bf2f83 100644 --- a/cpp/cert/src/rules/ERR57-CPP/DoNotLeakResourcesWhenHandlingExceptions.ql +++ b/cpp/cert/src/rules/ERR57-CPP/DoNotLeakResourcesWhenHandlingExceptions.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/err57-cpp * correctness * security + * 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 */ diff --git a/cpp/cert/src/rules/ERR58-CPP/HandleAllExceptionsThrownBeforeMainBeginsExecuting.ql b/cpp/cert/src/rules/ERR58-CPP/HandleAllExceptionsThrownBeforeMainBeginsExecuting.ql index a6d4baf0bb..ca6b6ae83f 100644 --- a/cpp/cert/src/rules/ERR58-CPP/HandleAllExceptionsThrownBeforeMainBeginsExecuting.ql +++ b/cpp/cert/src/rules/ERR58-CPP/HandleAllExceptionsThrownBeforeMainBeginsExecuting.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/err58-cpp * correctness + * 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 */ @@ -14,7 +19,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.handleallexceptionsduringstartup.HandleAllExceptionsDuringStartup -class HandleAllExceptionsThrownBeforeMainBeginsExcecutingQuery extends HandleAllExceptionsDuringStartupSharedQuery { +class HandleAllExceptionsThrownBeforeMainBeginsExcecutingQuery extends HandleAllExceptionsDuringStartupSharedQuery +{ HandleAllExceptionsThrownBeforeMainBeginsExcecutingQuery() { this = Exceptions1Package::handleAllExceptionsThrownBeforeMainBeginsExecutingQuery() } diff --git a/cpp/cert/src/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.ql b/cpp/cert/src/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.ql index 902d392c5f..e1c7af4030 100644 --- a/cpp/cert/src/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.ql +++ b/cpp/cert/src/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.ql @@ -11,6 +11,11 @@ * @problem.severity error * @tags external/cert/id/err59-cpp * 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 */ diff --git a/cpp/cert/src/rules/ERR60-CPP/ExceptionObjectsMustBeNothrowCopyConstructible.ql b/cpp/cert/src/rules/ERR60-CPP/ExceptionObjectsMustBeNothrowCopyConstructible.ql index 37a5fedd14..61a145c7a1 100644 --- a/cpp/cert/src/rules/ERR60-CPP/ExceptionObjectsMustBeNothrowCopyConstructible.ql +++ b/cpp/cert/src/rules/ERR60-CPP/ExceptionObjectsMustBeNothrowCopyConstructible.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err60-cpp * 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/cpp/cert/src/rules/ERR61-CPP/CatchExceptionsByLvalueReference.ql b/cpp/cert/src/rules/ERR61-CPP/CatchExceptionsByLvalueReference.ql index aa721f2eb2..8cc9c47854 100644 --- a/cpp/cert/src/rules/ERR61-CPP/CatchExceptionsByLvalueReference.ql +++ b/cpp/cert/src/rules/ERR61-CPP/CatchExceptionsByLvalueReference.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err61-cpp * 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 */ @@ -15,7 +20,7 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.catchexceptionsbylvaluereference.CatchExceptionsByLvalueReference -class CatchExceptionsByLvalueReferenceQuery extends CatchExceptionsByLValueReferenceSharedQuery { +class CatchExceptionsByLvalueReferenceQuery extends CatchExceptionsByLvalueReferenceSharedQuery { CatchExceptionsByLvalueReferenceQuery() { this = Exceptions1Package::catchExceptionsByLvalueReferenceQuery() } diff --git a/cpp/cert/src/rules/ERR62-CPP/DetectErrorsWhenConvertingAStringToANumber.ql b/cpp/cert/src/rules/ERR62-CPP/DetectErrorsWhenConvertingAStringToANumber.ql index 0f7d458fad..e5451a0fc4 100644 --- a/cpp/cert/src/rules/ERR62-CPP/DetectErrorsWhenConvertingAStringToANumber.ql +++ b/cpp/cert/src/rules/ERR62-CPP/DetectErrorsWhenConvertingAStringToANumber.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/err62-cpp * 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 */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.stringnumberconversionmissingerrorcheck.StringNumberConversionMissingErrorCheck -class DetectErrorsWhenConvertingAStringToANumberQuery extends StringNumberConversionMissingErrorCheckSharedQuery { +class DetectErrorsWhenConvertingAStringToANumberQuery extends StringNumberConversionMissingErrorCheckSharedQuery +{ DetectErrorsWhenConvertingAStringToANumberQuery() { this = TypeRangesPackage::detectErrorsWhenConvertingAStringToANumberQuery() } diff --git a/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql b/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql index f4b3d6d710..960d04449e 100644 --- a/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql +++ b/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp50-cpp * 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 */ @@ -16,7 +21,7 @@ import codingstandards.cpp.cert import codingstandards.cpp.SideEffect import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.dataflow.TaintTracking -import semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl +import semmle.code.cpp.valuenumbering.GlobalValueNumbering /** Holds if the function's return value is derived from the `AliasParamter` p. */ predicate returnValueDependsOnAliasParameter(AliasParameter p) { diff --git a/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql b/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql index 1ddb315506..4c268e9c7e 100644 --- a/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql +++ b/cpp/cert/src/rules/EXP50-CPP/DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp50-cpp * 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/cpp/cert/src/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql b/cpp/cert/src/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql index 1391a4d79f..d0935cc798 100644 --- a/cpp/cert/src/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql +++ b/cpp/cert/src/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql @@ -8,31 +8,36 @@ * @problem.severity error * @tags external/cert/id/exp51-cpp * 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.cpp.cert import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import AllocationToDeleteFlow::PathGraph -class AllocationToDeleteConfig extends DataFlow::Configuration { - AllocationToDeleteConfig() { this = "AllocationToDelete" } +module AllocationToDeleteConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NewArrayExpr } - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NewArrayExpr } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(DeleteArrayExpr dae | dae.getExpr() = sink.asExpr()) } } +module AllocationToDeleteFlow = DataFlow::Global; + from - AllocationToDeleteConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + AllocationToDeleteFlow::PathNode source, AllocationToDeleteFlow::PathNode sink, NewArrayExpr newArray, DeleteArrayExpr deleteArray where not isExcluded(deleteArray.getExpr(), FreedPackage::doNotDeleteAnArrayThroughAPointerOfTheIncorrectTypeQuery()) and - config.hasFlowPath(source, sink) and + AllocationToDeleteFlow::flowPath(source, sink) and newArray = source.getNode().asExpr() and deleteArray.getExpr() = sink.getNode().asExpr() and not newArray.getType().getUnspecifiedType() = deleteArray.getExpr().getType().getUnspecifiedType() diff --git a/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.ql b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.ql index 217be3db6a..59745c2cd0 100644 --- a/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.ql +++ b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp52-cpp * 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/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclValExpression.ql b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclValExpression.ql index 93bb653c11..c9ced6825c 100644 --- a/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclValExpression.ql +++ b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclValExpression.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp52-cpp * 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/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInNoExceptOperand.ql b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInNoExceptOperand.ql index a32aa1eb14..d8ed036a06 100644 --- a/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInNoExceptOperand.ql +++ b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInNoExceptOperand.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp52-cpp * 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/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.ql b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.ql index 4cc602362e..aa0b8ff23a 100644 --- a/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.ql +++ b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp52-cpp * 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/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInTypeIdOperand.ql b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInTypeIdOperand.ql index cc43a008d9..dc65dddcd1 100644 --- a/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInTypeIdOperand.ql +++ b/cpp/cert/src/rules/EXP52-CPP/DoNotRelyOnSideEffectsInTypeIdOperand.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp52-cpp * 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/cpp/cert/src/rules/EXP53-CPP/DoNotReadUninitializedMemory.ql b/cpp/cert/src/rules/EXP53-CPP/DoNotReadUninitializedMemory.ql index 47ee746038..9839fae0fd 100644 --- a/cpp/cert/src/rules/EXP53-CPP/DoNotReadUninitializedMemory.ql +++ b/cpp/cert/src/rules/EXP53-CPP/DoNotReadUninitializedMemory.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/exp53-cpp * 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/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedAfterLifetimeCert.ql b/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedAfterLifetimeCert.ql index 4f72fc725a..534bb83796 100644 --- a/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedAfterLifetimeCert.ql +++ b/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedAfterLifetimeCert.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/exp54-cpp * 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/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedBeforeLifetimeCert.ql b/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedBeforeLifetimeCert.ql index d97c002dbd..ea2349194b 100644 --- a/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedBeforeLifetimeCert.ql +++ b/cpp/cert/src/rules/EXP54-CPP/ObjectAccessedBeforeLifetimeCert.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/exp54-cpp * 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/cpp/cert/src/rules/EXP55-CPP/RemoveConstOrVolatileQualificationCert.ql b/cpp/cert/src/rules/EXP55-CPP/RemoveConstOrVolatileQualificationCert.ql index c7960b1b66..68216f2e43 100644 --- a/cpp/cert/src/rules/EXP55-CPP/RemoveConstOrVolatileQualificationCert.ql +++ b/cpp/cert/src/rules/EXP55-CPP/RemoveConstOrVolatileQualificationCert.ql @@ -8,6 +8,11 @@ * @problem.severity warning * @tags external/cert/id/exp55-cpp * 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 */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.removeconstorvolatilequalification.RemoveConstOrVolatileQualification -class RemoveConstOrVolatileQualificationCertQuery extends RemoveConstOrVolatileQualificationSharedQuery { +class RemoveConstOrVolatileQualificationCertQuery extends RemoveConstOrVolatileQualificationSharedQuery +{ RemoveConstOrVolatileQualificationCertQuery() { this = ConstPackage::removeConstOrVolatileQualificationCertQuery() } diff --git a/cpp/cert/src/rules/EXP56-CPP/FunctionWithMismatchedLanguageLinkage.ql b/cpp/cert/src/rules/EXP56-CPP/FunctionWithMismatchedLanguageLinkage.ql index 23efb87e0b..d8460c58fa 100644 --- a/cpp/cert/src/rules/EXP56-CPP/FunctionWithMismatchedLanguageLinkage.ql +++ b/cpp/cert/src/rules/EXP56-CPP/FunctionWithMismatchedLanguageLinkage.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/exp56-cpp * 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/cpp/cert/src/rules/EXP57-CPP/CastOfPointerToIncompleteClass.ql b/cpp/cert/src/rules/EXP57-CPP/CastOfPointerToIncompleteClass.ql index 4358f11b34..2a8345c05d 100644 --- a/cpp/cert/src/rules/EXP57-CPP/CastOfPointerToIncompleteClass.ql +++ b/cpp/cert/src/rules/EXP57-CPP/CastOfPointerToIncompleteClass.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/exp57-cpp * 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/cpp/cert/src/rules/EXP57-CPP/DeletingPointerToIncompleteClass.ql b/cpp/cert/src/rules/EXP57-CPP/DeletingPointerToIncompleteClass.ql index 8534885c9e..935218f78e 100644 --- a/cpp/cert/src/rules/EXP57-CPP/DeletingPointerToIncompleteClass.ql +++ b/cpp/cert/src/rules/EXP57-CPP/DeletingPointerToIncompleteClass.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/exp57-cpp * 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/cpp/cert/src/rules/EXP58-CPP/PassNonTrivialObjectToVaStart.ql b/cpp/cert/src/rules/EXP58-CPP/PassNonTrivialObjectToVaStart.ql index 5c7ef31a6f..b537fa34c5 100644 --- a/cpp/cert/src/rules/EXP58-CPP/PassNonTrivialObjectToVaStart.ql +++ b/cpp/cert/src/rules/EXP58-CPP/PassNonTrivialObjectToVaStart.ql @@ -9,6 +9,11 @@ * @problem.severity warning * @tags external/cert/id/exp58-cpp * 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/cpp/cert/src/rules/EXP58-CPP/PassPromotablePrimitiveTypeToVaStart.ql b/cpp/cert/src/rules/EXP58-CPP/PassPromotablePrimitiveTypeToVaStart.ql index dab95c8303..1d34680261 100644 --- a/cpp/cert/src/rules/EXP58-CPP/PassPromotablePrimitiveTypeToVaStart.ql +++ b/cpp/cert/src/rules/EXP58-CPP/PassPromotablePrimitiveTypeToVaStart.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/exp58-cpp * 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/cpp/cert/src/rules/EXP58-CPP/PassReferenceTypeToVaStart.ql b/cpp/cert/src/rules/EXP58-CPP/PassReferenceTypeToVaStart.ql index 0b9e0a9f99..ce340d63c8 100644 --- a/cpp/cert/src/rules/EXP58-CPP/PassReferenceTypeToVaStart.ql +++ b/cpp/cert/src/rules/EXP58-CPP/PassReferenceTypeToVaStart.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/exp58-cpp * 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/cpp/cert/src/rules/EXP59-CPP/OffsetUsedOnInvalidTypeOrMember.ql b/cpp/cert/src/rules/EXP59-CPP/OffsetUsedOnInvalidTypeOrMember.ql index 8cda1c0851..7ece8faef6 100644 --- a/cpp/cert/src/rules/EXP59-CPP/OffsetUsedOnInvalidTypeOrMember.ql +++ b/cpp/cert/src/rules/EXP59-CPP/OffsetUsedOnInvalidTypeOrMember.ql @@ -7,6 +7,11 @@ * @problem.severity recommendation * @tags external/cert/id/exp59-cpp * 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/cpp/cert/src/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.ql b/cpp/cert/src/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.ql index 8442e5eda1..ddd6fa0efc 100644 --- a/cpp/cert/src/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.ql +++ b/cpp/cert/src/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/exp60-cpp * 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 */ diff --git a/cpp/cert/src/rules/EXP61-CPP/EscapingLambdaObjectWithCaptureByReference.ql b/cpp/cert/src/rules/EXP61-CPP/EscapingLambdaObjectWithCaptureByReference.ql index 55ffa19c09..1268d1c82b 100644 --- a/cpp/cert/src/rules/EXP61-CPP/EscapingLambdaObjectWithCaptureByReference.ql +++ b/cpp/cert/src/rules/EXP61-CPP/EscapingLambdaObjectWithCaptureByReference.ql @@ -10,6 +10,11 @@ * @tags external/cert/id/exp61-cpp * 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 */ @@ -17,7 +22,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.danglingcapturewhenmovinglambdaobject.DanglingCaptureWhenMovingLambdaObject -class MovedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenMovingLambdaObjectSharedQuery { +class MovedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenMovingLambdaObjectSharedQuery +{ MovedLambdaObjectOutlivesCaptureByReferenceQuery() { this = LambdasPackage::escapingLambdaObjectWithCaptureByReferenceQuery() } diff --git a/cpp/cert/src/rules/EXP61-CPP/ReturningLambdaObjectWithCaptureByReference.ql b/cpp/cert/src/rules/EXP61-CPP/ReturningLambdaObjectWithCaptureByReference.ql index 4ca210ce97..eb76ba6187 100644 --- a/cpp/cert/src/rules/EXP61-CPP/ReturningLambdaObjectWithCaptureByReference.ql +++ b/cpp/cert/src/rules/EXP61-CPP/ReturningLambdaObjectWithCaptureByReference.ql @@ -10,6 +10,11 @@ * @tags external/cert/id/exp61-cpp * 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 */ @@ -17,7 +22,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.danglingcapturewhenreturninglambdaobject.DanglingCaptureWhenReturningLambdaObject -class ReturnedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenReturningLambdaObjectSharedQuery { +class ReturnedLambdaObjectOutlivesCaptureByReferenceQuery extends DanglingCaptureWhenReturningLambdaObjectSharedQuery +{ ReturnedLambdaObjectOutlivesCaptureByReferenceQuery() { this = LambdasPackage::returningLambdaObjectWithCaptureByReferenceQuery() } diff --git a/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.md b/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.md index c5ecafae1a..165436a126 100644 --- a/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.md +++ b/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.md @@ -136,7 +136,7 @@ The effects of accessing bits of an object representation that are not part of t ## Automated Detection -
Tool Version Checker Description
Astrée 20.10 invalid_pointer_dereferenceuninitialized_variable_use
CodeSonar 7.0p0 BADFUNC.MEMCMP BADFUNC.MEMSET Use of memcmp Use of memset
Helix QAC 2022.2 C++4726, C++4727, C++4728, C++4729, C++4731, C++4732, C++4733, C++4734
LDRA tool suite 618 S Partially implemented
Parasoft C/C++test 2022.1 CERT_CPP-EXP62-a Do not compare objects of a class that may contain padding bits with C standard library functions
PVS-Studio 7.19 V598 , V780, V1084
+
Tool Version Checker Description
Astrée 22.10 invalid_pointer_dereferenceuninitialized_variable_use
CodeSonar 7.2p0 BADFUNC.MEMCMP BADFUNC.MEMSET Use of memcmp Use of memset
Helix QAC 2022.4 DF4726, DF4727, DF4728, DF4729, DF4731, DF4732, DF4733, DF4734
Klocwork 2022.4 CERT.MEMCMP.PADDED_DATA CWARN.MEM.NONPOD
LDRA tool suite 618 S Partially implemented
Parasoft C/C++test 2022.2 CERT_CPP-EXP62-a Do not compare objects of a class that may contain padding bits with C standard library functions
Polyspace Bug Finder R2023a CERT C++: EXP62-CPP Checks for access attempts on padding and vtable bits (rule fully covered).
PVS-Studio 7.23 V598 , V780, V1084
## Related Vulnerabilities diff --git a/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.ql b/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.ql index c57a5f2f9b..64bfb4673b 100644 --- a/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.ql +++ b/cpp/cert/src/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.ql @@ -8,23 +8,20 @@ * @problem.severity error * @tags external/cert/id/exp62-cpp * 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.cpp.cert -import semmle.code.cpp.padding.Padding -import semmle.code.cpp.security.BufferAccess -import VirtualTable +import codingstandards.cpp.rules.memcmpusedtocomparepaddingdata.MemcmpUsedToComparePaddingData -from MemcmpBA cmp -where - not isExcluded(cmp, RepresentationPackage::memcmpUsedToAccessObjectRepresentationQuery()) and - cmp.getBuffer(_, _) - .getUnconverted() - .getUnspecifiedType() - .(PointerType) - .getBaseType() - .getUnspecifiedType() instanceof PaddedType -select cmp, - cmp.getName() + " accesses bits which are not part of the object's value representation." +class MemcmpUsedToAccessObjectRepresentationQuery extends MemcmpUsedToComparePaddingDataSharedQuery { + MemcmpUsedToAccessObjectRepresentationQuery() { + this = RepresentationPackage::memcmpUsedToAccessObjectRepresentationQuery() + } +} diff --git a/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.md b/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.md index 1dd80f651c..3301a7eacb 100644 --- a/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.md +++ b/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.md @@ -136,7 +136,7 @@ The effects of accessing bits of an object representation that are not part of t ## Automated Detection -
Tool Version Checker Description
Astrée 20.10 invalid_pointer_dereferenceuninitialized_variable_use
CodeSonar 7.0p0 BADFUNC.MEMCMP BADFUNC.MEMSET Use of memcmp Use of memset
Helix QAC 2022.2 C++4726, C++4727, C++4728, C++4729, C++4731, C++4732, C++4733, C++4734
LDRA tool suite 618 S Partially implemented
Parasoft C/C++test 2022.1 CERT_CPP-EXP62-a Do not compare objects of a class that may contain padding bits with C standard library functions
PVS-Studio 7.19 V598 , V780, V1084
+
Tool Version Checker Description
Astrée 22.10 invalid_pointer_dereferenceuninitialized_variable_use
CodeSonar 7.2p0 BADFUNC.MEMCMP BADFUNC.MEMSET Use of memcmp Use of memset
Helix QAC 2022.4 DF4726, DF4727, DF4728, DF4729, DF4731, DF4732, DF4733, DF4734
Klocwork 2022.4 CERT.MEMCMP.PADDED_DATA CWARN.MEM.NONPOD
LDRA tool suite 618 S Partially implemented
Parasoft C/C++test 2022.2 CERT_CPP-EXP62-a Do not compare objects of a class that may contain padding bits with C standard library functions
Polyspace Bug Finder R2023a CERT C++: EXP62-CPP Checks for access attempts on padding and vtable bits (rule fully covered).
PVS-Studio 7.23 V598 , V780, V1084
## Related Vulnerabilities diff --git a/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.ql b/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.ql index 87f797bf25..0e8847257c 100644 --- a/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.ql +++ b/cpp/cert/src/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/exp62-cpp * 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 */ diff --git a/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.md b/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.md index 5f37a5f449..77874d3110 100644 --- a/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.md +++ b/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.md @@ -136,7 +136,7 @@ The effects of accessing bits of an object representation that are not part of t ## Automated Detection -
Tool Version Checker Description
Astrée 20.10 invalid_pointer_dereferenceuninitialized_variable_use
CodeSonar 7.0p0 BADFUNC.MEMCMP BADFUNC.MEMSET Use of memcmp Use of memset
Helix QAC 2022.2 C++4726, C++4727, C++4728, C++4729, C++4731, C++4732, C++4733, C++4734
LDRA tool suite 618 S Partially implemented
Parasoft C/C++test 2022.1 CERT_CPP-EXP62-a Do not compare objects of a class that may contain padding bits with C standard library functions
PVS-Studio 7.19 V598 , V780, V1084
+
Tool Version Checker Description
Astrée 22.10 invalid_pointer_dereferenceuninitialized_variable_use
CodeSonar 7.2p0 BADFUNC.MEMCMP BADFUNC.MEMSET Use of memcmp Use of memset
Helix QAC 2022.4 DF4726, DF4727, DF4728, DF4729, DF4731, DF4732, DF4733, DF4734
Klocwork 2022.4 CERT.MEMCMP.PADDED_DATA CWARN.MEM.NONPOD
LDRA tool suite 618 S Partially implemented
Parasoft C/C++test 2022.2 CERT_CPP-EXP62-a Do not compare objects of a class that may contain padding bits with C standard library functions
Polyspace Bug Finder R2023a CERT C++: EXP62-CPP Checks for access attempts on padding and vtable bits (rule fully covered).
PVS-Studio 7.23 V598 , V780, V1084
## Related Vulnerabilities diff --git a/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.ql b/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.ql index 302410def1..a4ae635289 100644 --- a/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.ql +++ b/cpp/cert/src/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/exp62-cpp * 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 */ diff --git a/cpp/cert/src/rules/EXP63-CPP/DoNotRelyOnTheValueOfAMovedFromObject.ql b/cpp/cert/src/rules/EXP63-CPP/DoNotRelyOnTheValueOfAMovedFromObject.ql index 8f988827ce..48e534bfbb 100644 --- a/cpp/cert/src/rules/EXP63-CPP/DoNotRelyOnTheValueOfAMovedFromObject.ql +++ b/cpp/cert/src/rules/EXP63-CPP/DoNotRelyOnTheValueOfAMovedFromObject.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/exp63-cpp * 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 */ @@ -14,7 +19,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.movedfromobjectsunspecifiedstate.MovedFromObjectsUnspecifiedState -class DoNotRelyOnTheValueOfAMovedFromObjectQuery extends MovedFromObjectsUnspecifiedStateSharedQuery { +class DoNotRelyOnTheValueOfAMovedFromObjectQuery extends MovedFromObjectsUnspecifiedStateSharedQuery +{ DoNotRelyOnTheValueOfAMovedFromObjectQuery() { this = MoveForwardPackage::doNotRelyOnTheValueOfAMovedFromObjectQuery() } diff --git a/cpp/cert/src/rules/FIO50-CPP/InterleavedInputOutputWithoutPosition.ql b/cpp/cert/src/rules/FIO50-CPP/InterleavedInputOutputWithoutPosition.ql index e30168dc23..0333955f72 100644 --- a/cpp/cert/src/rules/FIO50-CPP/InterleavedInputOutputWithoutPosition.ql +++ b/cpp/cert/src/rules/FIO50-CPP/InterleavedInputOutputWithoutPosition.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/fio50-cpp * 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/cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql b/cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql index 8736348682..a444692594 100644 --- a/cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql +++ b/cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/fio51-cpp * 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 */ @@ -38,8 +43,10 @@ predicate filebufAccess(ControlFlowNode node, FileStreamSource fss) { //insertion or extraction operator calls node.(InsertionOperatorCall).getFStream() = fss.getAUse() or node.(ExtractionOperatorCall).getFStream() = fss.getAUse() or - //methods inherited from istream or ostream - node.(IOStreamFunctionCall).getFStream() = fss.getAUse() + // Methods inherited from istream or ostream that access the file stream. + // Exclude is_open as it is not a filebuf access + any(IOStreamFunctionCall call | node = call and not call.getTarget().hasName("is_open")) + .getFStream() = fss.getAUse() } /** diff --git a/cpp/cert/src/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.ql b/cpp/cert/src/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.ql index f90d3a42ef..c7437073e9 100644 --- a/cpp/cert/src/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.ql +++ b/cpp/cert/src/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/int50-cpp * 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/cpp/cert/src/rules/MEM50-CPP/UseAfterFree.ql b/cpp/cert/src/rules/MEM50-CPP/UseAfterFree.ql index 59bf3e5bc7..8c31fc104c 100644 --- a/cpp/cert/src/rules/MEM50-CPP/UseAfterFree.ql +++ b/cpp/cert/src/rules/MEM50-CPP/UseAfterFree.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/mem50-cpp * 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/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql b/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql index 0cc7779a46..70fd363c64 100644 --- a/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql +++ b/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/mem51-cpp * 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 */ @@ -26,7 +31,7 @@ predicate matching(string allocKind, string deleteKind) { from Expr alloc, Expr free, Expr freed, string allocKind, string deleteKind where - not isExcluded(freed, FreedPackage::newDeleteArrayMismatchQuery()) and + not isExcluded(freed, AllocationsPackage::properlyDeallocateDynamicallyAllocatedResourcesQuery()) and allocReaches(freed, alloc, allocKind) and freeExprOrIndirect(free, freed, deleteKind) and not matching(allocKind, deleteKind) diff --git a/cpp/cert/src/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.ql b/cpp/cert/src/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.ql index ce5a608489..90685f1c96 100644 --- a/cpp/cert/src/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.ql +++ b/cpp/cert/src/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/mem52-cpp * 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 */ @@ -74,22 +79,20 @@ class NotWrappedNoThrowAllocExpr extends NoThrowAllocExpr { /** * A data flow configuration for finding nothrow allocation calls which are checked in some kind of guard. */ -class NoThrowNewErrorCheckConfig extends DataFlow::Configuration { - NoThrowNewErrorCheckConfig() { this = "NoThrowNewErrorCheckConfig" } - - override predicate isSource(DataFlow::Node source) { +module NoThrowNewErrorCheckConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NotWrappedNoThrowAllocExpr } - override predicate isSink(DataFlow::Node sink) { - sink.asExpr() = any(GuardCondition gc).getAChild*() - } + predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(GuardCondition gc).getAChild*() } } +module NoThrowNewErrorCheckFlow = DataFlow::Global; + from NotWrappedNoThrowAllocExpr ae where not isExcluded(ae, AllocationsPackage::detectAndHandleMemoryAllocationErrorsQuery()) and - not any(NoThrowNewErrorCheckConfig nt).hasFlow(DataFlow::exprNode(ae), _) + not NoThrowNewErrorCheckFlow::flow(DataFlow::exprNode(ae), _) select ae, "nothrow new allocation of $@ returns here without a subsequent check to see whether the pointer is valid.", ae.getUnderlyingAlloc() as underlying, underlying.getType().getName() diff --git a/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll b/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll index 0fbca43041..0eaf9f8dfa 100644 --- a/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll +++ b/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll @@ -1,9 +1,8 @@ import codingstandards.cpp.cert import codingstandards.cpp.Conversion -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import ManuallyManagedLifetime import semmle.code.cpp.controlflow.Dominance -import semmle.code.cpp.dataflow.DataFlow2 import semmle.code.cpp.dataflow.TaintTracking /** @@ -11,19 +10,20 @@ import semmle.code.cpp.dataflow.TaintTracking * * We use a taint-tracking configuration because we also want to track sub-sections */ -class AllocToStaticCastConfig extends TaintTracking::Configuration { - AllocToStaticCastConfig() { this = "AllocToStaticCastConfig" } - - override predicate isSource(DataFlow::Node source) { +module AllocToStaticCastConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(AllocationExpr ae | ae.getType().getUnspecifiedType() instanceof VoidPointerType and - source.asExpr() = ae and - // Ignore realloc, as that memory may already be partially constructed - not ae.(FunctionCall).getTarget().getName().toLowerCase().matches("%realloc%") + source.asExpr() = ae ) } - override predicate isSink(DataFlow::Node sink) { + predicate isBarrier(DataFlow::Node sanitizer) { + // Ignore realloc, as that memory may already be partially constructed + sanitizer.asExpr().(FunctionCall).getTarget().getName().toLowerCase().matches("%realloc%") + } + + predicate isSink(DataFlow::Node sink) { exists(StaticOrCStyleCast sc, Class nonTrivialClass | sc.getExpr() = sink.asExpr() and nonTrivialClass = sc.getType().getUnspecifiedType().(PointerType).getBaseType() and @@ -32,12 +32,14 @@ class AllocToStaticCastConfig extends TaintTracking::Configuration { } } +module AllocToStaticCastFlow = TaintTracking::Global; + /** * A cast of some existing memory, where we believe the resulting pointer has not been properly * constructed. */ class CastWithoutConstruction extends StaticOrCStyleCast { - CastWithoutConstruction() { any(AllocToStaticCastConfig c).hasFlowToExpr(getExpr()) } + CastWithoutConstruction() { AllocToStaticCastFlow::flowToExpr(getExpr()) } } /* @@ -96,18 +98,16 @@ class NonDestructingDeallocationCall extends Expr { * A data flow configuration from a `CastWithoutConstruction` to a free call on the memory without * an intervening destructor invocation. */ -class FreeWithoutDestructorConfig extends DataFlow2::Configuration { - FreeWithoutDestructorConfig() { this = "FreeWithoutDestructorConfig" } - - override predicate isSource(DataFlow::Node source) { +module FreeWithoutDestructorConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() = any(CastWithoutConstruction c).getExpr() } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(NonDestructingDeallocationCall de).getFreedExpr() } - override predicate isBarrier(DataFlow::Node barrier) { + predicate isBarrier(DataFlow::Node barrier) { // Consider any expression which later has a destructor called upon it to be safe. exists(DirectOrIndirectDestructorCall dc | DataFlow::localFlow(barrier, DataFlow::exprNode(dc.getDestructedArgument())) @@ -122,7 +122,9 @@ class FreeWithoutDestructorConfig extends DataFlow2::Configuration { ) } - override predicate isAdditionalFlowStep(DataFlow::Node stepFrom, DataFlow::Node stepTo) { + predicate isAdditionalFlowStep(DataFlow::Node stepFrom, DataFlow::Node stepTo) { stepTo.asExpr().(StaticOrCStyleCast).getExpr() = stepFrom.asExpr() } } + +module FreeWithoutDestructorFlow = DataFlow::Global; diff --git a/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql b/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql index 2bcb9be07c..a56fa18da8 100644 --- a/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql +++ b/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql @@ -7,24 +7,29 @@ * @problem.severity error * @tags external/cert/id/mem53-cpp * 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.cpp.cert -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import ManuallyManagedLifetime import semmle.code.cpp.dataflow.TaintTracking -import DataFlow::PathGraph +import AllocToStaticCastFlow::PathGraph /* * Find flow from a manual allocation returning void* to a static_cast (or c-style cast) * to a specific type. */ -from AllocToStaticCastConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +from AllocToStaticCastFlow::PathNode source, AllocToStaticCastFlow::PathNode sink where not isExcluded(sink.getNode().asExpr(), AllocationsPackage::missingConstructorCallForManuallyManagedObjectQuery()) and - config.hasFlowPath(source, sink) + AllocToStaticCastFlow::flowPath(source, sink) select sink.getNode(), source, sink, "Allocation to cast without constructor call" diff --git a/cpp/cert/src/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.ql b/cpp/cert/src/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.ql index b96f284ad3..fe6fff2d4f 100644 --- a/cpp/cert/src/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.ql +++ b/cpp/cert/src/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.ql @@ -7,18 +7,23 @@ * @problem.severity error * @tags external/cert/id/mem53-cpp * 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.cpp.cert import ManuallyManagedLifetime -import semmle.code.cpp.dataflow.DataFlow2 -import DataFlow2::PathGraph +import semmle.code.cpp.dataflow.DataFlow +import FreeWithoutDestructorFlow::PathGraph -from FreeWithoutDestructorConfig dc, DataFlow2::PathNode source, DataFlow2::PathNode sink +from FreeWithoutDestructorFlow::PathNode source, FreeWithoutDestructorFlow::PathNode sink where not isExcluded(sink.getNode().asExpr(), AllocationsPackage::missingDestructorCallForManuallyManagedObjectQuery()) and - dc.hasFlowPath(source, sink) + FreeWithoutDestructorFlow::flowPath(source, sink) select sink.getNode(), source, sink, "Memory freed without an appropriate destructor called." diff --git a/cpp/cert/src/rules/MEM54-CPP/PlacementNewInsufficientStorageCert.ql b/cpp/cert/src/rules/MEM54-CPP/PlacementNewInsufficientStorageCert.ql index 695d39de69..fca9190552 100644 --- a/cpp/cert/src/rules/MEM54-CPP/PlacementNewInsufficientStorageCert.ql +++ b/cpp/cert/src/rules/MEM54-CPP/PlacementNewInsufficientStorageCert.ql @@ -10,6 +10,11 @@ * @tags external/cert/id/mem54-cpp * security * 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 */ diff --git a/cpp/cert/src/rules/MEM54-CPP/PlacementNewNotProperlyAlignedCert.ql b/cpp/cert/src/rules/MEM54-CPP/PlacementNewNotProperlyAlignedCert.ql index 4993de85ed..d623e85a50 100644 --- a/cpp/cert/src/rules/MEM54-CPP/PlacementNewNotProperlyAlignedCert.ql +++ b/cpp/cert/src/rules/MEM54-CPP/PlacementNewNotProperlyAlignedCert.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/mem54-cpp * security * 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 */ diff --git a/cpp/cert/src/rules/MEM55-CPP/OperatorDeleteMissingPartnerCert.ql b/cpp/cert/src/rules/MEM55-CPP/OperatorDeleteMissingPartnerCert.ql index d3366f15fc..fd8f4f3a04 100644 --- a/cpp/cert/src/rules/MEM55-CPP/OperatorDeleteMissingPartnerCert.ql +++ b/cpp/cert/src/rules/MEM55-CPP/OperatorDeleteMissingPartnerCert.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/mem55-cpp * 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 */ diff --git a/cpp/cert/src/rules/MEM55-CPP/ThrowingNoThrowOperatorNewDeleteCert.ql b/cpp/cert/src/rules/MEM55-CPP/ThrowingNoThrowOperatorNewDeleteCert.ql index 564d74c333..2740498eef 100644 --- a/cpp/cert/src/rules/MEM55-CPP/ThrowingNoThrowOperatorNewDeleteCert.ql +++ b/cpp/cert/src/rules/MEM55-CPP/ThrowingNoThrowOperatorNewDeleteCert.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/mem55-cpp * 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 */ diff --git a/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewReturnsNullCert.ql b/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewReturnsNullCert.ql index c07dbff76c..072c69201f 100644 --- a/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewReturnsNullCert.ql +++ b/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewReturnsNullCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/mem55-cpp * 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 */ diff --git a/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewThrowsInvalidExceptionCert.ql b/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewThrowsInvalidExceptionCert.ql index 55affc59d6..da4b63200b 100644 --- a/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewThrowsInvalidExceptionCert.ql +++ b/cpp/cert/src/rules/MEM55-CPP/ThrowingOperatorNewThrowsInvalidExceptionCert.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/mem55-cpp * 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 */ @@ -16,7 +21,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.throwingoperatornewthrowsinvalidexception.ThrowingOperatorNewThrowsInvalidException -class ThrowingOperatorNewThrowsInvalidExceptionCertQuery extends ThrowingOperatorNewThrowsInvalidExceptionSharedQuery { +class ThrowingOperatorNewThrowsInvalidExceptionCertQuery extends ThrowingOperatorNewThrowsInvalidExceptionSharedQuery +{ ThrowingOperatorNewThrowsInvalidExceptionCertQuery() { this = AllocationsPackage::throwingOperatorNewThrowsInvalidExceptionCertQuery() } diff --git a/cpp/cert/src/rules/MEM56-CPP/OwnedPointerValueStoredInUnrelatedSmartPointerCert.ql b/cpp/cert/src/rules/MEM56-CPP/OwnedPointerValueStoredInUnrelatedSmartPointerCert.ql index a7035dac81..ba7a39272a 100644 --- a/cpp/cert/src/rules/MEM56-CPP/OwnedPointerValueStoredInUnrelatedSmartPointerCert.ql +++ b/cpp/cert/src/rules/MEM56-CPP/OwnedPointerValueStoredInUnrelatedSmartPointerCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/mem56-cpp * 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,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.ownedpointervaluestoredinunrelatedsmartpointer.OwnedPointerValueStoredInUnrelatedSmartPointer -class OwnedPointerValueStoredInUnrelatedSmartPointerCertQuery extends OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery { +class OwnedPointerValueStoredInUnrelatedSmartPointerCertQuery extends OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery +{ OwnedPointerValueStoredInUnrelatedSmartPointerCertQuery() { this = SmartPointers2Package::ownedPointerValueStoredInUnrelatedSmartPointerCertQuery() } diff --git a/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql b/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql index d0210791d4..6c3d18c27f 100644 --- a/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql +++ b/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql @@ -9,31 +9,18 @@ * @tags external/cert/id/mem57-cpp * correctness * security + * external/cert/severity/medium + * external/cert/likelihood/unlikely + * external/cert/remediation-cost/low + * external/cert/priority/p6 + * external/cert/level/l2 * external/cert/obligation/rule */ import cpp import codingstandards.cpp.cert - -/* - * In theory each compilation of each file can have a different `max_align_t` value (for example, - * if the same file is compiled under different compilers in the same database). We don't have the - * fine-grained data to determine which compilation each operator new call is from, so we instead - * report only in cases where there's a single clear alignment for the whole database. - */ - -class MaxAlignT extends TypedefType { - MaxAlignT() { getName() = "max_align_t" } -} - -/** - * Gets the alignment for `max_align_t`, assuming there is a single consistent alignment for the - * database. - */ -int getGlobalMaxAlignT() { - count(MaxAlignT m | | m.getAlignment()) = 1 and - result = any(MaxAlignT t).getAlignment() -} +import codingstandards.cpp.Alignment +import codingstandards.cpp.Allocations from NewOrNewArrayExpr newExpr, Type overAlignedType where diff --git a/cpp/cert/src/rules/MSC50-CPP/DoNotUseRandForGeneratingPseudorandomNumbers.ql b/cpp/cert/src/rules/MSC50-CPP/DoNotUseRandForGeneratingPseudorandomNumbers.ql index bf58920b84..b67cec99f3 100644 --- a/cpp/cert/src/rules/MSC50-CPP/DoNotUseRandForGeneratingPseudorandomNumbers.ql +++ b/cpp/cert/src/rules/MSC50-CPP/DoNotUseRandForGeneratingPseudorandomNumbers.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/msc50-cpp * security * scope/single-translation-unit + * external/cert/severity/medium + * external/cert/likelihood/unlikely + * external/cert/remediation-cost/low + * external/cert/priority/p6 + * external/cert/level/l2 * external/cert/obligation/rule */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.donotuserandforgeneratingpseudorandomnumbers.DoNotUseRandForGeneratingPseudorandomNumbers -class DoNotUseRandForGeneratingPseudorandomNumbersQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery { +class DoNotUseRandForGeneratingPseudorandomNumbersQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery +{ DoNotUseRandForGeneratingPseudorandomNumbersQuery() { this = BannedFunctionsPackage::doNotUseRandForGeneratingPseudorandomNumbersQuery() } diff --git a/cpp/cert/src/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.ql b/cpp/cert/src/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.ql index 743ca43ea4..5322fbbde3 100644 --- a/cpp/cert/src/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.ql +++ b/cpp/cert/src/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/msc51-cpp * security * correctness + * external/cert/severity/medium + * external/cert/likelihood/likely + * external/cert/remediation-cost/low + * external/cert/priority/p18 + * external/cert/level/l1 * external/cert/obligation/rule */ @@ -33,7 +38,7 @@ where or // Initialized from a poor source of randomness exists(Call c | - c.getTarget().hasQualifiedName("std", "time") and + c.getTarget().hasGlobalOrStdName("time") and TaintTracking::localExprTaint(c, createRandomNumberEngine.getSeedArgument()) and seedSource = "initialized from std::time" ) diff --git a/cpp/cert/src/rules/MSC52-CPP/NonVoidFunctionDoesNotReturnCert.ql b/cpp/cert/src/rules/MSC52-CPP/NonVoidFunctionDoesNotReturnCert.ql index 9634592715..dcf42a78f4 100644 --- a/cpp/cert/src/rules/MSC52-CPP/NonVoidFunctionDoesNotReturnCert.ql +++ b/cpp/cert/src/rules/MSC52-CPP/NonVoidFunctionDoesNotReturnCert.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/msc52-cpp * 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/cpp/cert/src/rules/MSC53-CPP/FunctionNoReturnAttributeConditionCert.ql b/cpp/cert/src/rules/MSC53-CPP/FunctionNoReturnAttributeConditionCert.ql index 0f0863cc16..5044b3b421 100644 --- a/cpp/cert/src/rules/MSC53-CPP/FunctionNoReturnAttributeConditionCert.ql +++ b/cpp/cert/src/rules/MSC53-CPP/FunctionNoReturnAttributeConditionCert.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/msc53-cpp * correctness + * external/cert/severity/medium + * external/cert/likelihood/unlikely + * external/cert/remediation-cost/low + * external/cert/priority/p2 + * external/cert/level/l3 * external/cert/obligation/rule */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.functionnoreturnattributecondition.FunctionNoReturnAttributeCondition -class FuntionNoReturnAttributeConditionCertQuery extends FunctionNoReturnAttributeConditionSharedQuery { +class FuntionNoReturnAttributeConditionCertQuery extends FunctionNoReturnAttributeConditionSharedQuery +{ FuntionNoReturnAttributeConditionCertQuery() { this = FunctionsPackage::functionNoReturnAttributeConditionCertQuery() } diff --git a/cpp/cert/src/rules/MSC54-CPP/SignalHandlerMustBeAPlainOldFunction.ql b/cpp/cert/src/rules/MSC54-CPP/SignalHandlerMustBeAPlainOldFunction.ql index a537346630..885d8caa0a 100644 --- a/cpp/cert/src/rules/MSC54-CPP/SignalHandlerMustBeAPlainOldFunction.ql +++ b/cpp/cert/src/rules/MSC54-CPP/SignalHandlerMustBeAPlainOldFunction.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/msc54-cpp * 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 */ diff --git a/cpp/cert/src/rules/OOP50-CPP/DoNotInvokeVirtualFunctionsFromConstructorsOrDestructors.ql b/cpp/cert/src/rules/OOP50-CPP/DoNotInvokeVirtualFunctionsFromConstructorsOrDestructors.ql index 5cbcee6be9..1c3df97cfa 100644 --- a/cpp/cert/src/rules/OOP50-CPP/DoNotInvokeVirtualFunctionsFromConstructorsOrDestructors.ql +++ b/cpp/cert/src/rules/OOP50-CPP/DoNotInvokeVirtualFunctionsFromConstructorsOrDestructors.ql @@ -7,6 +7,11 @@ * @precision very-high * @problem.severity error * @tags external/cert/id/oop50-cpp + * 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/cpp/cert/src/rules/OOP51-CPP/DoNotSliceDerivedObjects.ql b/cpp/cert/src/rules/OOP51-CPP/DoNotSliceDerivedObjects.ql index f0af256fb9..4cb654730b 100644 --- a/cpp/cert/src/rules/OOP51-CPP/DoNotSliceDerivedObjects.ql +++ b/cpp/cert/src/rules/OOP51-CPP/DoNotSliceDerivedObjects.ql @@ -7,6 +7,11 @@ * @precision very-high * @problem.severity error * @tags external/cert/id/oop51-cpp + * 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/cpp/cert/src/rules/OOP52-CPP/DoNotDeleteAPolymorphicObjectWithoutAVirtualDestructor.ql b/cpp/cert/src/rules/OOP52-CPP/DoNotDeleteAPolymorphicObjectWithoutAVirtualDestructor.ql index 13bfdd5c0c..190c4d720d 100644 --- a/cpp/cert/src/rules/OOP52-CPP/DoNotDeleteAPolymorphicObjectWithoutAVirtualDestructor.ql +++ b/cpp/cert/src/rules/OOP52-CPP/DoNotDeleteAPolymorphicObjectWithoutAVirtualDestructor.ql @@ -7,6 +7,11 @@ * @precision very-high * @problem.severity warning * @tags external/cert/id/oop52-cpp + * 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/cpp/cert/src/rules/OOP53-CPP/UseCanonicalOrderForMemberInit.ql b/cpp/cert/src/rules/OOP53-CPP/UseCanonicalOrderForMemberInit.ql index 96fd7812d7..b42b54ef6c 100644 --- a/cpp/cert/src/rules/OOP53-CPP/UseCanonicalOrderForMemberInit.ql +++ b/cpp/cert/src/rules/OOP53-CPP/UseCanonicalOrderForMemberInit.ql @@ -12,6 +12,11 @@ * security * 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/cpp/cert/src/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.ql b/cpp/cert/src/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.ql index 85940bf862..844d0f54bb 100644 --- a/cpp/cert/src/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.ql +++ b/cpp/cert/src/rules/OOP54-CPP/GracefullyHandleSelfCopyAssignment.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/oop54-cpp * correctness + * 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 */ diff --git a/cpp/cert/src/rules/OOP55-CPP/MemberAccessWithUninitializedStaticPointerToMember.ql b/cpp/cert/src/rules/OOP55-CPP/MemberAccessWithUninitializedStaticPointerToMember.ql index d1fb58b549..27c63c2c16 100644 --- a/cpp/cert/src/rules/OOP55-CPP/MemberAccessWithUninitializedStaticPointerToMember.ql +++ b/cpp/cert/src/rules/OOP55-CPP/MemberAccessWithUninitializedStaticPointerToMember.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/oop55-cpp * 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 */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.accessofundefinedmemberthroughuninitializedstaticpointer.AccessOfUndefinedMemberThroughUninitializedStaticPointer -class MemberAccessWithUninitializedStaticPointerToMemberQuery extends AccessOfUndefinedMemberThroughUninitializedStaticPointerSharedQuery { +class MemberAccessWithUninitializedStaticPointerToMemberQuery extends AccessOfUndefinedMemberThroughUninitializedStaticPointerSharedQuery +{ MemberAccessWithUninitializedStaticPointerToMemberQuery() { this = PointersPackage::memberAccessWithUninitializedStaticPointerToMemberQuery() } diff --git a/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessNonexistentMember.ql b/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessNonexistentMember.ql index 6341dc49e4..72d640f29b 100644 --- a/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessNonexistentMember.ql +++ b/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessNonexistentMember.ql @@ -9,6 +9,11 @@ * @problem.severity error * @tags external/cert/id/oop55-cpp * 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 */ @@ -16,7 +21,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.accessofnonexistingmemberthroughpointertomember.AccessOfNonExistingMemberThroughPointerToMember -class UseOfPointerToMemberToAccessNonexistentMemberQuery extends AccessOfNonExistingMemberThroughPointerToMemberSharedQuery { +class UseOfPointerToMemberToAccessNonexistentMemberQuery extends AccessOfNonExistingMemberThroughPointerToMemberSharedQuery +{ UseOfPointerToMemberToAccessNonexistentMemberQuery() { this = PointersPackage::useOfPointerToMemberToAccessNonexistentMemberQuery() } diff --git a/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessUndefinedMember.ql b/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessUndefinedMember.ql index 3a5af3433e..202123c11c 100644 --- a/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessUndefinedMember.ql +++ b/cpp/cert/src/rules/OOP55-CPP/UseOfPointerToMemberToAccessUndefinedMember.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/oop55-cpp * 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 */ @@ -15,7 +20,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.accessofundefinedmemberthroughnullpointer.AccessOfUndefinedMemberThroughNullPointer -class UseOfPointerToMemberToAccessUndefinedMemberQuery extends AccessOfUndefinedMemberThroughNullPointerSharedQuery { +class UseOfPointerToMemberToAccessUndefinedMemberQuery extends AccessOfUndefinedMemberThroughNullPointerSharedQuery +{ UseOfPointerToMemberToAccessUndefinedMemberQuery() { this = PointersPackage::useOfPointerToMemberToAccessUndefinedMemberQuery() } diff --git a/cpp/cert/src/rules/OOP56-CPP/HonorNewReplacementHandlerRequirements.ql b/cpp/cert/src/rules/OOP56-CPP/HonorNewReplacementHandlerRequirements.ql index 18b259ef86..981bd1ce5b 100644 --- a/cpp/cert/src/rules/OOP56-CPP/HonorNewReplacementHandlerRequirements.ql +++ b/cpp/cert/src/rules/OOP56-CPP/HonorNewReplacementHandlerRequirements.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/oop56-cpp * correctness + * 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 */ diff --git a/cpp/cert/src/rules/OOP56-CPP/HonorTerminationReplacementHandlerRequirements.ql b/cpp/cert/src/rules/OOP56-CPP/HonorTerminationReplacementHandlerRequirements.ql index ea499791ff..4d59b36b52 100644 --- a/cpp/cert/src/rules/OOP56-CPP/HonorTerminationReplacementHandlerRequirements.ql +++ b/cpp/cert/src/rules/OOP56-CPP/HonorTerminationReplacementHandlerRequirements.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/oop56-cpp * correctness + * 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 */ diff --git a/cpp/cert/src/rules/OOP57-CPP/PreferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions.ql b/cpp/cert/src/rules/OOP57-CPP/PreferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions.ql index 19b14730bb..9ac17e84a0 100644 --- a/cpp/cert/src/rules/OOP57-CPP/PreferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions.ql +++ b/cpp/cert/src/rules/OOP57-CPP/PreferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions.ql @@ -8,6 +8,11 @@ * @tags external/cert/id/oop57-cpp * correctness * scope/single-translation-unit + * 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/cpp/cert/src/rules/OOP58-CPP/CopyOperationsMustNotMutateTheSourceObject.ql b/cpp/cert/src/rules/OOP58-CPP/CopyOperationsMustNotMutateTheSourceObject.ql index 97cfe0fa3c..9ad0593702 100644 --- a/cpp/cert/src/rules/OOP58-CPP/CopyOperationsMustNotMutateTheSourceObject.ql +++ b/cpp/cert/src/rules/OOP58-CPP/CopyOperationsMustNotMutateTheSourceObject.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/oop58-cpp * correctness + * 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/cpp/cert/src/rules/STR50-CPP/BasicStringMayNotBeNullTerminatedCert.ql b/cpp/cert/src/rules/STR50-CPP/BasicStringMayNotBeNullTerminatedCert.ql index d9787d2393..2cd08be70a 100644 --- a/cpp/cert/src/rules/STR50-CPP/BasicStringMayNotBeNullTerminatedCert.ql +++ b/cpp/cert/src/rules/STR50-CPP/BasicStringMayNotBeNullTerminatedCert.ql @@ -7,6 +7,11 @@ * @precision very-high * @problem.severity recommendation * @tags external/cert/id/str50-cpp + * 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 */ @@ -14,7 +19,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.basicstringmaynotbenullterminated.BasicStringMayNotBeNullTerminated -class BasicStringMayNotBeNullTerminatedCertQuery extends BasicStringMayNotBeNullTerminatedSharedQuery { +class BasicStringMayNotBeNullTerminatedCertQuery extends BasicStringMayNotBeNullTerminatedSharedQuery +{ BasicStringMayNotBeNullTerminatedCertQuery() { this = StringsPackage::basicStringMayNotBeNullTerminatedCertQuery() } diff --git a/cpp/cert/src/rules/STR50-CPP/OperationMayNotNullTerminateCStyleStringCert.ql b/cpp/cert/src/rules/STR50-CPP/OperationMayNotNullTerminateCStyleStringCert.ql index 2878c7b001..59f56207cd 100644 --- a/cpp/cert/src/rules/STR50-CPP/OperationMayNotNullTerminateCStyleStringCert.ql +++ b/cpp/cert/src/rules/STR50-CPP/OperationMayNotNullTerminateCStyleStringCert.ql @@ -7,6 +7,11 @@ * @precision very-high * @problem.severity recommendation * @tags external/cert/id/str50-cpp + * 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 */ @@ -14,7 +19,8 @@ import cpp import codingstandards.cpp.cert import codingstandards.cpp.rules.operationmaynotnullterminatecstylestring.OperationMayNotNullTerminateCStyleString -class OperationMayNotNullTerminateCStyleStringCertQuery extends OperationMayNotNullTerminateCStyleStringSharedQuery { +class OperationMayNotNullTerminateCStyleStringCertQuery extends OperationMayNotNullTerminateCStyleStringSharedQuery +{ OperationMayNotNullTerminateCStyleStringCertQuery() { this = StringsPackage::operationMayNotNullTerminateCStyleStringCertQuery() } diff --git a/cpp/cert/src/rules/STR51-CPP/DoNotAttemptToCreateAStringFromANullPointer.ql b/cpp/cert/src/rules/STR51-CPP/DoNotAttemptToCreateAStringFromANullPointer.ql index e775dc205f..a6337e2fcf 100644 --- a/cpp/cert/src/rules/STR51-CPP/DoNotAttemptToCreateAStringFromANullPointer.ql +++ b/cpp/cert/src/rules/STR51-CPP/DoNotAttemptToCreateAStringFromANullPointer.ql @@ -7,6 +7,11 @@ * @problem.severity error * @tags external/cert/id/str51-cpp * 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 */ diff --git a/cpp/cert/src/rules/STR52-CPP/UseValidReferencesForElementsOfString.ql b/cpp/cert/src/rules/STR52-CPP/UseValidReferencesForElementsOfString.ql index 211e490b33..21c29f54ef 100644 --- a/cpp/cert/src/rules/STR52-CPP/UseValidReferencesForElementsOfString.ql +++ b/cpp/cert/src/rules/STR52-CPP/UseValidReferencesForElementsOfString.ql @@ -8,6 +8,11 @@ * @problem.severity error * @tags external/cert/id/str52-cpp * 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 */ diff --git a/cpp/cert/src/rules/STR53-CPP/RangeCheckStringElementAccess.ql b/cpp/cert/src/rules/STR53-CPP/RangeCheckStringElementAccess.ql index 3300b77e18..c92f2b2316 100644 --- a/cpp/cert/src/rules/STR53-CPP/RangeCheckStringElementAccess.ql +++ b/cpp/cert/src/rules/STR53-CPP/RangeCheckStringElementAccess.ql @@ -9,6 +9,11 @@ * @tags external/cert/id/str53-cpp * 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/cpp/cert/test/codeql-pack.lock.yml b/cpp/cert/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/cert/test/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/cpp/cert/test/qlpack.yml b/cpp/cert/test/qlpack.yml index 0fdf27e6e8..8634569355 100644 --- a/cpp/cert/test/qlpack.yml +++ b/cpp/cert/test/qlpack.yml @@ -1,4 +1,6 @@ -name: cert-cpp-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: cert-cpp-coding-standards +name: codeql/cert-cpp-coding-standards-tests +version: 2.49.0-dev extractor: cpp +license: MIT +dependencies: + codeql/cert-cpp-coding-standards: '*' diff --git a/cpp/cert/test/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.expected b/cpp/cert/test/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.expected index 4e87d1436c..6be9fd55cc 100644 --- a/cpp/cert/test/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.expected +++ b/cpp/cert/test/rules/CTR52-CPP/GuaranteeGenericCppLibraryFunctionsDoNotOverflow.expected @@ -1,3 +1,12 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:93,7-15) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:93,27-35) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:94,9-17) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:98,9-17) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:98,29-37) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:99,11-19) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:109,35-43) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:110,11-19) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql:109,9-22) | test.cpp:8:42:8:46 | call to begin | Output iterator for $@ is not guaranteed to be large enough for the input iterator. | test.cpp:8:3:8:11 | call to copy | call to copy | | test.cpp:17:42:17:46 | call to begin | Output iterator for $@ is not guaranteed to be large enough for the input iterator. | test.cpp:17:3:17:11 | call to copy | call to copy | | test.cpp:55:42:55:46 | call to begin | Output iterator for $@ is not guaranteed to be large enough for the input iterator. | test.cpp:55:3:55:11 | call to copy | call to copy | diff --git a/cpp/cert/test/rules/CTR53-CPP/UseValidIteratorRanges.expected b/cpp/cert/test/rules/CTR53-CPP/UseValidIteratorRanges.expected index 61260a0579..d25d23185a 100644 --- a/cpp/cert/test/rules/CTR53-CPP/UseValidIteratorRanges.expected +++ b/cpp/cert/test/rules/CTR53-CPP/UseValidIteratorRanges.expected @@ -1,3 +1,9 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UseValidIteratorRanges.ql:29,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UseValidIteratorRanges.ql:29,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UseValidIteratorRanges.ql:30,7-15) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UseValidIteratorRanges.ql:36,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UseValidIteratorRanges.ql:36,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (UseValidIteratorRanges.ql:37,7-15) | test.cpp:7:3:7:15 | call to for_each | The $@ of iterator range function does not point to the end of an iterator. | test.cpp:7:28:7:32 | call to begin | argument | | test.cpp:7:3:7:15 | call to for_each | The $@ of iterator range function does not point to the start of an iterator. | test.cpp:7:19:7:21 | call to end | argument | | test.cpp:8:3:8:15 | call to for_each | The $@ of iterator range function does not point to the end of an iterator. | test.cpp:8:30:8:34 | call to begin | argument | diff --git a/cpp/cert/test/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.expected b/cpp/cert/test/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.expected index 06560517dd..db3b7358d8 100644 --- a/cpp/cert/test/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.expected +++ b/cpp/cert/test/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.expected @@ -1,4 +1,17 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:44,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:44,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:44,51-59) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:45,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:45,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:45,52-60) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:80,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:80,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUseAnAdditiveOperatorOnAnIterator.ql:81,7-15) | test.cpp:8:7:8:7 | i | Increment of iterator may overflow since its bounds are not checked. | | test.cpp:9:9:9:9 | i | Increment of iterator may overflow since its bounds are not checked. | | test.cpp:10:9:10:9 | i | Increment of iterator may overflow since its bounds are not checked. | -| test.cpp:27:31:27:31 | i | Increment of iterator may overflow since its bounds are not checked. | +| test.cpp:22:18:22:18 | i | Increment of iterator may overflow since its bounds are not checked. | +| test.cpp:28:31:28:31 | i | Increment of iterator may overflow since its bounds are not checked. | +| test.cpp:41:5:41:8 | end2 | Increment of iterator may overflow since its bounds are not checked. | +| test.cpp:53:42:53:42 | i | Increment of iterator may overflow since its bounds are not checked. | +| test.cpp:64:15:64:15 | i | Increment of iterator may overflow since its bounds are not checked. | diff --git a/cpp/cert/test/rules/CTR55-CPP/test.cpp b/cpp/cert/test/rules/CTR55-CPP/test.cpp index d80e8cfab9..e4d14ec25e 100644 --- a/cpp/cert/test/rules/CTR55-CPP/test.cpp +++ b/cpp/cert/test/rules/CTR55-CPP/test.cpp @@ -20,10 +20,47 @@ void f1(std::vector &v) { } for (auto i = v.begin(), l = (i + std::min(static_cast::size_type>(10), - v.size())); - i != l; ++i) { // COMPLIANT + v.size())); // NON_COMPLIANT - technically in the + // calculation + i != l; ++i) { // COMPLIANT } for (auto i = v.begin();; ++i) { // NON_COMPLIANT } +} + +void test_fp_reported_in_374(std::vector &v) { + { + auto end = v.end(); + for (auto i = v.begin(); i != end; ++i) { // COMPLIANT + } + } + + { + auto end2 = v.end(); + end2++; // NON_COMPLIANT + for (auto i = v.begin(); i != end2; + ++i) { // NON_COMPLIANT[FALSE_NEGATIVE] - case of invalidations to + // check before use expected to be less frequent, can model in + // future if need be + } + } +} + +void test(std::vector &v, std::vector &v2) { + { + auto end = v2.end(); + for (auto i = v.begin(); i != end; ++i) { // NON_COMPLIANT - wrong check + } + } +} + +void test2(std::vector &v) { + auto i = v.begin(); + while (1) { + auto i2 = ((i != v.end()) != 0); + if (!i2) + break; + (void)((++i)); // COMPLIANT[FALSE_POSITIVE] + } } \ No newline at end of file diff --git a/cpp/cert/test/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.expected b/cpp/cert/test/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.expected index 0ee15c65b5..51ef13412c 100644 --- a/cpp/cert/test/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.expected +++ b/cpp/cert/test/rules/CTR56-CPP/DoNotUsePointerArithmeticOnPolymorphicObjects.expected @@ -1,15 +1,19 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnPolymorphicObjects.ql:46,62-70) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnPolymorphicObjects.ql:47,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnPolymorphicObjects.ql:56,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotUsePointerArithmeticOnPolymorphicObjects.ql:62,3-11) edges -| test.cpp:15:19:15:21 | foo | test.cpp:16:24:16:26 | foo | -| test.cpp:15:19:15:21 | foo | test.cpp:16:51:16:53 | foo | -| test.cpp:27:19:27:21 | foo | test.cpp:29:18:29:20 | foo | -| test.cpp:40:12:40:19 | new | test.cpp:43:6:43:7 | l1 | -| test.cpp:40:12:40:19 | new | test.cpp:44:6:44:7 | l1 | -| test.cpp:42:12:42:14 | & ... | test.cpp:45:6:45:7 | l3 | -| test.cpp:42:12:42:14 | & ... | test.cpp:46:6:46:7 | l3 | -| test.cpp:43:6:43:7 | l1 | test.cpp:15:19:15:21 | foo | -| test.cpp:44:6:44:7 | l1 | test.cpp:27:19:27:21 | foo | -| test.cpp:45:6:45:7 | l3 | test.cpp:15:19:15:21 | foo | -| test.cpp:46:6:46:7 | l3 | test.cpp:27:19:27:21 | foo | +| test.cpp:15:19:15:21 | foo | test.cpp:16:24:16:26 | foo | provenance | | +| test.cpp:15:19:15:21 | foo | test.cpp:16:51:16:53 | foo | provenance | | +| test.cpp:27:19:27:21 | foo | test.cpp:29:18:29:20 | foo | provenance | | +| test.cpp:40:12:40:19 | new | test.cpp:43:6:43:7 | l1 | provenance | | +| test.cpp:40:12:40:19 | new | test.cpp:44:6:44:7 | l1 | provenance | | +| test.cpp:42:12:42:14 | & ... | test.cpp:45:6:45:7 | l3 | provenance | | +| test.cpp:42:12:42:14 | & ... | test.cpp:46:6:46:7 | l3 | provenance | | +| test.cpp:43:6:43:7 | l1 | test.cpp:15:19:15:21 | foo | provenance | | +| test.cpp:44:6:44:7 | l1 | test.cpp:27:19:27:21 | foo | provenance | | +| test.cpp:45:6:45:7 | l3 | test.cpp:15:19:15:21 | foo | provenance | | +| test.cpp:46:6:46:7 | l3 | test.cpp:27:19:27:21 | foo | provenance | | nodes | test.cpp:15:19:15:21 | foo | semmle.label | foo | | test.cpp:16:24:16:26 | foo | semmle.label | foo | diff --git a/cpp/cert/test/rules/DCL51-CPP/EnumeratorReusesReservedName.expected b/cpp/cert/test/rules/DCL51-CPP/EnumeratorReusesReservedName.expected index 63563899ba..fa45f86fec 100644 --- a/cpp/cert/test/rules/DCL51-CPP/EnumeratorReusesReservedName.expected +++ b/cpp/cert/test/rules/DCL51-CPP/EnumeratorReusesReservedName.expected @@ -1 +1 @@ -| test.cpp:9:3:9:9 | INT_MAX | The enumerator $@ reuses a reserved standard library name. | test.cpp:9:3:9:9 | INT_MAX | INT_MAX | +| test.cpp:10:3:10:9 | INT_MAX | The enumerator $@ reuses a reserved standard library name. | test.cpp:10:3:10:9 | INT_MAX | INT_MAX | diff --git a/cpp/cert/test/rules/DCL51-CPP/FunctionReusesReservedName.expected b/cpp/cert/test/rules/DCL51-CPP/FunctionReusesReservedName.expected index 7f6fbb1bce..97bbccbbd0 100644 --- a/cpp/cert/test/rules/DCL51-CPP/FunctionReusesReservedName.expected +++ b/cpp/cert/test/rules/DCL51-CPP/FunctionReusesReservedName.expected @@ -1 +1 @@ -| test.cpp:19:6:19:8 | min | The function $@ reuses a reserved standard library name. | test.cpp:19:6:19:8 | min | min | +| test.cpp:22:6:22:8 | min | The function $@ reuses a reserved standard library name. | test.cpp:22:6:22:8 | min | min | diff --git a/cpp/cert/test/rules/DCL51-CPP/ObjectReusesReservedName.expected b/cpp/cert/test/rules/DCL51-CPP/ObjectReusesReservedName.expected index 7fbb18a955..d1c0b8d60e 100644 --- a/cpp/cert/test/rules/DCL51-CPP/ObjectReusesReservedName.expected +++ b/cpp/cert/test/rules/DCL51-CPP/ObjectReusesReservedName.expected @@ -1 +1 @@ -| test.cpp:17:5:17:10 | tzname | The variable $@ reuses a reserved standard library name. | test.cpp:17:5:17:10 | tzname | tzname | +| test.cpp:19:5:19:10 | tzname | The variable $@ reuses a reserved standard library name. | test.cpp:19:5:19:10 | tzname | tzname | diff --git a/cpp/cert/test/rules/DCL51-CPP/RedefiningOfStandardLibraryName.expected b/cpp/cert/test/rules/DCL51-CPP/RedefiningOfStandardLibraryName.expected index 90285a4af7..fb01130c4d 100644 --- a/cpp/cert/test/rules/DCL51-CPP/RedefiningOfStandardLibraryName.expected +++ b/cpp/cert/test/rules/DCL51-CPP/RedefiningOfStandardLibraryName.expected @@ -1,3 +1,3 @@ -| test.cpp:5:1:5:14 | #undef INT_MAX | Redefinition of INT_MAX declared in a standard library header. | -| test.cpp:6:1:6:20 | #define SIZE_MAX 256 | Redefinition of SIZE_MAX declared in a standard library header. | -| test.cpp:36:1:37:9 | #define FD_SET(X) int _ ## X | Redefinition of FD_SET declared in a standard library header. | +| test.cpp:6:1:6:14 | #undef INT_MAX | Redefinition of INT_MAX declared in a standard library header. | +| test.cpp:7:1:7:20 | #define SIZE_MAX 256 | Redefinition of SIZE_MAX declared in a standard library header. | +| test.cpp:39:1:40:9 | #define FD_SET(X) int _ ## X | Redefinition of FD_SET declared in a standard library header. | diff --git a/cpp/cert/test/rules/DCL51-CPP/ReuseOfReservedIdentifier.expected b/cpp/cert/test/rules/DCL51-CPP/ReuseOfReservedIdentifier.expected index 9a119e037d..2d60df03b4 100644 --- a/cpp/cert/test/rules/DCL51-CPP/ReuseOfReservedIdentifier.expected +++ b/cpp/cert/test/rules/DCL51-CPP/ReuseOfReservedIdentifier.expected @@ -1,2 +1,2 @@ -| test.cpp:12:1:12:15 | #undef noreturn | Redefinition of $@ lexically identical to reserved attribute token. | test.cpp:12:1:12:15 | #undef noreturn | noreturn | -| test.cpp:13:1:13:17 | #define private 1 | Redefinition of $@ lexically identical to keyword. | test.cpp:13:1:13:17 | #define private 1 | private | +| test.cpp:13:1:13:15 | #undef noreturn | Redefinition of $@ lexically identical to reserved attribute token. | test.cpp:13:1:13:15 | #undef noreturn | noreturn | +| test.cpp:14:1:14:17 | #define private 1 | Redefinition of $@ lexically identical to keyword. | test.cpp:14:1:14:17 | #define private 1 | private | diff --git a/cpp/cert/test/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.expected b/cpp/cert/test/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.expected index 44beebe0d6..0d52226d5f 100644 --- a/cpp/cert/test/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.expected +++ b/cpp/cert/test/rules/DCL51-CPP/UseOfDoubleUnderscoreReservedPrefix.expected @@ -1,2 +1,2 @@ -| test.cpp:24:5:24:7 | __x | Name $@ uses the reserved prefix '__'. | test.cpp:24:5:24:7 | __x | __x | -| test.cpp:29:5:29:7 | __x | Name $@ uses the reserved prefix '__'. | test.cpp:29:5:29:7 | __x | __x | +| test.cpp:27:5:27:7 | __x | Name $@ uses the reserved prefix '__'. | test.cpp:27:5:27:7 | __x | __x | +| test.cpp:32:5:32:7 | __x | Name $@ uses the reserved prefix '__'. | test.cpp:32:5:32:7 | __x | __x | diff --git a/cpp/cert/test/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.expected b/cpp/cert/test/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.expected index 72fc44988d..96f3b1068e 100644 --- a/cpp/cert/test/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.expected +++ b/cpp/cert/test/rules/DCL51-CPP/UseOfReservedLiteralSuffixIdentifier.expected @@ -1 +1 @@ -| test.cpp:21:6:21:17 | operator ""x | Literal suffix identifier $@ does not start with an underscore. | test.cpp:21:6:21:17 | operator ""x | operator ""x | +| test.cpp:24:6:24:17 | operator ""x | Literal suffix identifier $@ does not start with an underscore. | test.cpp:24:6:24:17 | operator ""x | operator ""x | diff --git a/cpp/cert/test/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.expected b/cpp/cert/test/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.expected index 8701a91f08..544a26c996 100644 --- a/cpp/cert/test/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.expected +++ b/cpp/cert/test/rules/DCL51-CPP/UseOfSingleUnderscoreReservedPrefix.expected @@ -1,5 +1,5 @@ -| test.cpp:25:5:25:6 | _X | Name $@ uses the reserved prefix '_'. | test.cpp:25:5:25:6 | _X | _X | -| test.cpp:26:5:26:6 | _x | Name $@ uses the reserved prefix '_'. | test.cpp:26:5:26:6 | _x | _x | -| test.cpp:30:5:30:6 | _X | Name $@ uses the reserved prefix '_'. | test.cpp:30:5:30:6 | _X | _X | -| test.cpp:34:1:34:3 | _i | Name $@ uses the reserved prefix '_'. | test.cpp:34:1:34:3 | _i | _i | +| test.cpp:28:5:28:6 | _X | Name $@ uses the reserved prefix '_'. | test.cpp:28:5:28:6 | _X | _X | +| test.cpp:29:5:29:6 | _x | Name $@ uses the reserved prefix '_'. | test.cpp:29:5:29:6 | _x | _x | +| test.cpp:33:5:33:6 | _X | Name $@ uses the reserved prefix '_'. | test.cpp:33:5:33:6 | _X | _X | +| test.cpp:37:1:37:3 | _i | Name $@ uses the reserved prefix '_'. | test.cpp:37:1:37:3 | _i | _i | | test.h:2:1:2:15 | #define _TEST_H | Name $@ uses the reserved prefix '_'. | test.h:2:1:2:15 | #define _TEST_H | _TEST_H | diff --git a/cpp/cert/test/rules/DCL51-CPP/test.cpp b/cpp/cert/test/rules/DCL51-CPP/test.cpp index 2bfe811593..9248041b57 100644 --- a/cpp/cert/test/rules/DCL51-CPP/test.cpp +++ b/cpp/cert/test/rules/DCL51-CPP/test.cpp @@ -1,6 +1,7 @@ -#include - #include "test.h" +#include +#include +#include #undef INT_MAX // NON_COMPLIANT #define SIZE_MAX 256 // NON_COMPLIANT @@ -14,7 +15,9 @@ enum { // int NULL = 0; // NON_COMPLIANT, but not supported by compilers in practice +namespace ns { int tzname = 0; // NON_COMPLIANT +} void min() {} // NON_COMPLIANT @@ -35,4 +38,16 @@ F(i); // NON_COMPLIANT - user macro #define FD_SET(X) \ int _##X // NON_COMPLIANT - redefinition of standard library macro -FD_SET(j); // COMPLIANT - standard library macro \ No newline at end of file +FD_SET(j); // COMPLIANT - standard library macro + +void f() { + std::string x = __func__; // COMPLIANT +} + +void g(int (*l)(int)) {} + +void test_lambda(const int y) { + // Lambda generates a static function called `_FUN` when the lambda is + // converted to a function pointer + g([](int x) { return x; }); // COMPLIANT - compiler generated +} diff --git a/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.expected b/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.expected deleted file mode 100644 index 38fcaf61be..0000000000 --- a/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.expected +++ /dev/null @@ -1,20 +0,0 @@ -| arrays.cpp:11:20:11:21 | wa | 'wa' may leak information from {elements of a[...] (arrays.cpp:7)}. Path: wa (arrays.cpp:11) --> & ... (arrays.cpp:12) | -| arrays.cpp:33:22:33:23 | wa | 'wa' may leak information from {elements of elements of a[...][...] (arrays.cpp:29)}. Path: wa (arrays.cpp:33) --> & ... (arrays.cpp:34) | -| arrays.cpp:57:22:57:23 | wa | 'wa' may leak information from {WithPointer (arrays.cpp:52)}. Path: wa (arrays.cpp:57) --> & ... (arrays.cpp:59) | -| inheritance.cpp:19:14:19:14 | s | 's' may leak information from {i (inheritance.cpp:7)}. Path: s (inheritance.cpp:19) --> & ... (inheritance.cpp:21) | -| inheritance.cpp:32:14:32:14 | s | 's' may leak information from {0 to 4 bytes of padding in ptrDerived (inheritance.cpp:14)}. Path: s (inheritance.cpp:32) --> & ... (inheritance.cpp:35) | -| interprocedural.cpp:37:9:37:9 | p | 'p' may leak information from {y (interprocedural.cpp:8)}. Path: p (interprocedural.cpp:37) --> past assign_x (interprocedural.cpp:32) --> & ... (interprocedural.cpp:39) | -| interprocedural.cpp:104:9:104:9 | p | 'p' may leak information from {x (interprocedural.cpp:7), y (interprocedural.cpp:8)}. Path: p (interprocedural.cpp:104) --> overwrite_after_leak(...) (interprocedural.cpp:96) --> p (interprocedural.cpp:97) | -| multilayer.cpp:16:10:16:10 | s | 's' may leak information from {b (multilayer.cpp:12)}. Path: s (multilayer.cpp:16) --> & ... (multilayer.cpp:18) | -| multilayer.cpp:29:10:29:10 | s | 's' may leak information from {b (multilayer.cpp:12), x (multilayer.cpp:7)}. Path: s (multilayer.cpp:29) --> & ... (multilayer.cpp:30) | -| multilayer.cpp:34:8:34:8 | s | 's' may leak information from {intx (multilayer.cpp:6)}. Path: s (multilayer.cpp:34) --> & ... (multilayer.cpp:35) | -| test.cpp:12:12:12:12 | s | 's' may leak information from {y (test.cpp:8)}. Path: s (test.cpp:12) --> & ... (test.cpp:14) | -| test.cpp:18:12:18:12 | s | 's' may leak information from {x (test.cpp:7)}. Path: s (test.cpp:18) --> & ... (test.cpp:20) | -| test.cpp:24:12:24:12 | s | 's' may leak information from {x (test.cpp:7), y (test.cpp:8)}. Path: s (test.cpp:24) --> & ... (test.cpp:25) | -| test.cpp:36:12:36:12 | s | 's' may leak information from {y (test.cpp:8)}. Path: s (test.cpp:36) --> & ... (test.cpp:38) | -| test.cpp:43:12:43:12 | s | 's' may leak information from {x (test.cpp:7)}. Path: s (test.cpp:43) --> & ... (test.cpp:47) | -| test.cpp:58:12:58:12 | s | 's' may leak information from {x (test.cpp:7), y (test.cpp:8)}. Path: s (test.cpp:58) --> & ... (test.cpp:59) | -| test.cpp:64:12:64:12 | s | 's' may leak information from {y (test.cpp:8)}. Path: s (test.cpp:64) --> & ... (test.cpp:66) | -| test.cpp:112:16:112:16 | s | 's' may leak information from {buf (test.cpp:92)}. Path: s (test.cpp:112) --> & ... (test.cpp:115) | -| test.cpp:128:12:128:12 | s | 's' may leak information from {x (test.cpp:7), y (test.cpp:8)}. Path: s (test.cpp:128) --> & ... (test.cpp:132) | -| test.cpp:157:22:157:22 | s | 's' may leak information from {2 to 2 bytes of padding in has_padding (test.cpp:151)}. Path: s (test.cpp:157) --> & ... (test.cpp:160) | diff --git a/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.qlref b/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.qlref deleted file mode 100644 index 7fd5774344..0000000000 --- a/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.testref b/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.testref new file mode 100644 index 0000000000..44035e2ee4 --- /dev/null +++ b/cpp/cert/test/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.testref @@ -0,0 +1 @@ +cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/DCL55-CPP/test.cpp b/cpp/cert/test/rules/DCL55-CPP/test.cpp deleted file mode 100644 index 3b68ffbd86..0000000000 --- a/cpp/cert/test/rules/DCL55-CPP/test.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include - -unsigned long copy_to_user(void *to, const void *from, unsigned long n); - -typedef struct { - int x; - int y; -} MyStruct; - -void forget_y() { - MyStruct s; - s.x = 1; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) -} - -void forget_x() { - MyStruct s; - s.y = 1; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) -} - -void forget_both() { - MyStruct s; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x, y) -} - -void init_both() { - MyStruct s; - s.x = 1; - s.y = 1; - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -void init_after() { - MyStruct s; - s.x = 1; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT - s.y = 1; -} - -void init_other() { - MyStruct s, t; - s.y = 1; - t.x = 1; - t.y = 1; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) - copy_to_user(0, &t, sizeof t); // COMPLIANT -} - -void zero_memory() { - MyStruct s; - std::memset(&s, 0, sizeof s); - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -void zero_memory_after() { - MyStruct s; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT - std::memset(&s, 0, sizeof s); -} - -void zero_field() { - MyStruct s; - std::memset(&s.x, 0, sizeof s.x); - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) -} - -void overwrite_with_zeroed() { - MyStruct s, t; - std::memset(&t, 0, sizeof t); - s = t; - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -void overwrite_struct_with_uninit() { - MyStruct s, t; - s = t; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT[FALSE NEGATIVE] -} - -void overwrite_field_with_uninit() { - MyStruct s; - int x; - s.x = x; - s.y = 1; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT[FALSE NEGATIVE] -} - -typedef struct { - size_t length; - char buf[128]; -} PascalString; - -void zero_array() { - PascalString s; - std::memset(s.buf, 0, sizeof s.buf); - s.length = 0; - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -void zero_array_by_ref() { - PascalString s; - std::memset(&s.buf, 0, sizeof s.buf); - s.length = 0; - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -char *strcpy(char *dst, const char *src); - -void use_strcpy() { - PascalString s; - strcpy(s.buf, "Hello, World"); // does not zero rest of s.buf - s.length = std::strlen(s.buf); - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (buf) -} - -void *malloc(size_t size); - -void heap_memory() { - MyStruct *s; - s = (MyStruct *)malloc(sizeof(*s)); - s->x = 1; - copy_to_user(0, s, sizeof(*s)); // NON_COMPLIANT[FALSE NEGATIVE] -} - -void conditional_memset(int b) { - MyStruct s; - if (b) { - std::memset(&s, 0, sizeof s); - } - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT -} - -void memset_field() { - MyStruct s; - std::memset(&s.x, 0, sizeof(s.x)); - s.y = 1; - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -const static int one = 1; -void zero_if_true() { - MyStruct s; - if (one) { - std::memset(&s, 0, sizeof s); - } - copy_to_user(0, &s, sizeof s); // COMPLIANT -} - -struct has_padding { - short s; - int i; -}; - -void forget_padding() { - struct has_padding s; - s.s = 1; - s.i = 1; - copy_to_user(0, &s, sizeof s); // NON_COMPLIANT -} - -void remember_padding() { - struct has_padding s; - std::memset(&s, 0, sizeof s); - copy_to_user(0, &s, sizeof s); // COMPLIANT -} \ No newline at end of file diff --git a/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.expected b/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.expected deleted file mode 100644 index 220e1717b6..0000000000 --- a/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:7:9:7:19 | setjmp(env) | Use of banned setjmp macro. | -| test.cpp:12:3:12:9 | call to longjmp | Use of banned longjmp function. | diff --git a/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.qlref b/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.qlref deleted file mode 100644 index 230a25b8c1..0000000000 --- a/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.testref b/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.testref new file mode 100644 index 0000000000..87f68653c8 --- /dev/null +++ b/cpp/cert/test/rules/ERR52-CPP/DoNotUseSetjmpOrLongjmp.testref @@ -0,0 +1 @@ +cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/ERR52-CPP/test.cpp b/cpp/cert/test/rules/ERR52-CPP/test.cpp deleted file mode 100644 index 20a1ed8d9c..0000000000 --- a/cpp/cert/test/rules/ERR52-CPP/test.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -int test_jmps() { - jmp_buf env; - int val; - - val = setjmp(env); - if (val) { - return (val); - } - - longjmp(env, 101); - - return 0; -} diff --git a/cpp/cert/test/rules/ERR55-CPP/HonorExceptionSpecifications.expected b/cpp/cert/test/rules/ERR55-CPP/HonorExceptionSpecifications.expected index ba234df2b8..5091d1fc2e 100644 --- a/cpp/cert/test/rules/ERR55-CPP/HonorExceptionSpecifications.expected +++ b/cpp/cert/test/rules/ERR55-CPP/HonorExceptionSpecifications.expected @@ -1,8 +1,35 @@ edges +| test_dynamic_specification.cpp:9:6:9:22 | throw_logic_error [logic_error] | test_dynamic_specification.cpp:34:3:34:19 | call to throw_logic_error [logic_error] | +| test_dynamic_specification.cpp:9:6:9:22 | throw_logic_error [logic_error] | test_dynamic_specification.cpp:38:3:38:19 | call to throw_logic_error [logic_error] | +| test_dynamic_specification.cpp:9:6:9:22 | throw_logic_error [logic_error] | test_dynamic_specification.cpp:43:3:43:19 | call to throw_logic_error [logic_error] | +| test_dynamic_specification.cpp:9:28:9:58 | throw ... [logic_error] | test_dynamic_specification.cpp:9:6:9:22 | throw_logic_error [logic_error] | | test_dynamic_specification.cpp:22:3:22:24 | throw ... [exception] | test_dynamic_specification.cpp:20:6:20:49 | test_simple_exception_spec_covered_inherited [exception] | | test_dynamic_specification.cpp:29:3:29:24 | throw ... [exception] | test_dynamic_specification.cpp:28:6:28:30 | test_no_throw_contravened [exception] | -| test_no_except.cpp:8:3:8:14 | throw ... [char *] | test_no_except.cpp:7:6:7:23 | test_noexcept_true [char *] | +| test_dynamic_specification.cpp:33:6:33:31 | indirect_throw_logic_error [logic_error] | test_dynamic_specification.cpp:48:3:48:28 | call to indirect_throw_logic_error [logic_error] | +| test_dynamic_specification.cpp:34:3:34:19 | call to throw_logic_error [logic_error] | test_dynamic_specification.cpp:33:6:33:31 | indirect_throw_logic_error [logic_error] | +| test_dynamic_specification.cpp:38:3:38:19 | call to throw_logic_error [logic_error] | test_dynamic_specification.cpp:37:6:37:46 | indirect_throw_logic_error_but_terminates [logic_error] | +| test_dynamic_specification.cpp:43:3:43:19 | call to throw_logic_error [logic_error] | test_dynamic_specification.cpp:41:6:41:48 | indirect_throw_logic_error_but_terminates_2 [logic_error] | +| test_dynamic_specification.cpp:48:3:48:28 | call to indirect_throw_logic_error [logic_error] | test_dynamic_specification.cpp:47:6:47:25 | test_indirect_throws [logic_error] | +| test_no_except.cpp:4:3:4:20 | throw ... [ExceptionA] | test_no_except.cpp:3:6:3:15 | test_throw [ExceptionA] | +| test_no_except.cpp:7:6:7:11 | throwA [ExceptionA] | test_no_except.cpp:8:25:8:30 | call to throwA [ExceptionA] | +| test_no_except.cpp:7:6:7:11 | throwA [ExceptionA] | test_no_except.cpp:9:42:9:47 | call to throwA [ExceptionA] | +| test_no_except.cpp:7:6:7:11 | throwA [ExceptionA] | test_no_except.cpp:12:3:12:8 | call to throwA [ExceptionA] | +| test_no_except.cpp:7:6:7:11 | throwA [ExceptionA] | test_no_except.cpp:16:3:16:8 | call to throwA [ExceptionA] | +| test_no_except.cpp:7:17:7:34 | throw ... [ExceptionA] | test_no_except.cpp:7:6:7:11 | throwA [ExceptionA] | +| test_no_except.cpp:8:6:8:19 | indirectThrowA [ExceptionA] | test_no_except.cpp:33:3:33:16 | call to indirectThrowA [ExceptionA] | +| test_no_except.cpp:8:25:8:30 | call to throwA [ExceptionA] | test_no_except.cpp:8:6:8:19 | indirectThrowA [ExceptionA] | +| test_no_except.cpp:9:42:9:47 | call to throwA [ExceptionA] | test_no_except.cpp:9:6:9:27 | noexceptIndirectThrowA [ExceptionA] | +| test_no_except.cpp:12:3:12:8 | call to throwA [ExceptionA] | test_no_except.cpp:11:6:11:24 | test_indirect_throw [ExceptionA] | +| test_no_except.cpp:16:3:16:8 | call to throwA [ExceptionA] | test_no_except.cpp:15:6:15:26 | test_indirect_throw_2 [ExceptionA] | +| test_no_except.cpp:33:3:33:16 | call to indirectThrowA [ExceptionA] | test_no_except.cpp:32:6:32:26 | test_indirect_throw_6 [ExceptionA] | #select | test_dynamic_specification.cpp:20:6:20:49 | test_simple_exception_spec_covered_inherited | test_dynamic_specification.cpp:22:3:22:24 | throw ... [exception] | test_dynamic_specification.cpp:20:6:20:49 | test_simple_exception_spec_covered_inherited [exception] | test_simple_exception_spec_covered_inherited can throw an exception of type std::exception but has a dynamic exception specification that does not specify this type. | | test_dynamic_specification.cpp:28:6:28:30 | test_no_throw_contravened | test_dynamic_specification.cpp:29:3:29:24 | throw ... [exception] | test_dynamic_specification.cpp:28:6:28:30 | test_no_throw_contravened [exception] | test_no_throw_contravened can throw an exception of type std::exception but has a dynamic exception specification that does not specify this type. | -| test_no_except.cpp:7:6:7:23 | test_noexcept_true | test_no_except.cpp:8:3:8:14 | throw ... [char *] | test_no_except.cpp:7:6:7:23 | test_noexcept_true [char *] | test_noexcept_true can throw an exception of type char * but is marked noexcept(true). | +| test_dynamic_specification.cpp:37:6:37:46 | indirect_throw_logic_error_but_terminates | test_dynamic_specification.cpp:9:28:9:58 | throw ... [logic_error] | test_dynamic_specification.cpp:37:6:37:46 | indirect_throw_logic_error_but_terminates [logic_error] | indirect_throw_logic_error_but_terminates can throw an exception of type std::logic_error but has a dynamic exception specification that does not specify this type. | +| test_dynamic_specification.cpp:41:6:41:48 | indirect_throw_logic_error_but_terminates_2 | test_dynamic_specification.cpp:9:28:9:58 | throw ... [logic_error] | test_dynamic_specification.cpp:41:6:41:48 | indirect_throw_logic_error_but_terminates_2 [logic_error] | indirect_throw_logic_error_but_terminates_2 can throw an exception of type std::logic_error but has a dynamic exception specification that does not specify this type. | +| test_dynamic_specification.cpp:47:6:47:25 | test_indirect_throws | test_dynamic_specification.cpp:9:28:9:58 | throw ... [logic_error] | test_dynamic_specification.cpp:47:6:47:25 | test_indirect_throws [logic_error] | test_indirect_throws can throw an exception of type std::logic_error but has a dynamic exception specification that does not specify this type. | +| test_no_except.cpp:3:6:3:15 | test_throw | test_no_except.cpp:4:3:4:20 | throw ... [ExceptionA] | test_no_except.cpp:3:6:3:15 | test_throw [ExceptionA] | test_throw can throw an exception of type ExceptionA but is marked noexcept(true). | +| test_no_except.cpp:9:6:9:27 | noexceptIndirectThrowA | test_no_except.cpp:7:17:7:34 | throw ... [ExceptionA] | test_no_except.cpp:9:6:9:27 | noexceptIndirectThrowA [ExceptionA] | noexceptIndirectThrowA can throw an exception of type ExceptionA but is marked noexcept(true). | +| test_no_except.cpp:11:6:11:24 | test_indirect_throw | test_no_except.cpp:7:17:7:34 | throw ... [ExceptionA] | test_no_except.cpp:11:6:11:24 | test_indirect_throw [ExceptionA] | test_indirect_throw can throw an exception of type ExceptionA but is marked noexcept(true). | +| test_no_except.cpp:15:6:15:26 | test_indirect_throw_2 | test_no_except.cpp:7:17:7:34 | throw ... [ExceptionA] | test_no_except.cpp:15:6:15:26 | test_indirect_throw_2 [ExceptionA] | test_indirect_throw_2 can throw an exception of type ExceptionA but is marked noexcept(true). | +| test_no_except.cpp:32:6:32:26 | test_indirect_throw_6 | test_no_except.cpp:7:17:7:34 | throw ... [ExceptionA] | test_no_except.cpp:32:6:32:26 | test_indirect_throw_6 [ExceptionA] | test_indirect_throw_6 can throw an exception of type ExceptionA but is marked noexcept(true). | diff --git a/cpp/cert/test/rules/ERR55-CPP/test_dynamic_specification.cpp b/cpp/cert/test/rules/ERR55-CPP/test_dynamic_specification.cpp index 4b218e1847..82e32bd433 100644 --- a/cpp/cert/test/rules/ERR55-CPP/test_dynamic_specification.cpp +++ b/cpp/cert/test/rules/ERR55-CPP/test_dynamic_specification.cpp @@ -27,4 +27,28 @@ void test_no_throw() throw() { // COMPLIANT void test_no_throw_contravened() throw() { // NON_COMPLIANT throw std::exception(); +} + +class DummyException {}; +void indirect_throw_logic_error() throw(std::logic_error) { + throw_logic_error(); // Exception flows out of function as specification is + // compatible +} +void indirect_throw_logic_error_but_terminates() throw() { // NON_COMPLIANT + throw_logic_error(); // Exception does not flow out of function due to + // specification +} +void indirect_throw_logic_error_but_terminates_2() // NON_COMPLIANT + throw(DummyException) { + throw_logic_error(); // Exception does not flow out of function due to + // specification +} + +void test_indirect_throws() throw() { // NON_COMPLIANT + indirect_throw_logic_error(); +} + +void test_indirect_throws_but_terminated() throw() { // COMPLIANT + indirect_throw_logic_error_but_terminates(); + indirect_throw_logic_error_but_terminates_2(); } \ No newline at end of file diff --git a/cpp/cert/test/rules/ERR55-CPP/test_no_except.cpp b/cpp/cert/test/rules/ERR55-CPP/test_no_except.cpp index 7897bed237..767dbb7ec0 100644 --- a/cpp/cert/test/rules/ERR55-CPP/test_no_except.cpp +++ b/cpp/cert/test/rules/ERR55-CPP/test_no_except.cpp @@ -1,9 +1,34 @@ -#include +class ExceptionA {}; -void test_noexcept_false() { // COMPLIANT - throw "test"; +void test_throw() noexcept(true) { + throw ExceptionA(); // NON_COMPLIANT - function marked as noexcept(true) } -void test_noexcept_true() noexcept(true) { // NON_COMPLIANT - throw "test"; +void throwA() { throw ExceptionA(); } +void indirectThrowA() { throwA(); } +void noexceptIndirectThrowA() noexcept { throwA(); } // NON_COMPLIANT + +void test_indirect_throw() noexcept(true) { + throwA(); // NON_COMPLIANT - function marked as noexcept(true) +} + +void test_indirect_throw_2() noexcept { + throwA(); // NON_COMPLIANT - function marked as noexcept(true) +} + +void test_indirect_throw_3() noexcept(false) { + throwA(); // COMPLIANT - function marked as noexcept(false) +} + +void test_indirect_throw_4() { + throwA(); // COMPLIANT - function marked as noexcept(false) +} + +void test_indirect_throw_5() noexcept { + noexceptIndirectThrowA(); // COMPLIANT - noexceptIndirectThrowA would call + // std::terminate() if ExceptionA is thrown +} + +void test_indirect_throw_6() noexcept { + indirectThrowA(); // NON_COMPLIANT } \ No newline at end of file diff --git a/cpp/cert/test/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.expected.clang b/cpp/cert/test/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/cert/test/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.expected.gcc b/cpp/cert/test/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/cert/test/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.expected.qcc b/cpp/cert/test/rules/ERR59-CPP/DoNotThrowAnExceptionAcrossExecutionBoundaries.expected.qcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.expected b/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.expected index 723f3acedb..08d46a7bbd 100644 --- a/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.expected +++ b/cpp/cert/test/rules/EXP50-CPP/DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.expected @@ -1,5 +1,33 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:29,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:29,59-67) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:32,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:32,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:36,33-41) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:36,59-67) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:45,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:45,25-33) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:45,53-61) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:48,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:48,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:57,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:57,55-63) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:64,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:64,57-65) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:76,31-39) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:76,55-63) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:29,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:32,7-20) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:36,7-20) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:48,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:57,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:64,5-18) +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments.ql:76,5-18) | test.cpp:82:3:82:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:82:6:82:7 | call to f5 | call to f5 | test.cpp:82:12:82:13 | call to f6 | call to f6 | | test.cpp:84:3:84:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:84:6:84:7 | call to f5 | call to f5 | test.cpp:84:12:84:13 | call to f7 | call to f7 | | test.cpp:87:3:87:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:87:9:87:10 | call to m1 | call to m1 | test.cpp:87:18:87:19 | call to m1 | call to m1 | | test.cpp:89:3:89:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:89:9:89:10 | call to m2 | call to m2 | test.cpp:89:18:89:19 | call to m2 | call to m2 | +| test.cpp:92:3:92:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:92:6:92:7 | call to f8 | call to f8 | test.cpp:92:14:92:15 | call to f9 | call to f9 | +| test.cpp:93:3:93:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:93:6:93:7 | call to f8 | call to f8 | test.cpp:93:14:93:16 | call to f11 | call to f11 | +| test.cpp:95:3:95:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:95:6:95:7 | call to f8 | call to f8 | test.cpp:95:13:95:14 | call to f9 | call to f9 | +| test.cpp:96:3:96:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:96:6:96:7 | call to f8 | call to f8 | test.cpp:96:13:96:15 | call to f11 | call to f11 | | test.cpp:99:3:99:4 | call to f2 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.cpp:99:9:99:10 | call to m1 | call to m1 | test.cpp:99:18:99:19 | call to m1 | call to m1 | diff --git a/cpp/cert/test/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.expected b/cpp/cert/test/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.expected index a50daa096e..8b7a4902cc 100644 --- a/cpp/cert/test/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.expected +++ b/cpp/cert/test/rules/EXP51-CPP/DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.expected @@ -1,6 +1,10 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql:24,44-52) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql:25,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql:27,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType.ql:32,33-41) edges -| test.cpp:6:19:6:37 | new[] | test.cpp:9:12:9:13 | l1 | -| test.cpp:7:22:7:40 | new[] | test.cpp:10:12:10:13 | l2 | +| test.cpp:6:19:6:37 | new[] | test.cpp:9:12:9:13 | l1 | provenance | | +| test.cpp:7:22:7:40 | new[] | test.cpp:10:12:10:13 | l2 | provenance | | nodes | test.cpp:6:19:6:37 | new[] | semmle.label | new[] | | test.cpp:7:22:7:40 | new[] | semmle.label | new[] | diff --git a/cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.cpp b/cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.cpp index 59304f5104..68ffc8dc7d 100644 --- a/cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.cpp +++ b/cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInSizeOfOperand.cpp @@ -1,6 +1,6 @@ int g1 = 0; -unsigned char g2[sizeof(g1++)]; // NOT_COMPLIANT +unsigned char g2[sizeof(g1++)]; // NON_COMPLIANT void f1(int p); void f2(long long p); diff --git a/cpp/cert/test/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.expected.clang b/cpp/cert/test/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.expected.clang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/cert/test/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.expected.gcc b/cpp/cert/test/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.expected.gcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/cert/test/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.expected.qcc b/cpp/cert/test/rules/EXP60-CPP/DoNotPassANonstandardObjectAcrossBoundaries.expected.qcc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.expected b/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.expected deleted file mode 100644 index bb0dc52e0c..0000000000 --- a/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:19:8:19:18 | call to memcmp | memcmp accesses bits which are not part of the object's value representation. | diff --git a/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.qlref b/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.qlref deleted file mode 100644 index 103173391a..0000000000 --- a/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.testref b/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.testref new file mode 100644 index 0000000000..aacddd73c6 --- /dev/null +++ b/cpp/cert/test/rules/EXP62-CPP/MemcmpUsedToAccessObjectRepresentation.testref @@ -0,0 +1 @@ +cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.expected b/cpp/cert/test/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.expected index 23ba8ceb60..b690d25f2b 100644 --- a/cpp/cert/test/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.expected +++ b/cpp/cert/test/rules/EXP62-CPP/MemcpyUsedToAccessObjectRepresentation.expected @@ -1 +1 @@ -| test.cpp:32:3:32:13 | call to memcpy | call to memcpy accesses bits which are not part of the object's value representation. | +| test.cpp:12:3:12:13 | call to memcpy | call to memcpy accesses bits which are not part of the object's value representation. | diff --git a/cpp/cert/test/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.expected b/cpp/cert/test/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.expected index 56b93ef68b..1ad5885d8d 100644 --- a/cpp/cert/test/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.expected +++ b/cpp/cert/test/rules/EXP62-CPP/MemsetUsedToAccessObjectRepresentation.expected @@ -1 +1 @@ -| test.cpp:59:3:59:13 | call to memset | call to memset accesses bits which are not part of the object's value representation. | +| test.cpp:39:3:39:13 | call to memset | call to memset accesses bits which are not part of the object's value representation. | diff --git a/cpp/cert/test/rules/EXP62-CPP/test.cpp b/cpp/cert/test/rules/EXP62-CPP/test.cpp index 818686e1ff..ebda14e624 100644 --- a/cpp/cert/test/rules/EXP62-CPP/test.cpp +++ b/cpp/cert/test/rules/EXP62-CPP/test.cpp @@ -1,24 +1,4 @@ -#include - -struct S { - unsigned char buffType; - int size; - - friend bool operator==(const S &lhs, const S &rhs) { - return lhs.buffType == rhs.buffType && lhs.size == rhs.size; - } -}; - -void f(const S &s1, const S &s2) { - if (s1 == s2) { - // COMPLIANT S overloads operator==() to perform a comparison of the value - // representation of the object - } -} -void f1(const S &s1, const S &s2) { - if (!std::memcmp(&s1, &s2, sizeof(S))) { // NON_COMPLIANT - } -} +#include struct S1 { int i, j, k; diff --git a/cpp/cert/test/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.expected b/cpp/cert/test/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.expected index b7452ec199..41fa58045f 100644 --- a/cpp/cert/test/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.expected +++ b/cpp/cert/test/rules/MEM52-CPP/DetectAndHandleMemoryAllocationErrors.expected @@ -1,2 +1,9 @@ +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:64,5-13) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:66,36-44) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:82,46-54) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:83,22-30) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:87,20-28) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:90,35-43) +WARNING: module 'DataFlow' has been deprecated and may be removed in future (DetectAndHandleMemoryAllocationErrors.ql:95,38-46) | test.cpp:24:7:24:34 | new | nothrow new allocation of $@ returns here without a subsequent check to see whether the pointer is valid. | test.cpp:24:7:24:34 | new | StructA * | | test.cpp:40:17:40:38 | call to allocate_without_check | nothrow new allocation of $@ returns here without a subsequent check to see whether the pointer is valid. | test.cpp:35:17:35:44 | new | StructA * | diff --git a/cpp/cert/test/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.expected b/cpp/cert/test/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.expected index 00ed15c370..f7f4705ef3 100644 --- a/cpp/cert/test/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.expected +++ b/cpp/cert/test/rules/MEM53-CPP/MissingDestructorCallForManuallyManagedObject.expected @@ -1,8 +1,8 @@ edges -| test.cpp:16:26:16:31 | call to malloc | test.cpp:22:8:22:9 | a1 | -| test.cpp:17:38:17:43 | call to malloc | test.cpp:23:8:23:9 | a2 | -| test.cpp:18:26:18:39 | call to operator new | test.cpp:26:21:26:22 | a3 | -| test.cpp:20:29:20:42 | call to operator new | test.cpp:27:21:27:22 | a4 | +| test.cpp:16:26:16:31 | call to malloc | test.cpp:22:8:22:9 | a1 | provenance | | +| test.cpp:17:38:17:43 | call to malloc | test.cpp:23:8:23:9 | a2 | provenance | | +| test.cpp:18:26:18:39 | call to operator new | test.cpp:26:21:26:22 | a3 | provenance | | +| test.cpp:20:29:20:42 | call to operator new | test.cpp:27:21:27:22 | a4 | provenance | | nodes | test.cpp:16:26:16:31 | call to malloc | semmle.label | call to malloc | | test.cpp:17:38:17:43 | call to malloc | semmle.label | call to malloc | diff --git a/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected b/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected index 0128221ffc..606ccbff2b 100644 --- a/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected +++ b/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected @@ -1,3 +1,4 @@ +WARNING: module 'TaintTracking' has been deprecated and may be removed in future (BadlySeededRandomNumberGenerator.ql:42,7-20) | test.cpp:9:33:9:33 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | | test.cpp:10:30:10:31 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | | test.cpp:11:21:11:22 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | diff --git a/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected.gcc b/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected.gcc new file mode 100644 index 0000000000..c257c0e6b4 --- /dev/null +++ b/cpp/cert/test/rules/MSC51-CPP/BadlySeededRandomNumberGenerator.expected.gcc @@ -0,0 +1,15 @@ +| test.cpp:9:33:9:33 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:10:30:10:31 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:11:21:11:22 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:12:20:12:21 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:13:16:13:17 | call to mersenne_twister_engine | Random number generator mersenne_twister_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:14:19:14:20 | call to mersenne_twister_engine | Random number generator mersenne_twister_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:15:22:15:23 | call to subtract_with_carry_engine | Random number generator subtract_with_carry_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:16:22:16:23 | call to subtract_with_carry_engine | Random number generator subtract_with_carry_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:17:17:17:19 | call to discard_block_engine | Random number generator discard_block_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:18:17:18:19 | call to discard_block_engine | Random number generator discard_block_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:19:16:19:18 | call to shuffle_order_engine | Random number generator shuffle_order_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:21:34:22:47 | call to linear_congruential_engine | Random number generator linear_congruential_engine is initialized from std::time and is therefore not properly seeded. | +| test.cpp:23:34:23:36 | call to linear_congruential_engine | Random number generator linear_congruential_engine is is initialized to the constant value 2 and is therefore not properly seeded. | +| test.cpp:29:30:29:35 | default initialization engine of type default_random_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | +| test.cpp:36:23:36:23 | call to linear_congruential_engine | Random number generator linear_congruential_engine is default-initialized and is therefore not properly seeded. | diff --git a/cpp/cert/test/rules/OOP57-CPP/test.cpp b/cpp/cert/test/rules/OOP57-CPP/test.cpp index 02059f98cc..430c910985 100644 --- a/cpp/cert/test/rules/OOP57-CPP/test.cpp +++ b/cpp/cert/test/rules/OOP57-CPP/test.cpp @@ -1,4 +1,4 @@ -#include +#include class trivial {}; diff --git a/cpp/common/src/codeql-pack.lock.yml b/cpp/common/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/common/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/cpp/common/src/codingstandards/cpp/AccessPath.qll b/cpp/common/src/codingstandards/cpp/AccessPath.qll index ff7601ed4b..3af462e1ec 100644 --- a/cpp/common/src/codingstandards/cpp/AccessPath.qll +++ b/cpp/common/src/codingstandards/cpp/AccessPath.qll @@ -1,5 +1,5 @@ import cpp -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow newtype TFieldQualifier = ExplicitQualifier(VariableAccess v) or diff --git a/cpp/common/src/codingstandards/cpp/AlertReporting.qll b/cpp/common/src/codingstandards/cpp/AlertReporting.qll new file mode 100644 index 0000000000..3ef5315906 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/AlertReporting.qll @@ -0,0 +1,63 @@ +/** + * Provides a library for managing how alerts are reported. + */ + +import cpp + +signature class ResultType extends Element; + +/** + * A module for unwrapping results that occur in macro expansions. + */ +module MacroUnwrapper { + /** + * Gets a macro invocation that applies to the result element. + */ + private MacroInvocation getAMacroInvocation(ResultElement re) { + result.getAnExpandedElement() = re + } + + /** + * Gets the primary macro invocation that generated the result element. + */ + MacroInvocation getPrimaryMacroInvocation(ResultElement re) { + exists(MacroInvocation mi | + mi = getAMacroInvocation(re) and + // No other more specific macro that expands to element + not exists(MacroInvocation otherMi | + otherMi = getAMacroInvocation(re) and otherMi.getParentInvocation() = mi + ) and + result = mi + ) + } + + /** + * Gets the primary macro that generated the result element. + */ + Macro getPrimaryMacro(ResultElement re) { result = getPrimaryMacroInvocation(re).getMacro() } + + /** + * If a result element is expanded from a macro invocation, then return the "primary" macro that + * generated the element, otherwise return the element itself. + */ + Element unwrapElement(ResultElement re) { + if exists(getPrimaryMacro(re)) then result = getPrimaryMacro(re) else result = re + } + + /* Final class so we can extend it */ + final private class FinalMacroInvocation = MacroInvocation; + + /* A macro invocation that expands to create a `ResultElement` */ + class ResultMacroExpansion extends FinalMacroInvocation { + ResultElement re; + + ResultMacroExpansion() { re = getAnExpandedElement() } + + ResultElement getResultElement() { result = re } + } + + /* The most specific macro invocation that expands to create this `ResultElement`. */ + class PrimaryMacroExpansion extends ResultMacroExpansion { + PrimaryMacroExpansion() { this = getPrimaryMacroInvocation(re) } + } +} diff --git a/cpp/common/src/codingstandards/cpp/Alignment.qll b/cpp/common/src/codingstandards/cpp/Alignment.qll new file mode 100644 index 0000000000..c254d7909a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Alignment.qll @@ -0,0 +1,25 @@ +/** + * Provides a library with additional modeling for C and C++ memory alignment constructs. + */ + +import cpp + +/* + * In theory each compilation of each file can have a different `max_align_t` value (for example, + * if the same file is compiled under different compilers in the same database). We don't have the + * fine-grained data to determine which compilation each operator new call is from, so we instead + * report only in cases where there's a single clear alignment for the whole database. + */ + +class MaxAlignT extends TypedefType { + MaxAlignT() { getName() = "max_align_t" } +} + +/** + * Gets the alignment for `max_align_t`, assuming there is a single consistent alignment for the + * database. + */ +int getGlobalMaxAlignT() { + count(MaxAlignT m | | m.getAlignment()) = 1 and + result = any(MaxAlignT t).getAlignment() +} diff --git a/cpp/common/src/codingstandards/cpp/Allocations.qll b/cpp/common/src/codingstandards/cpp/Allocations.qll index db47b0b028..decdfe9fc4 100644 --- a/cpp/common/src/codingstandards/cpp/Allocations.qll +++ b/cpp/common/src/codingstandards/cpp/Allocations.qll @@ -7,7 +7,7 @@ import cpp import semmle.code.cpp.controlflow.SSA -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow /** * Holds if `alloc` is a use of `malloc` or `new`. `kind` is diff --git a/cpp/common/src/codingstandards/cpp/BuiltInNumericTypes.qll b/cpp/common/src/codingstandards/cpp/BuiltInNumericTypes.qll new file mode 100644 index 0000000000..b145428a57 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/BuiltInNumericTypes.qll @@ -0,0 +1,22 @@ +import cpp +import codingstandards.cpp.EncapsulatingFunctions + +class BuiltInIntegerType extends BuiltInType { + BuiltInIntegerType() { + this instanceof ShortType + or + this instanceof IntType + or + this instanceof LongType + or + this instanceof LongLongType + } +} + +/** + * any `Parameter` in a main function like: + * int main(int argc, char *argv[]) + */ +class ExcludedVariable extends Parameter { + ExcludedVariable() { getFunction() instanceof MainFunction } +} diff --git a/cpp/common/src/codingstandards/cpp/CharFunctions.qll b/cpp/common/src/codingstandards/cpp/CharFunctions.qll index 352f61858c..0782dbbcaa 100644 --- a/cpp/common/src/codingstandards/cpp/CharFunctions.qll +++ b/cpp/common/src/codingstandards/cpp/CharFunctions.qll @@ -1,31 +1,112 @@ import cpp -/** - * Models a class of functions that are either testers of characters - * or standard library conversion functions. - */ -class CToOrIsCharFunction extends Function { - CToOrIsCharFunction() { - this instanceof CIsCharFunction or - this instanceof CToCharFunction - } +private string getCToOrIsName() { + result = + [ + "isalnum", "isalpha", "isascii", "isblank", "iscntrl", "isdigit", "isgraph", "islower", + "isprint", "ispunct", "isspace", "isupper", "isxdigit", "__isspace", "toascii", "toupper", + "tolower" + ] } /** - * Models a class of functions that test characters. + * A use of one of the APIs in the `` header that test or convert characters. + * + * Note: these operations are commonly implemented as either function or a macro. This class + * abstracts away from those details, providing a `getConvertedArgument` predicate to get the + * argument after any conversions specified by the user, excluding any conversions induced by + * the structure of the macro, or */ -class CIsCharFunction extends Function { - CIsCharFunction() { - getName() in [ - "isalnum", "isalpha", "isascii", "isblank", "iscntrl", "isdigit", "isgraph", "islower", - "isprint", "ispunct", "isspace", "isupper", "isxdigit", "__isspace" - ] +abstract class UseOfToOrIsChar extends Element { + abstract Expr getConvertedArgument(); +} + +private class CToOrIsCharFunctionCall extends FunctionCall, UseOfToOrIsChar { + CToOrIsCharFunctionCall() { + getTarget().getName() = getCToOrIsName() and + // Some library implementations, such as musl, include a "dead" call to the same function + // that has also been implemented as a macro, in order to retain the right types. We exclude + // this call because it does not appear in the control flow or data flow graph. However, + // isspace directly calls __isspace, which is allowed + ( + getTarget().getName() = "__isspace" or + not any(CToOrIsCharMacroInvocation mi).getAnExpandedElement() = this + ) } + + override Expr getConvertedArgument() { result = getArgument(0).getExplicitlyConverted() } } -/** - * Models a class of functions convert characters. - */ -class CToCharFunction extends Function { - CToCharFunction() { getName() in ["toascii", "toupper", "tolower"] } +private class CToOrIsCharMacroInvocation extends MacroInvocation, UseOfToOrIsChar { + CToOrIsCharMacroInvocation() { getMacroName() = getCToOrIsName() } + + override Expr getConvertedArgument() { + /* + * There is no common approach to how the macros are defined, so we handle + * each compiler/library case individually. Fortunately, there's no conflict + * between different compilers. + */ + + // For the "is" APIs, if clang and gcc use a macro, then it expands to an + // array access on the left hand side of an & + exists(ArrayExpr ae | ae = getExpr().(BitwiseAndExpr).getLeftOperand() | + // Casted to an explicit (int), so we want unwind only a single conversion + result = ae.getArrayOffset().getFullyConverted().(Conversion).getExpr() + ) + or + // For the "toupper/tolower" APIs, QNX expands to an array access + exists(ArrayExpr ae | + ae = getExpr() and + result = ae.getArrayOffset().getFullyConverted().(Conversion).getExpr() + ) + or + // For the tolower/toupper cases, a secondary macro is expanded + exists(MacroInvocation mi | + mi.getParentInvocation() = this and + mi.getMacroName() = "__tobody" + | + /* + * tolower and toupper can be defined by macros which: + * - if the size of the type is greater than 1 + * - then check if it's a compile time constant + * - then use c < -128 || c > 255 ? c : (a)[c] + * - else call the function + * - else (a)[c] + */ + + exists(ArrayExpr ae | + ae = mi.getAnExpandedElement() and + result = ae.getArrayOffset() and + // There are two array access, but only one should be reachable + result.getBasicBlock().isReachable() + ) + or + exists(ConditionalExpr ce | + ce = mi.getAnExpandedElement() and + result = ce.getThen() and + result.getBasicBlock().isReachable() + ) + ) + or + // musl uses a conditional expression as the expansion + exists(ConditionalExpr ce | ce = getExpr() | + // for most macro expansions, the else is a subtraction inside a `<` + exists(SubExpr s | + not getMacroName() = "isalpha" and + s = ce.getElse().(LTExpr).getLeftOperand() and + // Casted to an explicit (int), so we want unwind only a single conversion + result = s.getLeftOperand().getFullyConverted().(Conversion).getExpr() + ) + or + // for isalpha, the else is a bitwise or inside a subtraction inside a `<` + exists(BitwiseOrExpr bo | + // Casted to an explicit (unsigned) + getMacroName() = "isalpha" and + bo = ce.getElse().(LTExpr).getLeftOperand().(SubExpr).getLeftOperand() and + // Casted to an explicit (int), so we want unwind only a single conversion + result = + bo.getLeftOperand().getFullyConverted().(Conversion).getExpr().(ParenthesisExpr).getExpr() + ) + ) + } } diff --git a/cpp/common/src/codingstandards/cpp/Class.qll b/cpp/common/src/codingstandards/cpp/Class.qll index 2fff01c91f..6f730736f9 100644 --- a/cpp/common/src/codingstandards/cpp/Class.qll +++ b/cpp/common/src/codingstandards/cpp/Class.qll @@ -5,15 +5,26 @@ import cpp import codingstandards.cpp.Expr +private Class getADerivedClass(Class c) { + result = c.getADerivedClass() + or + exists(ClassTemplateInstantiation instantiation | + instantiation.getADerivedClass() = result and c = instantiation.getTemplate() + ) +} + /** - * Holds if we believe that `c` is used or intended to be used as a base class. + * A class that is used or intended to be used as a base class. */ -predicate isPossibleBaseClass(Class c, string reason) { - // There exists a derivation in this database - exists(c.getADerivedClass()) and reason = "a derived class exists" - or - // The class must be extended at some point - c.isAbstract() and reason = "the class is abstract" +class BaseClass extends Class { + BaseClass() { + exists(getADerivedClass(this)) + or + this.isAbstract() + } + + // We don't override `getADerivedClass` because that introduces a non-monotonic recursion. + Class getASubClass() { result = getADerivedClass(this) } } /** @@ -138,8 +149,7 @@ class IntrospectedMemberFunction extends MemberFunction { } predicate hasTrivialLength() { - this.getBlock().getNumStmt() <= 3 and - not exists(this.getBlock().getStmt(_).getChildStmt()) + this.getBlock().getLocation().getEndLine() - this.getBlock().getLocation().getStartLine() <= 10 } predicate isSetter() { @@ -181,7 +191,13 @@ class TrivialMemberFunction extends IntrospectedMemberFunction { * class. */ class TemplateOrTemplateClassMemberFunction extends MemberFunction { - TemplateOrTemplateClassMemberFunction() { isFromUninstantiatedTemplate(_) } + TemplateOrTemplateClassMemberFunction() { + ( + isFromUninstantiatedTemplate(_) or + isFromTemplateInstantiation(_) + ) and + not this.isCompilerGenerated() + } } /** diff --git a/cpp/common/src/codingstandards/cpp/Clvalues.qll b/cpp/common/src/codingstandards/cpp/Clvalues.qll new file mode 100644 index 0000000000..157041f13b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Clvalues.qll @@ -0,0 +1,20 @@ +import cpp + +/** + * An lvalue in C (as opposed to C++). + * + * Note that `Expr.isLValue()` matches for C++ lvalues, which is a larger set + * than the set of C lvalues. + */ +predicate isCLValue(Expr expr) { + expr instanceof PointerFieldAccess + or + expr.isLValue() and + not expr instanceof ConditionalExpr and + not expr instanceof AssignExpr and + not expr instanceof CommaExpr and + not exists(Cast c | c = expr.getConversion*()) + or + // 6.5.2.5.4: Compound literals are always lvalues. + expr instanceof AggregateLiteral +} diff --git a/cpp/common/src/codingstandards/cpp/Compiler.qll b/cpp/common/src/codingstandards/cpp/Compiler.qll new file mode 100644 index 0000000000..20aade5827 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Compiler.qll @@ -0,0 +1,39 @@ +/** A module to reason about the compiler used to compile translation units. */ + +import cpp +import codingstandards.cpp.Scope + +newtype Compiler = + Gcc() or + Clang() or + UnsupportedCompiler() + +/** Get the match pattern to detect the compiler being mimicked by the extractor to determine the compiler used to compile a file. */ +string getMimicMatch(Compiler compiler) { + result = ["%gcc", "%g++"] and compiler instanceof Gcc + or + result = ["%clang", "%clang++"] and compiler instanceof Clang +} + +/** Get the compiler used to compile the translation unit the file `f` is part of. */ +Compiler getCompiler(File f) { + exists(Compilation compilation, TranslationUnit translationUnit | + compilation.getAFileCompiled() = translationUnit and + (f = translationUnit or f = translationUnit.getAUserFile()) + | + if exists(int mimicIndex | compilation.getArgument(mimicIndex) = "--mimic") + then + exists(int mimicIndex | + compilation.getArgument(mimicIndex) = "--mimic" and + ( + compilation.getArgument(mimicIndex + 1).matches(getMimicMatch(result)) + or + forall(string match | match = getMimicMatch(_) | + not compilation.getArgument(mimicIndex + 1).matches(match) + ) and + result = UnsupportedCompiler() + ) + ) + else result = UnsupportedCompiler() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/Concurrency.qll b/cpp/common/src/codingstandards/cpp/Concurrency.qll index a793a3d317..3f7e5c1af9 100644 --- a/cpp/common/src/codingstandards/cpp/Concurrency.qll +++ b/cpp/common/src/codingstandards/cpp/Concurrency.qll @@ -1,878 +1,14 @@ import cpp -import semmle.code.cpp.dataflow.TaintTracking - -/** - * Models CFG nodes which should be added to a thread context. - */ -abstract class ThreadedCFGPathExtension extends ControlFlowNode { - /** - * Returns the next `ControlFlowNode` in this thread context. - */ - abstract ControlFlowNode getNext(); -} - -/** - * Models a `FunctionCall` invoked from a threaded context. - */ -class ThreadContextFunctionCall extends FunctionCall, ThreadedCFGPathExtension { - override ControlFlowNode getNext() { getTarget().getEntryPoint() = result } -} - -/** - * Models a specialized `FunctionCall` that may create a thread. - */ -abstract class ThreadCreationFunction extends FunctionCall, ThreadedCFGPathExtension { - /** - * Returns the function that will be invoked. - */ - abstract Function getFunction(); -} - -/** - * Models a call to a thread constructor via `std::thread`. - */ -class ThreadConstructorCall extends ConstructorCall, ThreadCreationFunction { - Function f; - - ThreadConstructorCall() { - getTarget().getDeclaringType().hasQualifiedName("std", "thread") and - f = getArgument(0).(FunctionAccess).getTarget() - } - - /** - * Returns the function that will be invoked by this `std::thread`. - */ - override Function getFunction() { result = f } - - override ControlFlowNode getNext() { result = getFunction().getEntryPoint() } -} - -/** - * Models a call to a thread constructor via `thrd_create`. - */ -class C11ThreadCreateCall extends ThreadCreationFunction { - Function f; - - C11ThreadCreateCall() { - getTarget().getName() = "thrd_create" and - ( - f = getArgument(1).(FunctionAccess).getTarget() or - f = getArgument(1).(AddressOfExpr).getOperand().(FunctionAccess).getTarget() - ) - } - - /** - * Returns the function that will be invoked by this thread. - */ - override Function getFunction() { result = f } - - override ControlFlowNode getNext() { result = getFunction().getEntryPoint() } -} - -/** - * Common base class providing an interface into function call - * based mutex locks. - */ -abstract class MutexFunctionCall extends LockingOperation { - abstract predicate isRecursive(); - - abstract predicate isSpeculativeLock(); - - abstract predicate unlocks(MutexFunctionCall fc); -} - -/** - * Models calls to various mutex types found in CPP. - */ -class CPPMutexFunctionCall extends MutexFunctionCall { - VariableAccess var; - - CPPMutexFunctionCall() { - ( - // the non recursive kinds - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "mutex") or - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "timed_mutex") or - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "shared_timed_mutex") or - // the recursive ones - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "recursive_mutex") or - getTarget() - .(MemberFunction) - .getDeclaringType() - .hasQualifiedName("std", "recursive_timed_mutex") - ) and - var = getQualifier() - } - - /** - * Holds if this mutex is a recursive mutex. - */ - override predicate isRecursive() { - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "recursive_mutex") or - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "recursive_timed_mutex") - } - - /** - * Holds if this `CPPMutexFunctionCall` is a lock. - */ - override predicate isLock() { getTarget().getName() = "lock" } - - /** - * Holds if this `CPPMutexFunctionCall` is a speculative lock, defined as calling - * one of the speculative locking functions such as `try_lock`. - */ - override predicate isSpeculativeLock() { - getTarget().getName() in [ - "try_lock", "try_lock_for", "try_lock_until", "try_lock_shared_for", "try_lock_shared_until" - ] - } - - /** - * Returns the lock to which this `CPPMutexFunctionCall` refers to. - */ - override Variable getLock() { result = getQualifier().(VariableAccess).getTarget() } - - /** - * Returns the qualifier for this `CPPMutexFunctionCall`. - */ - override Expr getLockExpr() { result = var } - - /** - * Holds if this is a `unlock` and *may* unlock the previously locked `MutexFunctionCall`. - * This predicate does not check that the mutex is currently locked. - */ - override predicate unlocks(MutexFunctionCall fc) { - isUnlock() and - fc.getQualifier().(VariableAccess).getTarget() = getQualifier().(VariableAccess).getTarget() - } - - /** - * Holds if this is an unlock call. - */ - override predicate isUnlock() { getTarget().getName() = "unlock" } -} - -/** - * Models calls to various mutex types specialized to C code. - */ -class CMutexFunctionCall extends MutexFunctionCall { - Expr arg; - - CMutexFunctionCall() { - // the non recursive kinds - getTarget().getName() = ["mtx_lock", "mtx_unlock", "mtx_timedlock", "mtx_trylock"] and - arg = getArgument(0) - } - - /** - * Holds if this mutex is a recursive mutex. - */ - override predicate isRecursive() { none() } - - /** - * Holds if this `CMutexFunctionCall` is a lock. - */ - override predicate isLock() { - getTarget().getName() = ["mtx_lock", "mtx_timedlock", "mtx_trylock"] - } - - /** - * Holds if this `CMutexFunctionCall` is a speculative lock, defined as calling - * one of the speculative locking functions such as `try_lock`. - */ - override predicate isSpeculativeLock() { - getTarget().getName() in ["mtx_timedlock", "mtx_trylock"] - } - - /** - * Returns the `Variable` to which this `CMutexFunctionCall` refers to. For this - * style of lock it can reference a number of different variables. - */ - override Variable getLock() { - exists(VariableAccess va | - TaintTracking::localTaint(DataFlow::exprNode(va), DataFlow::exprNode(getLockExpr())) and - result = va.getTarget() - ) - } - - /** - * Returns the expression for this `CMutexFunctionCall`. - */ - override Expr getLockExpr() { result = arg } - - /** - * Holds if this is a `unlock` and *may* unlock the previously locked `CMutexFunctionCall`. - * This predicate does not check that the mutex is currently locked. - */ - override predicate unlocks(MutexFunctionCall fc) { - isUnlock() and - fc.getLock() = getLock() - } - - /** - * Holds if this is an unlock call. - */ - override predicate isUnlock() { getTarget().getName() = "mtx_unlock" } -} - -/** - * The thread-aware predecessor function is defined in terms of the thread aware - * successor function. This is because it is simpler to construct the forward - * paths of a thread's execution than the backwards paths. For this reason we - * require a `start` and `end` node. - * - * The logic of this function is that a thread aware predecessor is one that - * follows a `start` node, is not equal to the ending node, and does not follow - * the `end` node. Such nodes can only be predecessors of `end`. - * - * For this reason this function requires a `start` node from which to start - * considering something a predecessor of `end`. - */ -pragma[inline] -ControlFlowNode getAThreadContextAwarePredecessor(ControlFlowNode start, ControlFlowNode end) { - result = getAThreadContextAwareSuccessor(start) and - not result = getAThreadContextAwareSuccessor(end) and - not result = end -} - -/** - * A predicate for finding successors of `ControlFlowNode`s that are aware of - * the objects that my flow into a thread's context. This is achieved by adding - * additional edges to thread entry points and function calls. - */ -ControlFlowNode getAThreadContextAwareSuccessorR(ControlFlowNode cfn) { - result = cfn.getASuccessor() - or - result = cfn.(ThreadedCFGPathExtension).getNext() -} - -ControlFlowNode getAThreadContextAwareSuccessor(ControlFlowNode m) { - result = getAThreadContextAwareSuccessorR*(m) and - // for performance reasons we handle back edges by enforcing a lexical - // ordering restriction on these nodes if they are both in - // the same loop. One way of doing this is as follows: - // - // ````and ( - // exists(Loop loop | - // loop.getAChild*() = m and - // loop.getAChild*() = result - // ) - // implies - // not result.getLocation().isBefore(m.getLocation()) - // )``` - // In this implementation we opt for the more generic form below - // which seems to have reasonable performance. - ( - m.getEnclosingStmt().getParentStmt*() = result.getEnclosingStmt().getParentStmt*() - implies - not exists(Location l1, Location l2 | - l1 = result.getLocation() and - l2 = m.getLocation() - | - l1.getEndLine() < l2.getStartLine() - or - l1.getStartLine() = l2.getEndLine() and - l1.getEndColumn() < l2.getStartColumn() - ) - ) -} - -abstract class LockingOperation extends FunctionCall { - /** - * Returns the target of the lock underlying this RAII-style lock. - */ - abstract Variable getLock(); - - /** - * Returns the lock underlying this RAII-style lock. - */ - abstract Expr getLockExpr(); - - /** - * Holds if this is a lock operation - */ - abstract predicate isLock(); - - /** - * Holds if this is an unlock operation - */ - abstract predicate isUnlock(); -} - -/** - * Models a RAII-Style lock. - */ -class RAIIStyleLock extends LockingOperation { - VariableAccess lock; - Element e; - - RAIIStyleLock() { - ( - getTarget().getDeclaringType().hasQualifiedName("std", "lock_guard") or - getTarget().getDeclaringType().hasQualifiedName("std", "unique_lock") or - getTarget().getDeclaringType().hasQualifiedName("std", "scoped_lock") - ) - } - - /** - * Holds if this is a lock operation - */ - override predicate isLock() { - this instanceof ConstructorCall and - lock = getArgument(0).getAChild*() and - // defer_locks don't cause a lock - not exists(Expr exp | - exp = getArgument(1) and - exp.(VariableAccess) - .getTarget() - .getUnderlyingType() - .(Class) - .hasQualifiedName("std", "defer_lock_t") - ) - } - - /** - * Holds if this is an unlock operation - */ - override predicate isUnlock() { this instanceof DestructorCall } - - /** - * Returns the target of the lock underlying this RAII-style lock. - */ - override Variable getLock() { result = lock.getTarget() } - - /** - * Returns the lock underlying this RAII-style lock. - */ - override Expr getLockExpr() { result = lock } -} - -/** - * Models a function that may be executed by some thread. - */ -abstract class ThreadedFunction extends Function { } - -/** - * Models a function that may be executed by some thread via - * C++ standard classes. - */ -class CPPThreadedFunction extends ThreadedFunction { - CPPThreadedFunction() { exists(ThreadConstructorCall tcc | tcc.getFunction() = this) } -} - -/** - * Models a function that may be executed by some thread via - * C11 standard functions. - */ -class C11ThreadedFunction extends ThreadedFunction { - C11ThreadedFunction() { exists(C11ThreadCreateCall cc | cc.getFunction() = this) } -} - -/** - * Models a control flow node within a function that may be executed by some - * thread. - */ -class ThreadedCFN extends ControlFlowNode { - ThreadedCFN() { - exists(ThreadedFunction tf | this = getAThreadContextAwareSuccessor(tf.getEntryPoint())) - } -} - -/** - * Models a `ControlFlowNode` that is protected by some sort of lock. - */ -class LockProtectedControlFlowNode extends ThreadedCFN { - FunctionCall lockingFunction; - - LockProtectedControlFlowNode() { - exists(LockingOperation lock | - // there is a node that is a lock - lockingFunction = lock and - lock.isLock() and - // this node should be a successor of this lock - this = getAThreadContextAwareSuccessor(lock) and - // and there should not exist a predecessor of this - // node that is an unlock. Since we are doing thread context - // aware tracking it is easier to go forwards than backwards - // in constructing the call graph. Thus we can define predecessor - // in terms of a node that is a successor of the lock but NOT a - // successor of the current node. - not exists(ControlFlowNode unlock | - // it's an unlock - unlock = getAThreadContextAwarePredecessor(lock, this) and - unlock.(MutexFunctionCall).isUnlock() - // note that we don't check that it's the same lock -- this is left - // to the caller to enforce this condition. - ) and - (lock instanceof MutexFunctionCall implies not this.(MutexFunctionCall).isUnlock()) - ) - } - - /** - * The `MutexFunctionCall` holding the lock that locks this node. - */ - FunctionCall coveredByLock() { result = lockingFunction } - - /** - * The lock underlying this `LockProtectedControlFlowNode`. - */ - Variable getAProtectingLock() { result = lockingFunction.(LockingOperation).getLock() } -} - -/** - * Models a function that conditionally waits. - */ -abstract class ConditionalWait extends FunctionCall { } - -/** - * Models a function in CPP that will conditionally wait. - */ -class CPPConditionalWait extends ConditionalWait { - CPPConditionalWait() { - exists(MemberFunction mf | - mf = getTarget() and - mf.getDeclaringType().hasQualifiedName("std", "condition_variable") and - mf.getName() in ["wait", "wait_for", "wait_until"] - ) - } -} - -/** - * Models a function in C that will conditionally wait. - */ -class CConditionalWait extends ConditionalWait { - CConditionalWait() { getTarget().getName() in ["cnd_wait"] } -} - -/** - * Models a call to a `std::thread` constructor that depends on a mutex. - */ -class MutexDependentThreadConstructor extends ThreadConstructorCall { - Expr mutexExpr; - - MutexDependentThreadConstructor() { - mutexExpr = getAnArgument() and - mutexExpr.getUnderlyingType().stripType() instanceof MutexType - } - - Expr dependentMutex() { result = mutexExpr } -} - -/** - * Models thread waiting functions. - */ -abstract class ThreadWait extends FunctionCall { } - -/** - * Models a call to a `std::thread` join. - */ -class CPPThreadWait extends ThreadWait { - VariableAccess var; - - CPPThreadWait() { - getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "thread") and - getTarget().getName() = "join" - } -} - -/** - * Models a call to `thrd_join` in C11. - */ -class C11ThreadWait extends ThreadWait { - VariableAccess var; - - C11ThreadWait() { getTarget().getName() = "thrd_join" } -} - -/** - * Models thread detach functions. - */ -abstract class ThreadDetach extends FunctionCall { } - -/** - * Models a call to `thrd_detach` in C11. - */ -class C11ThreadDetach extends ThreadWait { - VariableAccess var; - - C11ThreadDetach() { getTarget().getName() = "thrd_detach" } -} - -abstract class MutexSource extends FunctionCall { } - -/** - * Models a C++ style mutex. - */ -class CPPMutexSource extends MutexSource, ConstructorCall { - CPPMutexSource() { getTarget().getDeclaringType().hasQualifiedName("std", "mutex") } -} - -/** - * Models a C11 style mutex. - */ -class C11MutexSource extends MutexSource, FunctionCall { - C11MutexSource() { getTarget().hasName("mtx_init") } -} - -/** - * Models a thread dependent mutex. A thread dependent mutex is a mutex - * that is used by a thread. This dependency is established either by directly - * passing in a mutex or by referencing a mutex that is in the local scope. The utility - * of this class is it captures the `DataFlow::Node` source at which the mutex - * came from. For example, if it is passed in from a local function to a thread. - * This functionality is critical, since it allows one to inspect how the thread - * behaves with respect to the owner of a resource. - * - * To model the myriad ways this can happen, the subclasses of this class are - * responsible for implementing the various usage patterns. - */ -abstract class ThreadDependentMutex extends DataFlow::Node { - DataFlow::Node sink; - - DataFlow::Node getASource() { - // the source is either the thing that declared - // the mutex - result = this - or - // or the thread we are using it in - result = getAThreadSource() - } - - /** - * Gets the dataflow nodes corresponding to thread local usages of the - * dependent mutex. - */ - DataFlow::Node getAThreadSource() { - // here we line up the actual parameter at the thread creation - // site with the formal parameter in the target thread. - // Note that there are differences between the C and C++ versions - // of the argument ordering in the thread creation function. However, - // since the C version only takes one parameter (as opposed to multiple) - // we can simplify this search by considering only the first argument. - exists(FunctionCall fc, Function f, int n | - // Get the argument to which the mutex flowed. - fc.getArgument(n) = sink.asExpr() and - // Get the thread function we are calling. - f = fc.getArgument(0).(FunctionAccess).getTarget() and - // in C++, there is an extra argument to the `std::thread` call - // so we must subtract 1 since this is not passed to the thread. - ( - result = DataFlow::exprNode(f.getParameter(n - 1).getAnAccess()) - or - // In C, only one argument is allowed. Thus IF the flow predicate holds, - // it will be to the first argument - result = DataFlow::exprNode(f.getParameter(0).getAnAccess()) - ) - ) - } - - /** - * Produces the set of dataflow nodes to thread creation for threads - * that are dependent on this mutex. - */ - DataFlow::Node getADependentThreadCreationExpr() { - exists(FunctionCall fc | - fc.getAnArgument() = sink.asExpr() and - result = DataFlow::exprNode(fc) - ) - } - - /** - * Gets a set of usages of this mutex in both the local and thread scope. - * In the case of scoped usage, this also captures typical accesses of variables. - */ - DataFlow::Node getAUsage() { TaintTracking::localTaint(getASource(), result) } -} - -/** - * This class models the type of thread/mutex dependency that is established - * through the typical parameter passing mechanisms found in C++. - */ -class FlowBasedThreadDependentMutex extends ThreadDependentMutex { - FlowBasedThreadDependentMutex() { - // some sort of dataflow, likely through parameter passing. - exists(ThreadDependentMutexTaintTrackingConfiguration config | config.hasFlow(this, sink)) - } -} - -/** - * This class models the type of thread/mutex dependency that is established by - * either scope based accesses (e.g., global variables) or block scope differences. - */ -class AccessBasedThreadDependentMutex extends ThreadDependentMutex { - Variable variableSource; - - AccessBasedThreadDependentMutex() { - // encapsulates usages from outside scopes not directly expressed - // in dataflow. - exists(MutexSource mutexSrc, ThreadedFunction f | - DataFlow::exprNode(mutexSrc) = this and - // find a variable that was assigned the mutex - TaintTracking::localTaint(DataFlow::exprNode(mutexSrc), - DataFlow::exprNode(variableSource.getAnAssignedValue())) and - // find all subsequent accesses of that variable that are within a - // function and set those to the sink - exists(VariableAccess va | - va = variableSource.getAnAccess() and - va.getEnclosingFunction() = f and - sink = DataFlow::exprNode(va) - ) - ) - } - - override DataFlow::Node getAUsage() { DataFlow::exprNode(variableSource.getAnAccess()) = result } -} - -/** - * In the typical C thread model, a mutex is a created by a function that is not responsible - * for creating the variable. Thus this class encodes a slightly different semantics - * wherein the usage pattern is that of variables that have been both initialized - * and then subsequently passed into a thread directly. - */ -class DeclarationInitBasedThreadDependentMutex extends ThreadDependentMutex { - Variable variableSource; - - DeclarationInitBasedThreadDependentMutex() { - exists(MutexSource ms, ThreadCreationFunction tcf | - this = DataFlow::exprNode(ms) and - // accessed as a mutex source - TaintTracking::localTaint(DataFlow::exprNode(variableSource.getAnAccess()), - DataFlow::exprNode(ms.getAnArgument())) and - // subsequently passed to a thread creation function (order not strictly - // enforced for performance reasons) - sink = DataFlow::exprNode(tcf.getAnArgument()) and - TaintTracking::localTaint(DataFlow::exprNode(variableSource.getAnAccess()), sink) - ) - } - - override DataFlow::Node getAUsage() { - TaintTracking::localTaint(getASource(), result) or - DataFlow::exprNode(variableSource.getAnAccess()) = result - } - - override DataFlow::Node getASource() { - // the source is either the thing that declared - // the mutex - result = this - or - // or the thread we are using it in - result = getAThreadSource() - } - - DataFlow::Node getSink() { result = sink } - - /** - * Gets the dataflow nodes corresponding to thread local usages of the - * dependent mutex. - */ - override DataFlow::Node getAThreadSource() { - // here we line up the actual parameter at the thread creation - // site with the formal parameter in the target thread. - // Note that there are differences between the C and C++ versions - // of the argument ordering in the thread creation function. However, - // since the C version only takes one parameter (as opposed to multiple) - // we can simplify this search by considering only the first argument. - exists( - FunctionCall fc, Function f, int n // CPP Version - | - fc.getArgument(n) = sink.asExpr() and - f = fc.getArgument(0).(FunctionAccess).getTarget() and - // in C++, there is an extra argument to the `std::thread` call - // so we must subtract 1 since this is not passed to the thread. - result = DataFlow::exprNode(f.getParameter(n - 1).getAnAccess()) - ) - or - exists( - FunctionCall fc, Function f // C Version - | - fc.getAnArgument() = sink.asExpr() and - // in C, the second argument is the function - f = fc.getArgument(1).(FunctionAccess).getTarget() and - // in C, the passed argument is always the zeroth argument - result = DataFlow::exprNode(f.getParameter(0).getAnAccess()) - ) - } -} - -/** - * In the typical C model, another way to use mutexes is to work with global variables - * that can be initialized at various points -- one of which must be inside a thread. - * This class encapsulates this pattern. - */ -class DeclarationInitAccessBasedThreadDependentMutex extends ThreadDependentMutex { - Variable variableSource; - - DeclarationInitAccessBasedThreadDependentMutex() { - exists(MutexSource ms, ThreadedFunction tf, VariableAccess va | - this = DataFlow::exprNode(ms) and - // accessed as a mutex source - TaintTracking::localTaint(DataFlow::exprNode(variableSource.getAnAccess()), - DataFlow::exprNode(ms.getAnArgument())) and - // is accessed somewhere else - va = variableSource.getAnAccess() and - sink = DataFlow::exprNode(va) and - // one of which must be a thread - va.getEnclosingFunction() = tf - ) - } - - override DataFlow::Node getAUsage() { result = DataFlow::exprNode(variableSource.getAnAccess()) } -} - -class ThreadDependentMutexTaintTrackingConfiguration extends TaintTracking::Configuration { - ThreadDependentMutexTaintTrackingConfiguration() { - this = "ThreadDependentMutexTaintTrackingConfiguration" - } - - override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof MutexSource } - - override predicate isSink(DataFlow::Node node) { - exists(ThreadCreationFunction f | f.getAnArgument() = node.asExpr()) - } -} - -/** - * Models expressions that destroy mutexes. - */ -abstract class MutexDestroyer extends StmtParent { - /** - * Gets the expression that references the mutex being destroyed. - */ - abstract Expr getMutexExpr(); -} - -/** - * Models C style mutex destruction via `mtx_destroy`. - */ -class C11MutexDestroyer extends MutexDestroyer, FunctionCall { - C11MutexDestroyer() { getTarget().getName() = "mtx_destroy" } - - /** - * Returns the `Expr` being destroyed. - */ - override Expr getMutexExpr() { result = getArgument(0) } -} - -/** - * Models a delete expression -- note it is necessary to add this in - * addition to destructors to handle certain implementations of the - * standard library which obscure the destructors of mutexes. - */ -class DeleteMutexDestroyer extends MutexDestroyer { - DeleteMutexDestroyer() { this instanceof DeleteExpr } - - override Expr getMutexExpr() { this.(DeleteExpr).getExpr() = result } -} - -/** - * Models a possible mutex variable that if it goes - * out of scope would destroy an underlying mutex. - */ -class LocalMutexDestroyer extends MutexDestroyer { - Expr assignedValue; - - LocalMutexDestroyer() { - exists(LocalVariable lv | - // static types aren't destroyers - not lv.isStatic() and - // neither are pointers - not lv.getType() instanceof PointerType and - lv.getAnAssignedValue() = assignedValue and - // map the location to the return statements of the - // enclosing function - exists(ReturnStmt rs | - rs.getEnclosingFunction() = assignedValue.getEnclosingFunction() and - rs = this - ) - ) - } - - override Expr getMutexExpr() { result = assignedValue } -} - -/** - * Models implicit or explicit calls to the destructor of a mutex, either via - * a `delete` statement or a variable going out of scope. - */ -class DestructorMutexDestroyer extends MutexDestroyer, DestructorCall { - DestructorMutexDestroyer() { getTarget().getDeclaringType().hasQualifiedName("std", "mutex") } - - /** - * Returns the `Expr` being deleted. - */ - override Expr getMutexExpr() { getQualifier() = result } -} - -/** - * Models a conditional variable denoted by `std::condition_variable`. - */ -class ConditionalVariable extends Variable { - ConditionalVariable() { - getUnderlyingType().(Class).hasQualifiedName("std", "condition_variable") - } -} - -/** - * Models a conditional function, which is a function that depends on the value - * of a conditional variable. - */ -class ConditionalFunction extends Function { - ConditionalFunction() { - exists(ConditionalVariable cv | cv.getAnAccess().getEnclosingFunction() = this) - } -} - -/** - * Models calls to thread specific storage function calls. - */ -abstract class ThreadSpecificStorageFunctionCall extends FunctionCall { - /** - * Gets the key to which this call references. - */ - Expr getKey() { getArgument(0) = result } -} - -/** - * Models calls to `tss_get`. - */ -class TSSGetFunctionCall extends ThreadSpecificStorageFunctionCall { - TSSGetFunctionCall() { getTarget().getName() = "tss_get" } -} - -/** - * Models calls to `tss_set`. - */ -class TSSSetFunctionCall extends ThreadSpecificStorageFunctionCall { - TSSSetFunctionCall() { getTarget().getName() = "tss_set" } -} - -/** - * Models calls to `tss_create` - */ -class TSSCreateFunctionCall extends ThreadSpecificStorageFunctionCall { - TSSCreateFunctionCall() { getTarget().getName() = "tss_create" } - - predicate hasDeallocator() { - not exists(MacroInvocation mi, NULLMacro nm | - getArgument(1) = mi.getExpr() and - mi = nm.getAnInvocation() - ) - } -} - -/** - * Models calls to `tss_delete` - */ -class TSSDeleteFunctionCall extends ThreadSpecificStorageFunctionCall { - TSSDeleteFunctionCall() { getTarget().getName() = "tss_delete" } -} - -/** - * Gets a call to `DeallocationExpr` that deallocates memory owned by thread specific - * storage. - */ -predicate getAThreadSpecificStorageDeallocationCall(C11ThreadCreateCall tcc, DeallocationExpr dexp) { - exists(TSSGetFunctionCall tsg | - tcc.getFunction().getEntryPoint().getASuccessor*() = tsg and - DataFlow::localFlow(DataFlow::exprNode(tsg), DataFlow::exprNode(dexp.getFreedExpr())) - ) -} +import codingstandards.cpp.concurrency.Atomic +import codingstandards.cpp.concurrency.CConditionOperation +import codingstandards.cpp.concurrency.ControlFlow +import codingstandards.cpp.concurrency.ConditionalWait +import codingstandards.cpp.concurrency.LockingOperation +import codingstandards.cpp.concurrency.LockProtectedControlFlow +import codingstandards.cpp.concurrency.MutexDestroyer +import codingstandards.cpp.concurrency.ThreadCreation +import codingstandards.cpp.concurrency.ThreadedFunction +import codingstandards.cpp.concurrency.ThreadDependentMutex +import codingstandards.cpp.concurrency.ThreadSpecificStorage +import codingstandards.cpp.concurrency.ThreadWaitDetach +import codingstandards.cpp.concurrency.Types diff --git a/cpp/common/src/codingstandards/cpp/Config.qll b/cpp/common/src/codingstandards/cpp/Config.qll new file mode 100644 index 0000000000..5ce3f7a035 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Config.qll @@ -0,0 +1,32 @@ +/** + * A module for runtime configuration settings specified in a `conding-standards.yml` file. + */ + +import cpp +import semmle.code.cpp.XML +import codingstandards.cpp.exclusions.RuleMetadata +import codingstandards.cpp.deviations.Deviations + +/** A `coding-standards.xml` configuration file (usually generated from an YAML configuration file). */ +class CodingStandardsFile extends XmlFile { + CodingStandardsFile() { + this.getBaseName() = "coding-standards.xml" and + // Must be within the users source code. + exists(this.getRelativePath()) + } +} + +class CodingStandardsConfigSection extends XmlElement { + CodingStandardsConfigSection() { getParent() instanceof CodingStandardsConfig } +} + +/** A "Coding Standards" configuration file */ +class CodingStandardsConfig extends XmlElement { + CodingStandardsConfig() { + any(CodingStandardsFile csf).getARootElement() = this and + this.getName() = "codingstandards" + } + + /** Get a section in this configuration file. */ + CodingStandardsConfigSection getASection() { result.getParent() = this } +} diff --git a/cpp/common/src/codingstandards/cpp/ConstHelpers.qll b/cpp/common/src/codingstandards/cpp/ConstHelpers.qll index a7457dc845..a3d12fd127 100644 --- a/cpp/common/src/codingstandards/cpp/ConstHelpers.qll +++ b/cpp/common/src/codingstandards/cpp/ConstHelpers.qll @@ -4,7 +4,7 @@ import cpp import codingstandards.cpp.SideEffect -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.FunctionParameter /** A variable that can be modified (both the pointer and object pointed to if pointer type) */ diff --git a/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll b/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll index afc8cb07a3..ca3a7fb251 100644 --- a/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll +++ b/cpp/common/src/codingstandards/cpp/Cpp14Literal.qll @@ -9,6 +9,9 @@ module Cpp14Literal { /** An numeric literal. */ abstract class NumericLiteral extends StandardLibrary::Literal { } + /** Convenience for implementing class `UnrecognizedNumericLiteral` */ + abstract private class RecognizedNumericLiteral extends StandardLibrary::Literal { } + /** An integer literal. */ abstract class IntegerLiteral extends NumericLiteral { predicate isSigned() { not isUnsigned() } @@ -23,8 +26,8 @@ module Cpp14Literal { * ``` * Octal literals must always start with the digit `0`. */ - class OctalLiteral extends IntegerLiteral { - OctalLiteral() { getValueText().regexpMatch("\\s*0[0-7']+[uUlL]*\\s*") } + class OctalLiteral extends IntegerLiteral, RecognizedNumericLiteral { + OctalLiteral() { getValueText().regexpMatch("\\s*0[0-7']*[uUlL]*\\s*") } override string getAPrimaryQlClass() { result = "OctalLiteral" } } @@ -35,7 +38,7 @@ module Cpp14Literal { * unsigned int32_t minus2 = 0xfffffffe; * ``` */ - class HexLiteral extends IntegerLiteral { + class HexLiteral extends IntegerLiteral, RecognizedNumericLiteral { HexLiteral() { getValueText().regexpMatch("\\s*0[xX][0-9a-fA-F']+[uUlL]*\\s*") } override string getAPrimaryQlClass() { result = "HexLiteral" } @@ -47,7 +50,7 @@ module Cpp14Literal { * unsigned int32_t binary = 0b101010; * ``` */ - class BinaryLiteral extends IntegerLiteral { + class BinaryLiteral extends IntegerLiteral, RecognizedNumericLiteral { BinaryLiteral() { getValueText().regexpMatch("\\s*0[bB][0-1']*[uUlL]*\\s*") } override string getAPrimaryQlClass() { result = "BinaryLiteral" } @@ -59,7 +62,7 @@ module Cpp14Literal { * unsigned int32_t decimal = 10340923; * ``` */ - class DecimalLiteral extends IntegerLiteral { + class DecimalLiteral extends IntegerLiteral, RecognizedNumericLiteral { DecimalLiteral() { getValueText().regexpMatch("\\s*[1-9][0-9']*[uUlL]*\\s*") } override string getAPrimaryQlClass() { result = "DecimalLiteral" } @@ -71,7 +74,7 @@ module Cpp14Literal { * double floating = 1.340923e-19; * ``` */ - class FloatingLiteral extends NumericLiteral { + class FloatingLiteral extends NumericLiteral, RecognizedNumericLiteral { FloatingLiteral() { getValueText().regexpMatch("\\s*[0-9][0-9']*(\\.[0-9']+)?([eE][\\+\\-]?[0-9']+)?[flFL]?\\s*") and // A decimal literal takes precedent @@ -82,4 +85,41 @@ module Cpp14Literal { override string getAPrimaryQlClass() { result = "FloatingLiteral" } } + + /** + * Literal values with conversions and macros cannot always be trivially + * parsed from `Literal.getValueText()`, and have loss of required + * information in `Literal.getValue()`. This class covers cases that appear + * to be `NumericLiteral`s but cannot be determined to be a hex, decimal, + * octal, binary, or float literal, but still are parsed as a Literal with a + * number value. + */ + class UnrecognizedNumericLiteral extends NumericLiteral { + UnrecognizedNumericLiteral() { + this.getValue().regexpMatch("[0-9.e]+") and + not this instanceof RecognizedNumericLiteral + } + } + + /** + * A character literal. For example: + * ``` + * char c1 = 'a'; + * char16_t c2 = u'a'; + * char32_t c3 = U'a'; + * wchar_t c4 = L'b'; + * ``` + */ + class CharLiteral extends StandardLibrary::TextLiteral { + CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*(L|u|U)?'.*") } + + override string getAPrimaryQlClass() { result = "CharLiteral" } + + /** + * Gets the character of this literal. For example `L'a'` has character `"a"`. + */ + string getCharacter() { + result = this.getValueText().regexpCapture("(?s)\\s*(L|u|U)?'(.*)'", 1) + } + } } diff --git a/cpp/common/src/codingstandards/cpp/Dereferenced.qll b/cpp/common/src/codingstandards/cpp/Dereferenced.qll index ffcd74728b..ba79ec3f1e 100644 --- a/cpp/common/src/codingstandards/cpp/Dereferenced.qll +++ b/cpp/common/src/codingstandards/cpp/Dereferenced.qll @@ -45,9 +45,13 @@ class BasicStringMemberFunctionDereferencedExpr extends BasicStringDereferencedE | // basic_string::basic_string(const charT *, const Allocator &) f instanceof Constructor and - f.getNumberOfParameters() = 2 and + f.getNumberOfParameters() <= 2 and f.getParameter(0).getType() = stringType.getConstCharTPointer() and - f.getParameter(1).getType() = stringType.getConstAllocatorReferenceType() and + ( + f.getNumberOfParameters() = 2 + implies + f.getParameter(1).getType() = stringType.getConstAllocatorReferenceType() + ) and this = fc.getArgument(0) or // basic_string &basic_string::append(const charT *) diff --git a/cpp/common/src/codingstandards/cpp/Emergent.qll b/cpp/common/src/codingstandards/cpp/Emergent.qll new file mode 100644 index 0000000000..30f1df58e4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Emergent.qll @@ -0,0 +1,15 @@ +import cpp + +/** + * Namespace for containing emergent language features in C11. + */ +module C11 { + abstract class EmergentLanguageFeature extends Element { } + + class LibExt1Macro extends EmergentLanguageFeature, Macro { + LibExt1Macro() { + getName() = "__STDC_WANT_LIB_EXT1__" and + getBody() = "1" + } + } +} diff --git a/cpp/common/src/codingstandards/cpp/EncapsulatingFunctions.qll b/cpp/common/src/codingstandards/cpp/EncapsulatingFunctions.qll index d8d9739033..559c04ce98 100644 --- a/cpp/common/src/codingstandards/cpp/EncapsulatingFunctions.qll +++ b/cpp/common/src/codingstandards/cpp/EncapsulatingFunctions.qll @@ -3,6 +3,7 @@ */ import cpp +import codingstandards.cpp.Class /** A function which represents the entry point into a specific thread of execution in the program. */ abstract class MainLikeFunction extends Function { } @@ -14,7 +15,38 @@ abstract class EncapsulatingFunction extends Function { } class MainFunction extends MainLikeFunction { MainFunction() { hasGlobalName("main") and - getType() instanceof IntType + getType().resolveTypedefs() instanceof IntType + } +} + +/** + * A test function from the GoogleTest infrastructure. + * + * Such functions can be treated as valid EntryPoint functions during analysis + * of "called" or "unused" functions. It is not straightforward to identify + * such functions, however, they have certain features that can be used for + * identification. This can be refined based on experiments/real-world use. + */ +class GoogleTestFunction extends MainLikeFunction { + GoogleTestFunction() { + // A GoogleTest function is named "TestBody" and + ( + this.hasName("TestBody") or + this instanceof SpecialMemberFunction + ) and + // it's parent class inherits a base class + exists(Class base | + base = this.getEnclosingAccessHolder+().(Class).getABaseClass+() and + ( + // with a name "Test" inside a namespace called "testing" + base.hasName("Test") and + base.getNamespace().hasName("testing") + or + // or at a location in a file called gtest.h (or gtest-internal.h, + // gtest-typed-test.h etc). + base.getDefinitionLocation().getFile().getBaseName().regexpMatch("gtest*.h") + ) + ) } } diff --git a/cpp/common/src/codingstandards/cpp/Exclusions.qll b/cpp/common/src/codingstandards/cpp/Exclusions.qll index af4b8521d4..e6a477b220 100644 --- a/cpp/common/src/codingstandards/cpp/Exclusions.qll +++ b/cpp/common/src/codingstandards/cpp/Exclusions.qll @@ -14,42 +14,39 @@ private class ExcludeOutsideSourceLocation extends ExcludedFile { ExcludeOutsideSourceLocation() { not exists(getRelativePath()) } } -/** Holds if the element should be excluded. */ -predicate isExcluded(Element e) { - e instanceof ExcludedElement - or - e.getFile() instanceof ExcludedFile - or - // Compiler generated - not exists(e.getFile()) -} +bindingset[e, query] +predicate isExcluded(Element e, Query query) { isExcluded(e, query, _) } bindingset[e, query] -predicate isExcluded(Element e, Query query) { - e instanceof ExcludedElement +predicate isExcluded(Element e, Query query, string reason) { + e instanceof ExcludedElement and reason = "Element is an excluded element." or - e.getFile() instanceof ExcludedFile + e.getFile() instanceof ExcludedFile and reason = "Element is part of an excluded file." or - not exists(e.getFile()) + not exists(e.getFile()) and reason = "Element is not part of the source repository." or - // There exists a `DeviationRecord` that applies to this element and query + // There exists a `DeviationRecord` that applies to this element and query, and the query's effective category permits deviation. + query.getEffectiveCategory().permitsDeviation() and exists(DeviationRecord dr | applyDeviationsAtQueryLevel() | // The element is in a file which has a deviation for this query exists(string path | dr.isDeviated(query, path) and e.getFile().getRelativePath().prefix(path.length()) = path - ) + ) and + reason = "Query has an associated deviation record for the element's file." or - // The element is on the same line as a suppression comment - exists(Comment c | - c = dr.getACodeIdentifierComment() and - query = dr.getQuery() - | - exists(string filepath, int endLine | - // Comment occurs on the same line as the end line of the element - e.getLocation().hasLocationInfo(filepath, _, _, endLine, _) and - c.getLocation().hasLocationInfo(filepath, endLine, _, _, _) - ) + // The element is annotated by a code identifier that deviates this rule + exists(CodeIdentifierDeviation deviationInCode | + dr.getQuery() = query and + deviationInCode = dr.getACodeIdentifierDeviation() and + deviationInCode.isElementMatching(e) and + reason = + "Query has an associated deviation record with a code identifier that is applied to the element." ) ) + or + // The effective category of the query is 'Disapplied'. + // This can occur when a Guideline Recategorization Plan is applied. + query.getEffectiveCategory().isDisapplied() and + reason = "The query is disapplied." } diff --git a/cpp/common/src/codingstandards/cpp/Expr.qll b/cpp/common/src/codingstandards/cpp/Expr.qll index f5ece8b76a..54ba86c5b7 100644 --- a/cpp/common/src/codingstandards/cpp/Expr.qll +++ b/cpp/common/src/codingstandards/cpp/Expr.qll @@ -1,7 +1,17 @@ import cpp -private import semmle.code.cpp.ir.internal.ASTValueNumbering +private import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.valuenumbering.GlobalValueNumbering import codingstandards.cpp.AccessPath +/** + * A unary or binary arithmetic operation. + */ +class ArithmeticOperation extends Operation { + ArithmeticOperation() { + this instanceof UnaryArithmeticOperation or this instanceof BinaryArithmeticOperation + } +} + /** A full expression as defined in [intro.execution] of N3797. */ class FullExpr extends Expr { FullExpr() { @@ -139,9 +149,9 @@ module MisraExpr { private predicate isCValue(Expr e) { not e.isConstant() and ( - exists(ReturnStmt return | e = return.getExpr()) + exists(ReturnStmt return | e = return.getExpr().getExplicitlyConverted()) or - exists(Call call | e = call.getAnArgument()) + exists(FunctionCall call | e = call.getAnArgument().getExplicitlyConverted()) ) or isCValue(e.(ParenthesisExpr).getExpr()) @@ -180,3 +190,108 @@ module MisraExpr { CValue() { isCValue(this) } } } + +/** + * An optimized set of expressions used to determine the flow through constexpr variables. + */ +class VariableAccessOrCallOrLiteral extends Expr { + VariableAccessOrCallOrLiteral() { + this instanceof VariableAccess and this.(VariableAccess).getTarget().isConstexpr() + or + this instanceof Call + or + this instanceof Literal + } +} + +/** + * Holds if the value of source flows through compile time evaluated variables to target. + */ +predicate flowsThroughConstExprVariables( + VariableAccessOrCallOrLiteral source, VariableAccessOrCallOrLiteral target +) { + ( + source = target + or + source != target and + exists(SsaDefinition intermediateDef, StackVariable intermediate | + intermediateDef.getAVariable().getFunction() = source.getEnclosingFunction() and + intermediateDef.getAVariable().getFunction() = target.getEnclosingFunction() and + intermediateDef.getAVariable() = intermediate and + intermediate.isConstexpr() + | + DataFlow::localExprFlow(source, intermediateDef.getDefiningValue(intermediate)) and + flowsThroughConstExprVariables(intermediateDef.getAUse(intermediate), target) + ) + ) +} + +predicate isCompileTimeEvaluatedExpression(Expr expression) { + forall(DataFlow::Node ultimateSource, DataFlow::Node source | + source = DataFlow::exprNode(expression) and + DataFlow::localFlow(ultimateSource, source) and + not DataFlow::localFlowStep(_, ultimateSource) + | + isDirectCompileTimeEvaluatedExpression(ultimateSource.asExpr()) and + // If the ultimate source is not the same as the source, then it must flow through + // constexpr variables. + ( + ultimateSource != source + implies + flowsThroughConstExprVariables(ultimateSource.asExpr(), source.asExpr()) + ) + ) +} + +predicate isDirectCompileTimeEvaluatedExpression(Expr expression) { + expression instanceof Literal + or + any(Call c | isCompileTimeEvaluatedCall(c)) = expression +} + +/* + * Returns true if the given call may be evaluated at compile time and is compile time evaluated because + * all its arguments are compile time evaluated and its default values are compile time evaluated. + */ + +predicate isCompileTimeEvaluatedCall(Call call) { + // 1. The call may be evaluated at compile time, because it is constexpr, and + call.getTarget().isConstexpr() and + // 2. all its arguments are compile time evaluated, and + forall(Expr argSource | argSource = call.getAnArgument() | + isCompileTimeEvaluatedExpression(argSource) + ) and + // 3. all the default values used are compile time evaluated. + forall(Expr defaultValue, Parameter parameterUsingDefaultValue, int idx | + parameterUsingDefaultValue = call.getTarget().getParameter(idx) and + not exists(call.getArgument(idx)) and + parameterUsingDefaultValue.getAnAssignedValue() = defaultValue + | + isDirectCompileTimeEvaluatedExpression(defaultValue) + ) and + // 4. the call's qualifier is compile time evaluated. + (not call.hasQualifier() or isCompileTimeEvaluatedExpression(call.getQualifier())) +} + +/* + * an operator that does not evaluate its operand + */ + +class UnevaluatedExprExtension extends Expr { + UnevaluatedExprExtension() { + this.getAChild().isUnevaluated() + or + exists(FunctionCall declval | + declval.getTarget().hasQualifiedName("std", "declval") and + declval.getAChild() = this + ) + } +} + +/** A class representing left and right bitwise shift operations. */ +class BitShiftExpr extends BinaryBitwiseOperation { + BitShiftExpr() { + this instanceof LShiftExpr or + this instanceof RShiftExpr + } +} diff --git a/cpp/common/src/codingstandards/cpp/Extensions.qll b/cpp/common/src/codingstandards/cpp/Extensions.qll new file mode 100644 index 0000000000..5ca6cea4f6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Extensions.qll @@ -0,0 +1,11 @@ +import cpp + +/** + * Common base class for modeling compiler extensions. + */ +abstract class CompilerExtension extends Locatable { } + +/** + * Common base class for modeling compiler extensions in CPP. + */ +abstract class CPPCompilerExtension extends CompilerExtension { } diff --git a/cpp/common/src/codingstandards/cpp/FgetsErrorManagement.qll b/cpp/common/src/codingstandards/cpp/FgetsErrorManagement.qll index 4f99b02e2e..026fd93045 100644 --- a/cpp/common/src/codingstandards/cpp/FgetsErrorManagement.qll +++ b/cpp/common/src/codingstandards/cpp/FgetsErrorManagement.qll @@ -4,7 +4,7 @@ */ import cpp -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.controlflow.Guards /* diff --git a/cpp/common/src/codingstandards/cpp/FloatingPoint.qll b/cpp/common/src/codingstandards/cpp/FloatingPoint.qll new file mode 100644 index 0000000000..e2a13bd62e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/FloatingPoint.qll @@ -0,0 +1,413 @@ +import codeql.util.Boolean +import codingstandards.cpp.RestrictedRangeAnalysis +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis as SimpleRangeAnalysis + +predicate exprMayEqualZero(Expr e) { + RestrictedRangeAnalysis::upperBound(e) >= 0 and + RestrictedRangeAnalysis::lowerBound(e) <= 0 and + not guardedNotEqualZero(e) +} + +newtype TFPClassification = + TFinite() or + TNaN() or + TInfinite() + +class FPClassification extends TFPClassification { + string toString() { + this = TFinite() and + result = "finite" + or + this = TNaN() and + result = "NaN" + or + this = TInfinite() and + result = "infinite" + } +} + +newtype TFPClassificationConstraint = + /* The value may be infinite, NaN, or finite. */ + TUnclassified() or + /** + * The value must be one of: infinite, NaN, or finite. + * + * If strict is `true` then this inverts naively. For example, `!isfinite(x)` means `x` must not + * be finite. However, `!iszero(x)` is true for some finite values, and inverts to + * `TUnclassified`. + */ + TExactFPClassification(TFPClassification cls, Boolean strict) or + /* The value must not be one of: infinite, NaN, or finite. */ + TExcludeFPClassification(TFPClassification cls1) + +class FPClassificationConstraint extends TFPClassificationConstraint { + string toString() { + this = TUnclassified() and + result = "unclassified" + or + exists(FPClassification cls, Boolean strict | + this = TExactFPClassification(cls, strict) and + result = "must be " + cls.toString() + ", strict: " + strict.toString() + or + this = TExcludeFPClassification(cls) and + result = "must NOT be " + cls.toString() + ) + } + + /** + * Invert the constraint, for instance, "must be finite" becomes "must not be finite". + * + * Non-strict exact constraints are inverted to the unclassified constraint. For example, + * `iszero(x)` guarantees `x` to be finite, however, `!iszero(x)` can be true for all three + * classes of floating point values. + * + * The unclassified constraint inverts to itself. + */ + FPClassificationConstraint invert() { + // Unclassified inverts to itself. + this = TUnclassified() and result = this + or + exists(FPClassification cls | + // `!isfinite()` implies is infinite or NaN. + this = TExactFPClassification(cls, true) and + result = TExcludeFPClassification(cls) + or + // `!iszero()` implies nothing. + this = TExactFPClassification(cls, false) and + result = TUnclassified() + or + // For completeness: `!isfinite(x) ? ... : x` would imply `isfinite(x)`. + this = TExcludeFPClassification(cls) and + result = TExactFPClassification(cls, true) + ) + } + + /** + * Naively invert the constraint, for instance, "must be finite" becomes "must not be finite". + * + * Word of caution: inverting a guard condition does not necessarily invert the constraint. For + * example, `iszero(x)` guarantees `x` to be finite, however, `isnotzero(x)` does not guarantee + * `x` not to be finite. + * + * The unclassified constraint is not inverted. + */ + FPClassificationConstraint naiveInversion() { + this = TUnclassified() and result = this + or + exists(FPClassification cls | + this = TExactFPClassification(cls, _) and + result = TExcludeFPClassification(cls) + or + this = TExcludeFPClassification(cls) and + result = TExactFPClassification(cls, true) + ) + } + + predicate mustBe(FPClassification cls) { this = TExactFPClassification(cls, _) } + + predicate mustNotBe(FPClassification cls) { + this = TExcludeFPClassification(cls) + or + this = TExactFPClassification(_, _) and + not this = TExactFPClassification(cls, _) + } + + predicate mayBe(FPClassification cls) { not mustNotBe(cls) } +} + +/** + * The names of the functions or macros that classify floating point values. + * + * These names reflect a check that a value is finite, or infinite, or NaN. Finite and NaN checks + * are either strict (return true for all values in the class) or not (return true for some + * values). + * + * The infinite check is always strict, and specially, returns 1 or -1 for positive or negative + * infinity. + */ +newtype TFPClassifierName = + TClassifiesFinite(string name, boolean strict) { + strict = true and + name = ["finite" + ["", "l", "f"], "isfinite"] + or + strict = false and + name = ["isnormal", "issubnormal", "iszero"] + } or + TClassifiesNaN(string name, boolean strict) { + strict = true and + name = "isnan" + ["", "f", "l"] + or + strict = false and + name = "issignaling" + } or + TClassifiesInfinite(string name) { name = "isinf" + ["", "f", "l"] } + +class FPClassifierName extends TFPClassifierName { + string name; + boolean strict; + + FPClassifierName() { + this = TClassifiesFinite(name, strict) + or + this = TClassifiesInfinite(name) and + strict = true + or + this = TClassifiesNaN(name, strict) + } + + string toString() { result = name } + + /** The classification name, for instance, "isfinite". */ + string getName() { result = name } + + /** + * Whether the check holds for all values in the class, or only some. + * + * For instance, "isfinite" is strict because it returns true for all finite values, but + * "isnormal" is not as it returns false for some finite values. + */ + predicate isStrict() { strict = true } + + FPClassificationConstraint getConstraint() { + this = TClassifiesFinite(_, strict) and + result = TExactFPClassification(any(TFinite t), strict) + or + this = TClassifiesNaN(_, strict) and + result = TExactFPClassification(any(TNaN t), strict) + or + this = TClassifiesInfinite(_) and + // TODO: isinf() is special + result = TExactFPClassification(any(TInfinite t), false) + } +} + +/** + * An invocation of a classification function, for instance, "isfinite(x)", implemented as a macro. + */ +class FPClassifierMacroInvocation extends MacroInvocation { + FPClassifierName classifier; + + FPClassifierMacroInvocation() { getMacroName() = classifier.getName() } + + Expr getCheckedExpr() { + // Getting the checked expr in a cross-platform way is extroardinarily difficult, as glibc has + // multiple conditional implementations of the same macro. Assume the checked expr is a + // variable access so we can search optimistically like so: + exists(VariableAccess va | + va.getTarget().getName() = getExpandedArgument(0) and + va = getAnExpandedElement() and + result = va + ) + } + + /** + * The classification name, for instance, "isfinite". + */ + FPClassifierName getFPClassifierName() { result = classifier } +} + +/** + * A classification function, for instance, "isfinite", when implemented as a function. + */ +class FPClassifierFunction extends Function { + FPClassifierName classifier; + + FPClassifierFunction() { getName() = classifier.getName() } + + FPClassifierName getFPClassifierName() { result = classifier } +} + +class FPClassificationGuard instanceof GuardCondition { + Expr floatExpr; + Expr checkResultExpr; + FPClassifierName classifier; + + FPClassificationGuard() { + super.comparesEq(checkResultExpr, _, _, _) and + ( + exists(FPClassifierMacroInvocation m | + floatExpr = m.getCheckedExpr() and + checkResultExpr = m.getExpr() and + classifier = m.getFPClassifierName() + ) + or + exists(FunctionCall fc, FPClassifierFunction f | + fc.getTarget() = f and + floatExpr = fc.getArgument(0) and + checkResultExpr = fc and + classifier = f.getFPClassifierName() + ) + ) + } + + string toString() { + result = + classifier.toString() + " guard on " + floatExpr.toString() + " via " + + checkResultExpr.toString() + } + + predicate constrainsFPClass(Expr e, FPClassificationConstraint constraint, Boolean testIsTrue) { + floatExpr = e and + exists(BooleanValue value, boolean areEqual, int testResult, FPClassificationConstraint base | + super.comparesEq(checkResultExpr, testResult, areEqual, value) and + base = getBaseConstraint(areEqual, testResult) and + if value.getValue() = testIsTrue then constraint = base else constraint = base.invert() + ) + } + + // Helper predicate, gets base constraint assuming `classifier() == value` or `classifier != value`. + private FPClassificationConstraint getBaseConstraint(Boolean areEqual, int testResult) { + exists(FPClassificationConstraint base | + testResult = 0 and + exists(Boolean strict | + // Handle isfinite() != 0: + classifier = TClassifiesFinite(_, strict) and + base = TExactFPClassification(TFinite(), strict) + or + // Handle isNaN() != 0: + classifier = TClassifiesNaN(_, strict) and + base = TExactFPClassification(TNaN(), strict) + or + // Handle isinf() != 0, which matches for +/- infinity: + classifier = TClassifiesInfinite(_) and + base = TExactFPClassification(TInfinite(), true) + ) and + // Invert the base constraint in the case of `classifier() == 0` + if areEqual = false then result = base else result = base.invert() + or + // Handle isinf() == 1 or isInf() == -1, which matches for one of +/- infinity: + testResult = 1 and + classifier = TClassifiesInfinite(_) and + base = TExactFPClassification(TInfinite(), false) and + // Invert the base constraint in the case of `classifier() != 1` + if areEqual = true then result = base else result = base.invert() + // TODO: handle fpclassify() == FP_INFINITE, FP_NAN, FP_NORMAL, FP_ZERO, etc. + ) + } + + predicate controls(Expr e, boolean testIsTrue) { + exists(IRGuardCondition irg, IRBlock irb, Instruction eir, BooleanValue bval | + irg.getUnconvertedResultExpression() = this and + bval.getValue() = testIsTrue and + irg.valueControls(irb, bval) and + eir.getAst().(ControlFlowNode).getBasicBlock() = e.getBasicBlock() and + eir.getBlock() = irb + ) + } +} + +predicate guardedNotEqualZero(Expr e) { + /* Note Boolean cmpEq, false means cmpNeq */ + exists(Expr checked, GuardCondition guard, boolean cmpEq, BooleanValue value | + hashCons(checked) = hashCons(e) and + guard.controls(e.getBasicBlock(), cmpEq) and + value.getValue() = cmpEq and + guard.comparesEq(checked, 0, false, value) + ) + or + exists(Expr checked, Expr val, int valVal, GuardCondition guard, boolean cmpEq | + hashCons(checked) = hashCons(e) and + forex(float v | + v = [RestrictedRangeAnalysis::lowerBound(val), RestrictedRangeAnalysis::upperBound(val)] + | + valVal = v + ) and + guard.controls(e.getBasicBlock(), cmpEq) and + guard.comparesEq(checked, val, -valVal, false, cmpEq) + ) +} + +predicate guardedNotFPClass(Expr e, FPClassification cls) { + /* Note Boolean cmpEq, false means cmpNeq */ + exists( + Expr checked, FPClassificationGuard guard, FPClassificationConstraint constraint, boolean cmpEq + | + hashCons(checked) = hashCons(e) and + guard.controls(e, cmpEq) and + guard.constrainsFPClass(checked, constraint, cmpEq) and + constraint.mustNotBe(cls) and + not checked = e + ) +} + +predicate exprMayEqualInfinity(Expr e, Boolean positive) { + exists(float target | + positive = true and target = 1.0 / 0.0 + or + positive = false and target = -1.0 / 0.0 + | + RestrictedRangeAnalysis::upperBound(e.getUnconverted()) = target or + RestrictedRangeAnalysis::lowerBound(e.getUnconverted()) = target + ) and + not guardedNotFPClass(e, TInfinite()) and + not e.getType() instanceof IntegralType +} + +signature float upperBoundPredicate(Expr e); + +signature float lowerBoundPredicate(Expr e); + +signature predicate exprMayEqualZeroPredicate(Expr e); + +predicate exprMayEqualZeroNaive(Expr e) { e.getValue().toFloat() = 0 } + +/** + * Get the math function name variants for the given name, e.g., "acos" has variants "acos", + * "acosf", and "acosl". + */ +Function getMathVariants(string name) { result.hasGlobalOrStdName([name, name + "f", name + "l"]) } + +module DomainError< + upperBoundPredicate/1 ub, lowerBoundPredicate/1 lb, exprMayEqualZeroPredicate/1 mayEqualZero> +{ + predicate hasDomainError(FunctionCall fc, string description) { + exists(Function functionWithDomainError | fc.getTarget() = functionWithDomainError | + functionWithDomainError = [getMathVariants(["acos", "asin", "atanh"])] and + not ( + ub(fc.getArgument(0)) <= 1.0 and + lb(fc.getArgument(0)) >= -1.0 + ) and + description = + "the argument has a range " + lb(fc.getArgument(0)) + "..." + ub(fc.getArgument(0)) + + " which is outside the domain of this function (-1.0...1.0)" + or + functionWithDomainError = getMathVariants(["atan2", "pow"]) and + ( + mayEqualZero(fc.getArgument(0)) and + mayEqualZero(fc.getArgument(1)) and + description = "both arguments are equal to zero" + ) + or + functionWithDomainError = getMathVariants("pow") and + ( + ub(fc.getArgument(0)) < 0.0 and + ub(fc.getArgument(1)) < 0.0 and + description = "both arguments are less than zero" + ) + or + functionWithDomainError = getMathVariants("acosh") and + ub(fc.getArgument(0)) < 1.0 and + description = "argument is less than 1" + or + //pole error is the same as domain for logb and tgamma (but not ilogb - no pole error exists) + functionWithDomainError = getMathVariants(["ilogb", "logb", "tgamma"]) and + fc.getArgument(0).getValue().toFloat() = 0 and + description = "argument is equal to zero" + or + functionWithDomainError = getMathVariants(["log", "log10", "log2", "sqrt"]) and + ub(fc.getArgument(0)) < 0.0 and + description = "argument is negative" + or + functionWithDomainError = getMathVariants("log1p") and + ub(fc.getArgument(0)) < -1.0 and + description = "argument is less than 1" + or + functionWithDomainError = getMathVariants("fmod") and + fc.getArgument(1).getValue().toFloat() = 0 and + description = "y is 0" + ) + } +} + +import DomainError as RestrictedDomainError +import DomainError as SimpleDomainError diff --git a/cpp/common/src/codingstandards/cpp/Function.qll b/cpp/common/src/codingstandards/cpp/Function.qll new file mode 100644 index 0000000000..c96fcbd840 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Function.qll @@ -0,0 +1,10 @@ +/** A module to reason about functions, such as well-known functions. */ + +import cpp + +/** + * 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%") } +} diff --git a/cpp/common/src/codingstandards/cpp/Identifiers.qll b/cpp/common/src/codingstandards/cpp/Identifiers.qll new file mode 100644 index 0000000000..665acbeb6b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Identifiers.qll @@ -0,0 +1,46 @@ +import cpp +import codingstandards.cpp.Linkage + +class ExternalIdentifiers extends InterestingIdentifiers { + ExternalIdentifiers() { + hasExternalLinkage(this) and + getNamespace() instanceof GlobalNamespace + } + + string getSignificantName() { + //C99 states the first 31 characters of external identifiers are significant + //C90 states the first 6 characters of external identifiers are significant and case is not required to be significant + //C90 is not currently considered by this rule + result = this.getName().prefix(31) + } +} + +//Identifiers that are candidates for checking uniqueness +class InterestingIdentifiers extends Declaration { + InterestingIdentifiers() { + not this.isFromTemplateInstantiation(_) and + not this.isFromUninstantiatedTemplate(_) and + not this instanceof TemplateParameter and + not this.hasDeclaringType() and + not this instanceof Operator and + not this.hasName("main") and + exists(this.getADeclarationLocation()) + } + + //this definition of significant relies on the number of significant characters for a macro name (C99) + //this is used on macro name comparisons only + //not necessarily against other types of identifiers + string getSignificantNameComparedToMacro() { result = this.getName().prefix(63) } +} + +//Declarations that omit type - C90 compiler assumes int +predicate isDeclaredImplicit(Declaration d) { + d.hasSpecifier("implicit_int") and + exists(Type t | + (d.(Variable).getType() = t or d.(Function).getType() = t) and + // Exclude "short" or "long", as opposed to "short int" or "long int". + t instanceof IntType and + // Exclude "signed" or "unsigned", as opposed to "signed int" or "unsigned int". + not exists(IntegralType it | it = t | it.isExplicitlySigned() or it.isExplicitlyUnsigned()) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/Includes.qll b/cpp/common/src/codingstandards/cpp/Includes.qll new file mode 100644 index 0000000000..c0c66ae2f5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Includes.qll @@ -0,0 +1,37 @@ +/** A library which supports analysis of includes. */ + +import cpp +import codingstandards.cpp.PreprocessorDirective +import semmle.code.cpp.headers.MultipleInclusion + +pragma[noinline] +private predicate hasIncludeLocation(Include include, string filepath, int startline) { + include.getLocation().hasLocationInfo(filepath, startline, _, _, _) +} + +/** + * Holds if `include` is included conditionally based on the branch directive `b1`. + */ +pragma[noinline] +predicate isConditionallyIncluded(PreprocessorBranchDirective bd, Include include) { + not bd = any(CorrectIncludeGuard c).getIfndef() and + not bd.getHead().regexpMatch(".*_H(_.*)?") and + exists(string filepath, int startline, int endline, int includeline | + isBranchDirectiveRange(bd, filepath, startline, endline) and + hasIncludeLocation(include, filepath, includeline) and + startline < includeline and + endline > includeline + ) +} + +/** + * Gets a file which is directly included from `fromFile` unconditionally. + */ +File getAnUnconditionallyIncludedFile(File fromFile) { + // Find an include which isn't conditional + exists(Include i | + i.getFile() = fromFile and + not isConditionallyIncluded(_, i) and + result = i.getIncludedFile() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/IntegerConstantMacro.qll b/cpp/common/src/codingstandards/cpp/IntegerConstantMacro.qll new file mode 100644 index 0000000000..ce72033ecc --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/IntegerConstantMacro.qll @@ -0,0 +1,37 @@ +import cpp + +/** + * The family of macros `xINTsize_C(arg)` (e.g. `UINT16_C(123)`) which are used + * to create an integer constant of type `Xint_leastSIZE_t` (e.g. + * `uint_least16_t). + */ +class IntegerConstantMacro extends Macro { + boolean signed; + int size; + + IntegerConstantMacro() { + signed = true and size = getName().regexpCapture("INT(8|16|32|64)_C", 1).toInt() + or + signed = false and size = getName().regexpCapture("UINT(8|16|32|64)_C", 1).toInt() + } + + predicate isSmall() { size < any(IntType it | it.isSigned()).getSize() * 8 } + + int getSize() { result = size } + + predicate isSigned() { signed = true } + + float maxValue() { + signed = true and result = 2.pow(size - 1 * 1.0) - 1 + or + signed = false and result = 2.pow(size) - 1 + } + + float minValue() { + signed = true and result = -2.pow(size - 1) + or + signed = false and result = 0 + } + + string getRangeString() { result = minValue().toString() + ".." + maxValue().toString() } +} diff --git a/cpp/common/src/codingstandards/cpp/IrreplaceableFunctionLikeMacro.qll b/cpp/common/src/codingstandards/cpp/IrreplaceableFunctionLikeMacro.qll new file mode 100644 index 0000000000..8daf129622 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/IrreplaceableFunctionLikeMacro.qll @@ -0,0 +1,62 @@ +import cpp +import codingstandards.cpp.Macro +import codingstandards.cpp.Naming + +/** + * Macros that cannot be replaced by functions + */ +abstract class IrreplaceableFunctionLikeMacro extends FunctionLikeMacro { } + +/** A function like macro that contains the use of a stringize or tokenize operator should not be replaced by a function. */ +private class StringizeOrTokenizeMacro extends IrreplaceableFunctionLikeMacro { + StringizeOrTokenizeMacro() { + exists(TokenPastingOperator t | t.getMacro() = this) or + exists(StringizingOperator s | s.getMacro() = this) + } +} + +/** A standard library function like macro that should not be replaced by a function. */ +private class StandardLibraryFunctionLikeMacro extends IrreplaceableFunctionLikeMacro { + StandardLibraryFunctionLikeMacro() { Naming::Cpp14::hasStandardLibraryMacroName(this.getName()) } +} + +/** A function like macro invocation as an `asm` argument cannot be replaced by a function. */ +private class AsmArgumentInvoked extends IrreplaceableFunctionLikeMacro { + AsmArgumentInvoked() { + any(AsmStmt s).getLocation().subsumes(this.getAnInvocation().getLocation()) + } +} + +/** A macro that is only invoked with constant arguments is more likely to be compile-time evaluated than a function call so do not suggest replacement. */ +private class OnlyConstantArgsInvoked extends IrreplaceableFunctionLikeMacro { + OnlyConstantArgsInvoked() { + forex(MacroInvocation mi | mi = this.getAnInvocation() | + //int/float literals + mi.getUnexpandedArgument(_).regexpMatch("\\d+") + or + //char literal or string literal, which is a literal surrounded by single quotes or double quotes + mi.getUnexpandedArgument(_).regexpMatch("('[^']*'|\"[^\"]*\")") + ) + } +} + +/** A function like macro invoked to initialize an object with static storage that cannot be replaced with a function call. */ +private class UsedToStaticInitialize extends IrreplaceableFunctionLikeMacro { + UsedToStaticInitialize() { + any(StaticStorageDurationVariable v).getInitializer().getExpr() = + this.getAnInvocation().getExpr() + } +} + +/** A function like macro that is called with an argument that is an operator that cannot be replaced with a function call. */ +private class FunctionLikeMacroWithOperatorArgument extends IrreplaceableFunctionLikeMacro { + FunctionLikeMacroWithOperatorArgument() { + exists(MacroInvocation mi | mi.getMacro() = this | + mi.getUnexpandedArgument(_) = any(Operation op).getOperator() + ) + } +} + +private class GenericMacro extends IrreplaceableFunctionLikeMacro { + GenericMacro() { getBody().matches("%_Generic%") } +} diff --git a/cpp/common/src/codingstandards/cpp/Iterators.qll b/cpp/common/src/codingstandards/cpp/Iterators.qll index a9d45d5e36..c8c217aea4 100644 --- a/cpp/common/src/codingstandards/cpp/Iterators.qll +++ b/cpp/common/src/codingstandards/cpp/Iterators.qll @@ -3,15 +3,22 @@ */ import cpp -import semmle.code.cpp.dataflow.DataFlow -import semmle.code.cpp.dataflow.TaintTracking +private import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.TaintTracking +import codingstandards.cpp.StdNamespace +import codingstandards.cpp.rules.containeraccesswithoutrangecheck.ContainerAccessWithoutRangeCheck as ContainerAccessWithoutRangeCheck +import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils abstract class ContainerAccess extends VariableAccess { abstract Variable getOwningContainer(); } pragma[noinline, nomagic] -predicate localTaint(DataFlow::Node n1, DataFlow::Node n2) { TaintTracking::localTaint(n1, n2) } +private predicate localTaint(DataFlow::Node n1, DataFlow::Node n2) { + TaintTracking::localTaint(n1, n2) +} // define this as anything with dataflow FROM the vector class ContainerPointerOrReferenceAccess extends ContainerAccess { @@ -32,7 +39,9 @@ class ContainerPointerOrReferenceAccess extends ContainerAccess { ) and localTaint(DataFlow::exprNode(fc), DataFlow::exprNode(this)) and (getUnderlyingType() instanceof ReferenceType or getUnderlyingType() instanceof PointerType) and - fc.getQualifier().(VariableAccess).getTarget() = owningContainer + fc.getQualifier().(VariableAccess).getTarget() = owningContainer and + // Exclude cases where we see taint into the owning container + not this = owningContainer.getAnAccess() ) } @@ -62,9 +71,11 @@ class ContainerIteratorAccess extends ContainerAccess { ) } - // get a function call to cbegin/begin that - // assigns its value to the iterator represented by this - // access + /** + * gets a function call to cbegin/begin that + * assigns its value to the iterator represented by this + * access + */ FunctionCall getANearbyAssigningIteratorCall() { // the underlying container for this variable is one wherein // there is an assigned value of cbegin/cend @@ -169,9 +180,8 @@ class ContainerInvalidationOperation extends FunctionCall { ) or exists(FunctionCall fc | - fc.getTarget().getNamespace().getName() = "std" and - fc.getTarget().getName() in ["swap", "operator>>", "getline"] and - this = fc + fc.getTarget().getNamespace() instanceof StdNS and + this.getTarget().getName() in ["swap", "operator>>", "getline"] ) ) ) @@ -195,7 +205,7 @@ class ContainerInvalidationOperation extends FunctionCall { /** An iterator type in the `std` namespace. */ class StdIteratorType extends UserType { StdIteratorType() { - getNamespace().getName() = "std" and + this.getNamespace() instanceof StdNS and getSimpleName().matches("%_iterator") and not getSimpleName().matches("const_%") } @@ -247,7 +257,7 @@ class AdditiveOperatorFunctionCall extends FunctionCall { */ class STLContainer extends Class { STLContainer() { - getNamespace().getName() = "std" and + getNamespace() instanceof StdNS and getSimpleName() in [ "vector", "list", "deque", "set", "multiset", "map", "multimap", "stack", "queue", "priority_queue", "string", "forward_list", "unordered_set", "unordered_multiset", @@ -378,7 +388,7 @@ class IteratorRangeFunctionCall extends FunctionCall { count(Expr e | e = getAnArgument() and e.getType() instanceof IteratorType and - getTarget().getNamespace().getName() = "std" and + getTarget().getNamespace() instanceof StdNS and not getTarget().getName() in ["operator==", "operator!="] ) > 1 } @@ -462,3 +472,18 @@ ControlFlowNode getANonInvalidatedSuccessor(ContainerInvalidationOperation op) { not result instanceof ContainerInvalidationOperation ) } + +/** + * Guarded by a bounds check that ensures our destination is larger than "some" value + */ +predicate sizeCompareBoundsChecked(IteratorSource iteratorCreationCall, Expr guarded) { + exists( + GuardCondition guard, ContainerAccessWithoutRangeCheck::ContainerSizeCall sizeCall, + boolean branch + | + globalValueNumber(sizeCall.getQualifier()) = + globalValueNumber(iteratorCreationCall.getQualifier()) and + guard.controls(guarded.getBasicBlock(), branch) and + relOpWithSwapAndNegate(guard, sizeCall, _, Greater(), _, branch) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/Literals.qll b/cpp/common/src/codingstandards/cpp/Literals.qll index 0a6a40aa19..edec04152e 100644 --- a/cpp/common/src/codingstandards/cpp/Literals.qll +++ b/cpp/common/src/codingstandards/cpp/Literals.qll @@ -3,6 +3,9 @@ */ import cpp +import codingstandards.cpp.Cpp14Literal + +class IntegerLiteral = Cpp14Literal::IntegerLiteral; /** Gets `Literal.getValueText()` truncated to at most 20 characters. */ string getTruncatedLiteralText(Literal l) { @@ -12,3 +15,101 @@ string getTruncatedLiteralText(Literal l) { else result = text ) } + +class WideStringLiteral extends StringLiteral { + WideStringLiteral() { this.getValueText().regexpMatch("(?s)\\s*L\".*") } +} + +class Utf8StringLiteral extends StringLiteral { + Utf8StringLiteral() { this.getValueText().regexpMatch("(?s)\\s*u8\".*") } +} + +class Utf16StringLiteral extends StringLiteral { + Utf16StringLiteral() { this.getValueText().regexpMatch("(?s)\\s*u\".*") } +} + +class Utf32StringLiteral extends StringLiteral { + Utf32StringLiteral() { this.getValueText().regexpMatch("(?s)\\s*U\".*") } +} + +class LiteralZero extends Literal { + LiteralZero() { this.getValue() = "0" } +} + +/** + * A literal resulting from the use of a constexpr + * variable, or macro expansion. + * We rely on the fact that the value text of a literal is equal to the + * `constexpr` variable or macro name. + */ +class CompileTimeComputedIntegralLiteral extends Literal { + CompileTimeComputedIntegralLiteral() { + this.getUnspecifiedType() instanceof IntegralType and + // Exclude bool, whose value text is true or false, but the value itself + // is 1 or 0. + not this instanceof BoolLiteral and + // Exclude character literals, whose value text is the quoted character, but the value + // is the numeric value of the character. + not this instanceof Cpp14Literal::CharLiteral and + not this.getValueText() + .trim() + .regexpMatch("([0-9][0-9']*|0[xX][0-9a-fA-F']+|0b[01']+)[uU]?([lL]{1,2}|[zZ])?") and + // Exclude class field initializers whose value text equals the initializer expression, e.g., `x(0)` + not any(ConstructorFieldInit cfi).getExpr() = this + } +} + +class BoolLiteral extends Literal { + BoolLiteral() { + this.getType() instanceof BoolType + or + // When used as non-type template arguments, bool literals might + // have been converted to a non-bool type. + this.getValue() = "1" and this.getValueText() = "true" + or + this.getValue() = "0" and this.getValueText() = "false" + } +} + +/** + * Abstract case to handle positive and negative "literal" expressions. + * + * All numeric literals in c/cpp are positive. To create a negative constant + * value in a program means applying the unary- operator to a positive literal. + * This class effectively describes positive or negative literals. + */ +abstract class PossiblyNegativeLiteral extends Expr { + /* The syntactic literal, stripped of potential negation */ + abstract Cpp14Literal::NumericLiteral getBaseLiteral(); + + /* The value as a literal reads, without potential underflows from negation */ + abstract float getRawValue(); + + predicate isNegative() { this instanceof NegativeLiteral } +} + +/** + * A negation of a positive literal, creating what can be thought of as a + * "negative literal." + */ +class NegativeLiteral extends PossiblyNegativeLiteral, UnaryMinusExpr { + Cpp14Literal::NumericLiteral literal; + + NegativeLiteral() { literal = getOperand() } + + override Cpp14Literal::NumericLiteral getBaseLiteral() { result = literal } + + override float getRawValue() { result = -literal.getValue().toFloat() } +} + +/** + * A literal which is not immediately negated by a parent unary- expression, + * which can be thought of as a "positive literal." + */ +class PositiveLiteral extends PossiblyNegativeLiteral, Cpp14Literal::NumericLiteral { + PositiveLiteral() { not exists(UnaryMinusExpr l | l.getOperand() = this) } + + override Cpp14Literal::NumericLiteral getBaseLiteral() { result = this } + + override float getRawValue() { result = getValue().toFloat() } +} diff --git a/cpp/common/src/codingstandards/cpp/Locations.qll b/cpp/common/src/codingstandards/cpp/Locations.qll new file mode 100644 index 0000000000..800f44d18a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Locations.qll @@ -0,0 +1,30 @@ +import cpp + +/** Holds if `lineNumber` is an indexed line number in file `f`. */ +predicate isLineNumber(File f, int lineNumber) { + exists(Location l | l.getFile() = f | + l.getStartLine() = lineNumber + or + l.getEndLine() = lineNumber + ) +} + +/** Gets the last line number in `f`. */ +int getLastLineNumber(File f) { result = max(int lineNumber | isLineNumber(f, lineNumber)) } + +/** Gets the last column number on the last line of `f`. */ +int getLastColumnNumber(File f) { + result = + max(Location l | + l.getFile() = f and + l.getEndLine() = getLastLineNumber(f) + | + l.getEndColumn() + ) +} + +/** Gets the last column number on the given line of `filepath`. */ +bindingset[filepath, lineNumber] +int getLastColumnNumber(string filepath, int lineNumber) { + result = max(Location l | l.hasLocationInfo(filepath, _, _, lineNumber, _) | l.getEndColumn()) +} diff --git a/cpp/common/src/codingstandards/cpp/LoggingOperation.qll b/cpp/common/src/codingstandards/cpp/LoggingOperation.qll index c77f2c450a..de2201a5cd 100644 --- a/cpp/common/src/codingstandards/cpp/LoggingOperation.qll +++ b/cpp/common/src/codingstandards/cpp/LoggingOperation.qll @@ -1,5 +1,6 @@ import cpp import semmle.code.cpp.security.OutputWrite +import codingstandards.cpp.standardlibrary.FileStreams /** * A operation which may perform logging. @@ -16,9 +17,34 @@ class OutputWriteLogging extends LoggingOperation, OutputWrite { override Expr getALoggedExpr() { result = getASource() } } +/** + * A `FileStreamFunctionCall` operation is considered a log operation for Coding Standards purposes. + */ +class FileStreamLogging extends LoggingOperation { + FileStreamLogging() { this instanceof FileStreamFunctionCall } + + override Expr getALoggedExpr() { result = this.(FileStreamFunctionCall).getAnArgument() } + + Expr getFStream() { result = this.(FileStreamFunctionCall).getQualifier() } +} + /** A call which looks like `printf`. */ class PrintfLikeCall extends LoggingOperation, Call { PrintfLikeCall() { getTarget().getName().toLowerCase().matches("%printf%") } override Expr getALoggedExpr() { result = getAnArgument() } } + +/** + * In a wrapper `Function`, all accesses of all `Parameters` + * are in located in logging or stream calls + */ +class LoggerOrStreamWrapperFunction extends Function { + LoggerOrStreamWrapperFunction() { + forall(VariableAccess va | + exists(Parameter p | p.getFunction() = this and va = p.getAnAccess()) + | + any(LoggingOperation logOp).getALoggedExpr().getAChild*() = va + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/Loops.qll b/cpp/common/src/codingstandards/cpp/Loops.qll index 599b229771..aa3dc64ea5 100644 --- a/cpp/common/src/codingstandards/cpp/Loops.qll +++ b/cpp/common/src/codingstandards/cpp/Loops.qll @@ -5,6 +5,133 @@ import cpp import Operator +// ******* COPIED FROM semmle.code.cpp.Iteration ******* // +/** + * Holds if `child` is in the condition `forCondition` of a 'for' + * statement. + * + * For example, if a program includes + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * then this predicate will hold with `forCondition` as `i < 10`, + * and `child` as any of `i`, `10` and `i < 10`. + */ +pragma[noopt] +private predicate inForCondition(Expr forCondition, Expr child) { + exists(ForStmt for | + forCondition = for.getCondition() and + child = forCondition and + for instanceof ForStmt + ) + or + exists(Expr mid | + inForCondition(forCondition, mid) and + child.getParent() = mid + ) +} + +// ******* COPIED FROM semmle.code.cpp.Iteration ******* // +/** + * Holds if `child` is in the update `forUpdate` of a 'for' statement. + * + * For example, if a program includes + * ``` + * for (i = 0; i < 10; i += 1) { j++; } + * ``` + * then this predicate will hold with `forUpdate` as `i += 1`, + * and `child` as any of `i`, `1` and `i += 1`. + */ +pragma[noopt] +private predicate inForUpdate(Expr forUpdate, Expr child) { + exists(ForStmt for | forUpdate = for.getUpdate() and child = forUpdate) + or + exists(Expr mid | inForUpdate(forUpdate, mid) and child.getParent() = mid) +} + +class MemberCrementOperation extends FunctionCall { + MemberCrementOperation() { this.getTarget() instanceof UserCrementOperator } + + Expr getOperand() { result = this.getQualifier() } +} + +class MemberAssignmentOperation extends FunctionCall { + MemberAssignmentOperation() { this.getTarget() instanceof AssignmentOperator } + + Expr getLValue() { result = this.getQualifier() } + + string getOperator() { result = this.getTarget().getName().regexpCapture("operator(.+)", 1) } +} + +/** + * Gets a LoopCounter for the given `ForStmt`. + * + * Equivalent to ForStmt.getAnIterationVariable(), but handles += and -= as well. + */ +pragma[noopt] +Variable getALoopCounter(ForStmt fs) { + // ------------------------------------------------------------------------------------------------ + // NOTE: This is an updated version of ForStmt.getAnIterationVariable(), handling additional cases. + // The use of pragma[noopt] is retained from the original code, as we haven't determined + // whether it's still necessary across a broad range of databases. As a noopt predicate, it + // includes a degree of duplication as the join order is defined based on the order of the + // conditions. + // ------------------------------------------------------------------------------------------------ + // check that it is assigned to, incremented or decremented in the update + exists(Expr updateOpRoot, Expr updateOp | + updateOpRoot = fs.getUpdate() and + inForUpdate(updateOpRoot, updateOp) + | + exists(CrementOperation op, VariableAccess va | + op = updateOp and + op instanceof CrementOperation and + op.getOperand() = va and + va = result.getAnAccess() + ) + or + exists(MemberCrementOperation op, VariableAccess va | + op = updateOp and + op instanceof MemberCrementOperation and + op.getOperand() = va and + va = result.getAnAccess() + ) + or + exists(MemberAssignmentOperation op, VariableAccess va | + op = updateOp and + op instanceof MemberAssignmentOperation and + op.getOperator() = ["+=", "-="] and + op.getLValue() = va and + va = result.getAnAccess() + ) + or + exists(AssignArithmeticOperation op, VariableAccess va | + op = updateOp and + op instanceof AssignArithmeticOperation and + op.getOperator() = ["+=", "-="] and + op.getLValue() = va and + va = result.getAnAccess() + ) + or + updateOp = result.getAnAssignedValue() + or + // updateOp is an access whose address is taken in a non-const way + exists(FunctionCall fc, VariableAccess va | + fc = updateOp and + fc instanceof FunctionCall and + fc.getAnArgument() = va and + va = result.getAnAccess() and + va.isAddressOfAccessNonConst() + ) + ) and + result instanceof Variable and + // checked or used in the condition + exists(Expr e, VariableAccess va | + va = result.getAnAccess() and + inForCondition(e, va) and + e = fs.getCondition() + ) +} + /** * Gets an iteration variable as identified by the initialization statement for the loop. */ @@ -37,19 +164,33 @@ predicate isForLoopWithFloatingPointCounters(ForStmt forLoop, Variable v) { * Holds if for loop `forLoop` contains an invalid for loop incrementation. * M6-5-2 */ -predicate isInvalidForLoopIncrementation(ForStmt forLoop, LoopControlVariable v) { - v.getAnAccess() = forLoop.getCondition().getAChild*() and - exists(VariableAccess va | - va = v.getAnAccess() and - va = forLoop.getUpdate().getAChild*() and - not exists(CrementOperation cop | cop.getOperand() = va) and - not exists(Call c | c.getQualifier() = va and c.getTarget() instanceof UserCrementOperator) +predicate isInvalidForLoopIncrementation(ForStmt forLoop, Variable v, VariableAccess modification) { + v = getAnIterationVariable(forLoop) and + modification = v.getAnAccess() and + modification = forLoop.getUpdate().getAChild*() and + // Is modified + ( + // Variable directly modified + modification.isModified() + or + // Has a call to a member function on the variable, where the target is non-const, + // i.e. can modify the state of the object + exists(Call c | + c.getQualifier() = modification and + not c.getTarget() instanceof ConstMemberFunction + ) + ) and + // And not by a call to a crement operator + not exists(CrementOperation cop | cop.getOperand() = modification) and + not exists(Call c | + c.getQualifier() = modification and c.getTarget() instanceof UserCrementOperator ) and exists(VariableAccess va | va = forLoop.getCondition().getAChild*() and va = v.getAnAccess() | exists(EqualityOperation eop | eop.getAnOperand() = va) or exists(Call call | - call.getTarget() instanceof UserEqualityOperator and call.getQualifier() = va + call.getTarget() instanceof UserEqualityOperator and + [call.getQualifier(), call.getArgument(0)] = va ) ) } @@ -63,7 +204,7 @@ predicate isLoopCounterModifiedInCondition(ForStmt forLoop, VariableAccess loopC loopCounterAccess = getAnIterationVariable(forLoop).getAnAccess() and ( loopCounterAccess.isModified() or - loopCounterAccess.isAddressOfAccess() + loopCounterAccess.isAddressOfAccessNonConst() ) } @@ -78,7 +219,7 @@ predicate isLoopCounterModifiedInStatement( loopCounterAccess = loopCounter.getAnAccess() and ( loopCounterAccess.isModified() or - loopCounterAccess.isAddressOfAccess() + loopCounterAccess.isAddressOfAccessNonConst() ) and forLoop.getStmt().getChildStmt*() = loopCounterAccess.getEnclosingStmt() } @@ -135,7 +276,7 @@ predicate isLoopControlVarModifiedInLoopCondition( loopControlVariableAccess = forLoop.getCondition().getAChild+() and ( loopControlVariableAccess.isModified() or - loopControlVariableAccess.isAddressOfAccess() + loopControlVariableAccess.isAddressOfAccessNonConst() ) } @@ -147,11 +288,12 @@ predicate isLoopControlVarModifiedInLoopExpr( ForStmt forLoop, LoopControlVariable loopControlVariable, VariableAccess loopControlVariableAccess ) { loopControlVariableAccess = loopControlVariable.getVariableAccessInLoop(forLoop) and - not loopControlVariable = getAnIterationVariable(forLoop) and + // Not a standard loop counter for this loop + not loopControlVariable = getALoopCounter(forLoop) and loopControlVariableAccess = forLoop.getUpdate().getAChild() and ( loopControlVariableAccess.isModified() or - loopControlVariableAccess.isAddressOfAccess() + loopControlVariableAccess.isAddressOfAccessNonConst() ) } @@ -163,26 +305,72 @@ predicate isLoopControlVarModifiedInLoopExpr( predicate isNonBoolLoopControlVar( ForStmt forLoop, LoopControlVariable loopControlVariable, VariableAccess loopControlVariableAccess ) { - // get a loop control variable that is not a loop counter - loopControlVariableAccess = loopControlVariable.getVariableAccessInLoop(forLoop) and - not loopControlVariable = getAnIterationVariable(forLoop) and - loopControlVariableAccess.getEnclosingStmt() = forLoop.getStmt().getAChild*() and - // filter only loop control variables that are modified - ( - loopControlVariableAccess.isModified() or - loopControlVariableAccess.isAddressOfAccess() - ) and - // check if the variable type is anything but bool - not loopControlVariable.getType() instanceof BoolType -} - -predicate isInvalidLoop(ForStmt forLoop) { - isInvalidForLoopIncrementation(forLoop, _) or - isForLoopWithMulipleCounters(forLoop) or - isForLoopWithFloatingPointCounters(forLoop, _) or - isLoopCounterModifiedInCondition(forLoop, _) or - isLoopCounterModifiedInStatement(forLoop, _, _) or - isIrregularLoopCounterModification(forLoop, _, _) or - isLoopControlVarModifiedInLoopExpr(forLoop, _, _) or - isNonBoolLoopControlVar(forLoop, _, _) + exists(Variable loopCounter, ComparisonOperation terminationCheck | + loopCounter = getAnIterationVariable(forLoop) and + forLoop.getCondition() = terminationCheck.getParent*() + | + // get a loop control variable that is not a loop counter + loopControlVariableAccess = loopControlVariable.getVariableAccessInLoop(forLoop) and + not loopControlVariable = getAnIterationVariable(forLoop) and + // filter only loop control variables that are modified + ( + loopControlVariableAccess.isModified() or + loopControlVariableAccess.isAddressOfAccess() + ) and + // check if the variable type is anything but bool + not loopControlVariable.getType() instanceof BoolType and + // check if the control variable is part of the termination check, but is not compared to the loop counter + terminationCheck.getAnOperand() = loopControlVariable.getAnAccess().getParent*() and + not terminationCheck.getAnOperand() = loopCounter.getAnAccess().getParent*() + ) +} + +predicate isInvalidLoop(ForStmt forLoop) { isInvalidLoop(forLoop, _, _, _) } + +predicate isInvalidLoop(ForStmt forLoop, string reason, Locatable reasonLocation, string reasonLabel) { + exists(Variable loopCounter | + isInvalidForLoopIncrementation(forLoop, loopCounter, reasonLocation) and + reason = + "it $@ its loop counter '" + loopCounter.getName() + + "' with an operation that is not an increment or decrement" and + reasonLabel = "updates" + ) + or + isForLoopWithMulipleCounters(forLoop) and + reason = "it uses multiple loop counters$@" and + reasonLabel = "" and + reasonLocation.getLocation() instanceof UnknownExprLocation + or + isForLoopWithFloatingPointCounters(forLoop, reasonLocation) and + reason = "it uses a loop counter '$@' of type floating-point" and + reasonLabel = reasonLocation.(Variable).getName() + or + isLoopCounterModifiedInCondition(forLoop, reasonLocation) and + reason = + "it $@ the loop counter '" + reasonLocation.(VariableAccess).getTarget().getName() + + "' in the condition" and + reasonLabel = "updates" + or + exists(Variable loopCounter | + isLoopCounterModifiedInStatement(forLoop, loopCounter, reasonLocation) and + reason = "it $@ the loop counter '" + loopCounter.getName() + "' in the body of the loop" and + reasonLabel = "updates" + ) + or + exists(Variable loopCounter | + isIrregularLoopCounterModification(forLoop, loopCounter, reasonLocation) and + reason = "it $@ the loop counter '" + loopCounter.getName() + "' irregularly" and + reasonLabel = "updates" + ) + or + exists(Variable loopControlVariable | + isLoopControlVarModifiedInLoopExpr(forLoop, loopControlVariable, reasonLocation) and + reason = + "it updates $@, a loop control variable other than the loop counter, in the update expression of the loop" and + reasonLabel = loopControlVariable.getName() + ) + or + isNonBoolLoopControlVar(forLoop, reasonLocation, _) and + reason = "its $@ is not a boolean" and + reasonLabel = "loop control variable" } diff --git a/cpp/common/src/codingstandards/cpp/Macro.qll b/cpp/common/src/codingstandards/cpp/Macro.qll index cb1231e04f..6514e957fb 100644 --- a/cpp/common/src/codingstandards/cpp/Macro.qll +++ b/cpp/common/src/codingstandards/cpp/Macro.qll @@ -15,7 +15,8 @@ class FunctionLikeMacro extends Macro { int getAParameterUse(int index) { exists(string parameter | parameter = getParameter(index) | - result = this.getBody().indexOf(parameter) + // Find identifier tokens in the program that match the parameter name + exists(this.getBody().regexpFind("\\#?\\b" + parameter + "\\b", _, result)) ) } } @@ -68,3 +69,29 @@ pragma[noinline] predicate isMacroInvocationLocation(MacroInvocation mi, File f, int startChar, int endChar) { mi.getActualLocation().charLoc(f, startChar, endChar) } + +/** A macro within the source location of this project. */ +class UserProvidedMacro extends Macro { + UserProvidedMacro() { + exists(this.getFile().getRelativePath()) and + // Exclude macros in our standard library header stubs for tests, because qltest sets the source + // root to the qlpack root, which means our stubs all look like source files. + // + // This may affect "real" code as well, if it happens to be at this path, but given the name + // I think it's likely that we'd want that to be the case anyway. + not this.getFile().getRelativePath().substring(0, "includes/standard-library".length()) = + "includes/standard-library" + } +} + +/** A macro defined within a library used by this project. */ +class LibraryMacro extends Macro { + LibraryMacro() { not this instanceof UserProvidedMacro } +} + +/** + * 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") } +} diff --git a/cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll b/cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll new file mode 100644 index 0000000000..8a28bd0517 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll @@ -0,0 +1,264 @@ +/** + * A library for parsing a string of parentheses and non-parentheses characters. + * + * Simply implement the signature class `InputString` for the set of strings that you wish to parse, + * and then use the `MatchingParenthesis` module which exposes the following classes/predicates: + * - `ParsedRoot`: The root of the parse tree. + * - `ParsedGroup`: Parenthesis groups. The root is also a group, even if not parenthesized. + * - `ParsedText`: All text that is not '(' or ')'. + * - `Tokenized`: A linked list of the tokens in the input string. + * - `textFrom(start, end)`: A function to get the text between two tokens. + * + * The parenthesis AST has functions `getChild(int i)` and `getParent()` to navigate the tree, as + * well as `getRoot()` and `getText()` for `ParsedText` nodes. They also have methods + * `getStartToken()`, `getEndToken()` which are especially useful with the method `textFrom(...)`. + * + * This module can allow for slightly more intelligent interpretation of macro strings, but it has + * limitations. + * - It _only_ handles the parenthesis. + * - It assumes parentheses are matched. + * - It does not handle the case where a parenthesis is inside a string literal. + * - It does not handle the case where a parenthesis is inside a comment. + * + * This module has been moderately optimized, but still it is best to be selective with the set of + * strings you attempt to parse with it. + */ + +import codeql.util.Option + +signature class InputString extends string; + +module MatchingParenthesis { + newtype TTokenType = + TOpenParen() or + TCloseParen() or + TNotParen() + + bindingset[char] + private TTokenType tokenTypeOfChar(string char) { + result = TOpenParen() and char = "(" + or + result = TCloseParen() and char = ")" + } + + private int inputId(Input i) { rank[result](Input inp) = i } + + private newtype TTokenized = + TTokenizerStart(int iid) { iid = inputId(_) } or + TToken(int iid, TTokenized prev, TTokenType token, int occurrence, int endPos) { + exists(string inputStr, int prevEndPos, int prevOccurrence, string char | + iid = inputId(inputStr) and + ( + prev = TTokenizerStart(iid) and prevOccurrence = -1 and prevEndPos = 0 + or + prev = TToken(iid, _, _, prevOccurrence, prevEndPos) + ) and + inputStr.charAt(prevEndPos) = char and + if char = ["(", ")"] + then ( + endPos = prevEndPos + 1 and + token = tokenTypeOfChar(char) and + occurrence = prevOccurrence + 1 + ) else ( + token = TNotParen() and + exists(inputStr.regexpFind("\\(|\\)", prevOccurrence + 1, endPos)) and + occurrence = prevOccurrence + ) + ) + } + + class Tokenized extends TTokenized { + string toString() { + getTokenType() = TOpenParen() and result = "(" + or + getTokenType() = TCloseParen() and result = ")" + or + getTokenType() = TNotParen() and result = "non-parenthesis" + } + + int getInputId() { this = TToken(result, _, _, _, _) } + + TTokenType getTokenType() { this = TToken(_, _, result, _, _) } + + Tokenized getPrevious() { this = TToken(_, result, _, _, _) } + + string getInputString() { + this = TToken(inputId(result), _, _, _, _) or this = TTokenizerStart(inputId(result)) + } + + int getStartPos() { + if exists(getPrevious()) then result = getPrevious().getEndPos() else result = 0 + } + + int getEndPos() { + this = TToken(_, _, _, _, result) + or + this = TTokenizerStart(_) and result = 0 + } + + string getText() { result = textFrom(this, this) } + + Tokenized getNext() { result.getPrevious() = this } + + Tokenized getLast() { + if exists(getNext()) then result = getNext().getLast() else result = this + } + } + + /** + * The root of the parse tree. + */ + class ParsedRoot extends ParsedGroup { + ParsedRoot() { not exists(getParent()) } + + override ParsedRoot getRoot() { result = this } + + override string getDebugText() { result = this.(Tokenized).getInputString() } + } + + /** + * A group of tokens that may be parenthesized. + * + * The `ParseRoot` is the only group that isn't parenthesized. + */ + class ParsedGroup extends Parsed { + ParsedGroup() { isGroup() } + + Parsed getChild(int i) { + result.getParent() = this and + result.getChildIdx() = i + } + } + + /** + * Get the text from the `start` token to the `end` token (inclusive on both ends). + */ + pragma[inline] + string textFrom(Tokenized start, Tokenized end) { + result = start.getInputString().substring(start.getStartPos(), end.getEndPos()) + } + + /** + * All text that is not '(' or ')'. + */ + class ParsedText extends Parsed { + ParsedText() { not isGroup() } + + string getText() { result = textFrom(getStartToken(), getEndToken()) } + } + + /** + * The AST for the input string parsed with matching parenthesis. + */ + class Parsed extends TTokenized { + Option::Option parent; + int childIdx; + boolean isGroup; + + Parsed() { + this.(Tokenized).getTokenType() = TNotParen() and + parseStepAppend(this, parent.asSome(), childIdx) and + isGroup = false + or + this.(Tokenized).getTokenType() = TOpenParen() and + parseStepOpen(this, parent.asSome(), childIdx) and + isGroup = true + or + this = TTokenizerStart(_) and + parent.isNone() and + childIdx = 0 and + isGroup = true + } + + ParsedRoot getRoot() { result = getParent().getRoot() } + + string getInputString() { result = this.(Tokenized).getInputString() } + + /** + * The token that starts this group. + * + * For `ParsedText`, this is the same as the end token. + */ + Tokenized getStartToken() { result = this } + + /** + * The token that endns this group. + * + * For `ParsedText`, this is the same as the start token. If parentheses are not matched, this + * may not have a result. + */ + Tokenized getEndToken() { + this.(Tokenized).getTokenType() = TNotParen() and + result = this + or + this.(Tokenized).getTokenType() = TOpenParen() and + parseStepClose(result, this) + or + this = TTokenizerStart(_) and + result = getStartToken().(Tokenized).getLast() + } + + /** + * The index of this child in the parent group. + */ + int getChildIdx() { result = childIdx } + + ParsedGroup getParent() { result = parent.asSome() } + + predicate isGroup() { isGroup = true } + + string getDebugText() { result = textFrom(getStartToken(), getEndToken()) } + + string toString() { result = this.(Tokenized).toString() } + } + + /** + * Parse open parenthesis and add it to the open group or parse root. Parsing algorithm may not + * behave reliably for mismatched parenthesis. + */ + private predicate parseStepOpen(Tokenized consumeToken, ParsedGroup parent, int childIdx) { + consumeToken.getTokenType() = TOpenParen() and + ( + consumeToken.getPrevious() = parent.getStartToken() and + childIdx = 0 + or + exists(Parsed prevSibling | + prevSibling.getEndToken() = consumeToken.getPrevious() and + childIdx = prevSibling.getChildIdx() + 1 and + parent = prevSibling.getParent() + ) + ) + } + + /** + * Parse raw text that isn't '(' or ')' and add it to the open group or parse root. + */ + private predicate parseStepAppend(Tokenized consumeToken, ParsedGroup parent, int childIdx) { + consumeToken.getTokenType() = TNotParen() and + ( + consumeToken.getPrevious() = parent.getStartToken() and childIdx = 0 + or + exists(Parsed prevSibling | + prevSibling.getEndToken() = consumeToken.getPrevious() and + childIdx = prevSibling.getChildIdx() + 1 and + parent = prevSibling.getParent() + ) + ) + } + + /** + * Parse a close parenthesis to close the currently open group. Parsing algorithm may not behave + * properly for mismatched parenthesis. + */ + private predicate parseStepClose(Tokenized consumeToken, ParsedGroup closed) { + consumeToken.getTokenType() = TCloseParen() and + ( + closed.getStartToken() = consumeToken.getPrevious() + or + exists(Parsed finalChild | + consumeToken.getPrevious() = finalChild.getEndToken() and + finalChild.getParent() = closed + ) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/NameInDependentBase.qll b/cpp/common/src/codingstandards/cpp/NameInDependentBase.qll new file mode 100644 index 0000000000..e599f286ae --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/NameInDependentBase.qll @@ -0,0 +1,129 @@ +import cpp + +/** + * Gets a dependent base type of the given template class. + * + * This returns the `TemplateClass` for the base type, rather than the `ClassTemplateInstantiation`, + * as the instantiation does not appear to include any member declarations. + */ +TemplateClass getADependentBaseType(TemplateClass t) { + exists(ClassTemplateInstantiation baseType | + baseType = t.getABaseClass() and + // Base type depends on at least one of the template parameters of class t + baseType.getATemplateArgument() = t.getATemplateArgument() and + // Return the template itself + result = baseType.getTemplate() + ) +} + +/** + * Helper predicate that ensures we do not join on function pairs by name early on, as that creates + * a large dataset on big databases with lots of name duplication. + */ +pragma[nomagic] +private FunctionCall helper_functioncall( + TemplateClass t, TemplateClass dependentBaseType, Function target, string name +) { + dependentBaseType = getADependentBaseType(t) and + // The target of the call is not declared in the dependent base type + not target.getDeclaringType() = dependentBaseType and + result = target.getACallToThisFunction() and + result.getEnclosingFunction() = t.getAMemberFunction() and + name = target.getName() +} + +/** + * Gets a function call in `TemplateClass` `t` where the target function name exists in a dependent + * base type and the call is to a function that is not declared in the dependent base type. + */ +FunctionCall getConfusingFunctionCall( + TemplateClass t, string name, Function target, MemberFunction dependentTypeFunction +) { + exists(TemplateClass dependentBaseType | + result = helper_functioncall(t, dependentBaseType, target, name) and + // The dependentTypeFunction is declared on the dependent base type + dependentBaseType.getAMember() = dependentTypeFunction and + // And has the same name as the target of the function call in the child + name = dependentTypeFunction.getName() + ) +} + +/** + * Helper predicate that ensures we do not join on function pairs by name early on, as that creates + * a large dataset on big databases with lots of name duplication. + */ +pragma[nomagic] +private FunctionAccess helper_functionaccess( + TemplateClass t, TemplateClass dependentBaseType, Function target, string name +) { + dependentBaseType = getADependentBaseType(t) and + // The target of the access is not declared in the dependent base type + not target.getDeclaringType() = dependentBaseType and + result = target.getAnAccess() and + result.getEnclosingFunction() = t.getAMemberFunction() and + name = target.getName() +} + +/** + * Gets a function access in `TemplateClass` `t` where the target function name exists in a dependent + * base type and the access is to a function declared outside the dependent base type. + */ +FunctionAccess getConfusingFunctionAccess( + TemplateClass t, string name, Function target, MemberFunction dependentTypeFunction +) { + exists(TemplateClass dependentBaseType | + result = helper_functionaccess(t, dependentBaseType, target, name) and + dependentBaseType.getAMember() = dependentTypeFunction and + name = dependentTypeFunction.getName() + ) +} + +/** + * Helper predicate that ensures we do not join on variable pairs by name early on, as that creates + * a large dataset on big databases with lots of name duplication. + */ +pragma[nomagic] +private VariableAccess helper_memberaccess( + TemplateClass t, TemplateClass dependentBaseType, Variable target, string name +) { + dependentBaseType = getADependentBaseType(t) and + // The target of the access is not declared in the dependent base type + not target.getDeclaringType() = dependentBaseType and + result = target.getAnAccess() and + result.getEnclosingFunction() = t.getAMemberFunction() and + name = target.getName() and + // The target is not a local variable, which isn't subject to confusion + not target instanceof LocalScopeVariable +} + +/** + * Gets a memmber access in `TemplateClass` `t` where the target member name exists in a dependent + * base type and the access is to a variable declared outside the dependent base type. + */ +VariableAccess getConfusingMemberVariableAccess( + TemplateClass t, string name, Variable target, MemberVariable dependentTypeMemberVariable +) { + exists(TemplateClass dependentBaseType | + result = helper_memberaccess(t, dependentBaseType, target, name) and + dependentBaseType.getAMemberVariable() = dependentTypeMemberVariable and + name = dependentTypeMemberVariable.getName() + ) +} + +/** + * A `NameQualifiableElement` without a name qualifier + */ +predicate missingNameQualifier(NameQualifiableElement element) { + not exists(NameQualifier q | q = element.getNameQualifier()) +} + +/** + * heuristics: do not care about: + * compiler generated calls + * superclass constructor use, in constructor delegation + */ +predicate isCustomExcluded(Expr fn) { + fn.isCompilerGenerated() or + fn instanceof ConstructorDirectInit or + fn.(FunctionCall).getTarget() instanceof Operator +} diff --git a/cpp/common/src/codingstandards/cpp/Noreturn.qll b/cpp/common/src/codingstandards/cpp/Noreturn.qll new file mode 100644 index 0000000000..eabe86b56e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Noreturn.qll @@ -0,0 +1,22 @@ +import cpp + +/** + * A function marked with _Noreturn or __attribute((noreturn)) + */ +class NoreturnFunction extends Function { + NoreturnFunction() { + this.getASpecifier().getName() = "noreturn" or + this.getAnAttribute().getName() = "noreturn" + } +} + +/** + * A function that may complete normally, and/or contains an explicit reachable + * return statement. + */ +predicate mayReturn(Function function) { + exists(ReturnStmt s | + function = s.getEnclosingFunction() and + s.getBasicBlock().isReachable() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/Nullness.qll b/cpp/common/src/codingstandards/cpp/Nullness.qll index e3af58b794..8751c54d9b 100644 --- a/cpp/common/src/codingstandards/cpp/Nullness.qll +++ b/cpp/common/src/codingstandards/cpp/Nullness.qll @@ -1,19 +1,14 @@ import cpp import semmle.code.cpp.dataflow.DataFlow -import semmle.code.cpp.dataflow.DataFlow2 private class PointerToMember extends Variable { PointerToMember() { this.getType() instanceof PointerToMemberType } } -class NullPointerToPointerMemberExpressionConfig extends DataFlow::Configuration { - NullPointerToPointerMemberExpressionConfig() { - this = "NullPointerToPointerMemberExpressionConfig" - } - - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NullValue } +module NullPointerToPointerMemberExpressionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NullValue } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { // The null value can flow to a pointer-to-member expressions that points to a function exists(VariableCall call, VariableAccess va | call.getQualifier() = va and va = sink.asExpr() | va.getTarget() instanceof PointerToMember @@ -24,12 +19,13 @@ class NullPointerToPointerMemberExpressionConfig extends DataFlow::Configuration } } -class NullValueToAssignmentConfig extends DataFlow2::Configuration { - NullValueToAssignmentConfig() { this = "NullValueToAssignmentConfig" } +module NullPointerToPointerMemberExpressionFlow = + DataFlow::Global; - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NullValue } +module NullValueToAssignmentConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NullValue } - override predicate isSink(DataFlow::Node sink) { - exists(Assignment a | a.getRValue() = sink.asExpr()) - } + predicate isSink(DataFlow::Node sink) { exists(Assignment a | a.getRValue() = sink.asExpr()) } } + +module NullValueToAssignmentFlow = DataFlow::Global; diff --git a/cpp/common/src/codingstandards/cpp/Operator.qll b/cpp/common/src/codingstandards/cpp/Operator.qll index 55db1f9da2..6d2cc39933 100644 --- a/cpp/common/src/codingstandards/cpp/Operator.qll +++ b/cpp/common/src/codingstandards/cpp/Operator.qll @@ -1,5 +1,7 @@ import cpp import Expr +private import semmle.code.cpp.security.FileWrite +private import codingstandards.cpp.standardlibrary.FileStreams /** * any assignment operator that also reads from the access @@ -8,10 +10,10 @@ class AnyAssignOperation extends Expr { AnyAssignOperation() { this instanceof AssignOperation or - // operator op, where op is +=, -=, *=, /=, %=, ^=, &=, |=, >>= + // operator op, where op is +=, -=, *=, /=, %=, ^=, &=, |=, >>=, <<= exists(string op | "operator" + op = this.(FunctionCall).getTarget().getName() and - op in ["+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>="] + op in ["+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>=", "<<="] ) } } @@ -119,14 +121,16 @@ class UserAssignmentOperator extends AssignmentOperator { } /** An assignment operator of any sort */ -class AssignmentOperator extends MemberFunction { +class AssignmentOperator extends Function { AssignmentOperator() { - // operator op, where op is =, +=, -=, *=, /=, %=, ^=, &=, |=, >>= + // operator op, where op is =, +=, -=, *=, /=, %=, ^=, &=, |=, >>=, <<= exists(string op | "operator" + op = this.getName() and - op in ["=", "+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>="] + op in ["=", "+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>=", "<<="] ) } + + predicate isLValueRefQualified() { this.(MemberFunction).isLValueRefQualified() } } class UserComparisonOperator extends Function { @@ -211,6 +215,20 @@ class IncrementOperator extends Operator { } } +class PostIncrementOperator extends Operator { + PostIncrementOperator() { + hasName("operator++") and + getNumberOfParameters() = 1 + } +} + +class PostDecrementOperator extends Operator { + PostDecrementOperator() { + hasName("operator--") and + getNumberOfParameters() = 1 + } +} + class StructureDerefOperator extends Operator { StructureDerefOperator() { hasName("operator->") and @@ -264,3 +282,112 @@ class UserOverloadedOperator extends Function { not this.isCompilerGenerated() } } + +/** An implementation of a stream insertion operator. */ +class StreamInsertionOperator extends Function { + StreamInsertionOperator() { + this.hasName("operator<<") and + ( + if this.isMember() + then this.getNumberOfParameters() = 1 + else ( + this.getNumberOfParameters() = 2 and + this.getParameter(0).getType() instanceof BasicOStreamClass + ) + ) and + this.getType() instanceof BasicOStreamClass + } +} + +/** An implementation of a stream extraction operator. */ +class StreamExtractionOperator extends Function { + StreamExtractionOperator() { + this.hasName("operator>>") and + ( + if this.isMember() + then this.getNumberOfParameters() = 1 + else ( + this.getNumberOfParameters() = 2 and + this.getParameter(0).getType() instanceof IStream + ) + ) and + this.getType() instanceof IStream + } +} + +/** A user defined operator address of operator (`&`). */ +class UnaryAddressOfOperator extends Operator { + UnaryAddressOfOperator() { + hasName("operator&") and + ( + // If this is a member function, it needs to have zero arguments to be the unary addressof + // operator + if this instanceof MemberFunction + then getNumberOfParameters() = 0 + else + // Otherwise it needs one argument to be unary + getNumberOfParameters() = 1 + ) + } +} + +private newtype TOperatorUse = + TBuiltinOperatorUse(Operation op) or + TOverloadedOperatorUse(FunctionCall call, Operator op) { op.getACallToThisFunction() = call } + +/** + * A class to reason about builtin operator and overloaded operator use. + */ +class OperatorUse extends TOperatorUse { + string toString() { + exists(Operation op | result = op.toString() and this = TBuiltinOperatorUse(op)) + or + exists(Operator op | result = op.toString() and this = TOverloadedOperatorUse(_, op)) + } + + predicate isOverloaded() { this = TOverloadedOperatorUse(_, _) } + + Operation asBuiltin() { this = TBuiltinOperatorUse(result) } + + Operator asOverloaded(FunctionCall call) { this = TOverloadedOperatorUse(call, result) } + + Type getType() { + result = this.asBuiltin().getType() + or + result = this.asOverloaded(_).getType() + } + + Parameter getParameter(int index) { result = this.asOverloaded(_).getParameter(index) } + + Parameter getAParameter() { result = this.asOverloaded(_).getParameter(_) } + + Expr getAnOperand() { + result = this.asBuiltin().getAnOperand() + or + exists(FunctionCall call, Operator op | op = this.asOverloaded(call) | + result = call.getAnArgument() + ) + } + + Location getLocation() { + result = this.asBuiltin().getLocation() + or + exists(FunctionCall call, Operator op | op = this.asOverloaded(call) | + result = call.getLocation() + ) + } + + string getOperator() { + result = this.asBuiltin().getOperator() + or + result = this.asOverloaded(_).getName().regexpCapture("^operator(.*)$", 1) + } +} + +class UnaryOperatorUse extends OperatorUse { + UnaryOperatorUse() { + this.asBuiltin() instanceof UnaryOperation + or + this.asOverloaded(_).getNumberOfParameters() = 0 + } +} diff --git a/cpp/autosar/src/rules/A18-5-4/OperatorDelete.qll b/cpp/common/src/codingstandards/cpp/OperatorDelete.qll similarity index 96% rename from cpp/autosar/src/rules/A18-5-4/OperatorDelete.qll rename to cpp/common/src/codingstandards/cpp/OperatorDelete.qll index ada7d109cd..c9ff315866 100644 --- a/cpp/autosar/src/rules/A18-5-4/OperatorDelete.qll +++ b/cpp/common/src/codingstandards/cpp/OperatorDelete.qll @@ -1,5 +1,4 @@ import cpp -import codingstandards.cpp.autosar class StdNoThrow extends Class { StdNoThrow() { hasQualifiedName("std", "nothrow_t") } diff --git a/cpp/common/src/codingstandards/cpp/Overflow.qll b/cpp/common/src/codingstandards/cpp/Overflow.qll new file mode 100644 index 0000000000..b81147d6bf --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Overflow.qll @@ -0,0 +1,428 @@ +/** + * This module provides predicates for checking whether an integer operation overflows, underflows or wraps. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import SimpleRangeAnalysisCustomizations +import semmle.code.cpp.controlflow.Guards +private import semmle.code.cpp.dataflow.TaintTracking +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import codingstandards.cpp.Expr +import codingstandards.cpp.UndefinedBehavior + +/** + * An integer operation that may overflow, underflow or wrap. + */ +class InterestingOverflowingOperation extends Operation { + InterestingOverflowingOperation() { + // We are only interested in integer experssions + this.getUnderlyingType() instanceof IntegralType and + // Might overflow or underflow + ( + exprMightOverflowNegatively(this) + or + exprMightOverflowPositively(this) + or + // Division and remainder are not handled by the library + exists(DivOrRemOperation divOrRem | divOrRem = this | + // The right hand side could be -1 + upperBound(divOrRem.getDivisor()) >= -1.0 and + lowerBound(divOrRem.getDivisor()) <= -1.0 and + // The left hand side could be the smallest possible integer value + lowerBound(divOrRem.getDividend()) <= + typeLowerBound(divOrRem.getDividend().getType().getUnderlyingType()) + ) + ) and + // Multiplication is not covered by the standard range analysis library, so implement our own + // mini analysis. + (this instanceof MulExpr implies MulExprAnalysis::overflows(this)) and + // This shouldn't be a "safe" crement operation + not LoopCounterAnalysis::isCrementSafeFromOverflow(this) and + // Not within a macro + not this.isAffectedByMacro() and + // Ignore pointer arithmetic + not this instanceof PointerArithmeticOperation and + // In case of the shift operation, it must cause undefined behavior + (this instanceof BitShiftExpr implies this instanceof ShiftByNegativeOrGreaterPrecisionOperand) + } + + /** + * Holds if there is a correct validity check after this expression which may overflow. + */ + predicate hasValidPreCheck() { + // For binary operations (both arithmetic operations and arithmetic assignment operations) + exists(GVN i1, GVN i2, Expr op1, Expr op2 | + op1 = getAnOperand() and + op2 = getAnOperand() and + not op1 = op2 and + i1 = globalValueNumber(op1) and + i2 = globalValueNumber(op2) + | + // For unsigned integer addition, look for this pattern: + // if (x + y > x) + // use(x + y) + // Ensuring it is not a bad overflow check + (this instanceof AddExpr or this instanceof AssignAddExpr) and + this.getType().getUnspecifiedType().(IntegralType).isUnsigned() and + exists(AddExpr ae, RelationalOperation relOp | + globalValueNumber(relOp.getAnOperand()) = i1 and + relOp.getAnOperand() = ae and + globalValueNumber(ae.getAnOperand()) = i1 and + globalValueNumber(ae.getAnOperand()) = i2 + | + // At least one operand is not smaller than int + exists(Expr op | op = ae.getAnOperand() | + op.getType().getSize() >= any(IntType i).getSize() + ) + or + // The result of the addition is explicitly converted to a smaller type before the comparison + ae.getExplicitlyConverted().getType().getSize() < any(IntType i).getSize() + ) + or + // Match this pattern for checking for unsigned integer overflow on add + // if (UINT_MAX - i1 < i2) + (this instanceof AddExpr or this instanceof AssignAddExpr) and + this.getType().getUnspecifiedType().(IntegralType).isUnsigned() and + exists(SubExpr se, RelationalOperation relOp | + globalValueNumber(relOp.getGreaterOperand()) = i2 and + relOp.getAnOperand() = se and + globalValueNumber(se.getRightOperand()) = i1 and + se.getLeftOperand().getValue().toFloat() = typeUpperBound(getType()) + ) + or + // Match this pattern for checking for unsigned integer underflow on subtract + // if (i1 < i2) + (this instanceof SubExpr or this instanceof AssignSubExpr) and + this.getType().getUnspecifiedType().(IntegralType).isUnsigned() and + exists(RelationalOperation relOp | + globalValueNumber(relOp.getGreaterOperand()) = i2 and + globalValueNumber(relOp.getLesserOperand()) = i1 + ) + or + // The CERT rule for signed integer overflow has a very specific pattern it recommends + // for checking for overflow. We try to match the pattern here. + // ((i2 > 0 && i1 > (INT_MAX - i2)) || (i2 < 0 && i1 < (INT_MIN - i2))) + (this instanceof AddExpr or this instanceof AssignAddExpr) and + exists(LogicalOrExpr orExpr | + // GuardCondition doesn't work in this case, so just confirm that this check dominates the overflow + bbDominates(orExpr.getBasicBlock(), this.getBasicBlock()) and + exists(LogicalAndExpr andExpr | + andExpr = orExpr.getAnOperand() and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getLesserOperand().getValue() = "0" and + globalValueNumber(gt.getGreaterOperand()) = i2 + ) and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getLesserOperand() = + any(SubExpr se | + se.getLeftOperand().getValue().toFloat() = typeUpperBound(getType()) and + globalValueNumber(se.getRightOperand()) = i2 + ) and + globalValueNumber(gt.getGreaterOperand()) = i1 + ) + ) and + exists(LogicalAndExpr andExpr | + andExpr = orExpr.getAnOperand() and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getGreaterOperand().getValue() = "0" and + globalValueNumber(gt.getLesserOperand()) = i2 + ) and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getGreaterOperand() = + any(SubExpr se | + se.getLeftOperand().getValue().toFloat() = typeLowerBound(getType()) and + globalValueNumber(se.getRightOperand()) = i2 + ) and + globalValueNumber(gt.getLesserOperand()) = i1 + ) + ) + ) + or + // The CERT rule for signed integer overflow has a very specific pattern it recommends + // for checking for underflow. We try to match the pattern here. + // ((i2 > 0 && i1 > (INT_MIN + i2)) || (i2 < 0 && i1 < (INT_MAX + i2))) + (this instanceof SubExpr or this instanceof AssignSubExpr) and + exists(LogicalOrExpr orExpr | + // GuardCondition doesn't work in this case, so just confirm that this check dominates the overflow + bbDominates(orExpr.getBasicBlock(), this.getBasicBlock()) and + exists(LogicalAndExpr andExpr | + andExpr = orExpr.getAnOperand() and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getLesserOperand().getValue() = "0" and + globalValueNumber(gt.getGreaterOperand()) = i2 + ) and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getGreaterOperand() = + any(AddExpr se | + se.getLeftOperand().getValue().toFloat() = typeLowerBound(getType()) and + globalValueNumber(se.getRightOperand()) = i2 + ) and + globalValueNumber(gt.getLesserOperand()) = i1 + ) + ) and + exists(LogicalAndExpr andExpr | + andExpr = orExpr.getAnOperand() and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getGreaterOperand().getValue() = "0" and + globalValueNumber(gt.getLesserOperand()) = i2 + ) and + exists(StrictRelationalOperation gt | + gt = andExpr.getAnOperand() and + gt.getLesserOperand() = + any(AddExpr se | + se.getLeftOperand().getValue().toFloat() = typeUpperBound(getType()) and + globalValueNumber(se.getRightOperand()) = i2 + ) and + globalValueNumber(gt.getGreaterOperand()) = i1 + ) + ) + ) + or + // CERT recommends checking for divisor != -1 and dividor != INT_MIN + this instanceof DivOrRemOperation and + exists(EqualityOperation eop | + // GuardCondition doesn't work in this case, so just confirm that this check dominates the overflow + globalValueNumber(eop.getAnOperand()) = i1 and + eop.getAnOperand().getValue().toFloat() = + typeLowerBound(this.(DivOrRemOperation).getDividend().getType().getUnderlyingType()) + ) and + exists(EqualityOperation eop | + // GuardCondition doesn't work in this case, so just confirm that this check dominates the overflow + globalValueNumber(eop.getAnOperand()) = i2 and + eop.getAnOperand().getValue().toInt() = -1 + ) + or + // The CERT rule for signed integer overflow has a very specific pattern it recommends + // for checking for multiplication underflow/overflow. We just use a heuristic here, + // which determines if at least 4 checks of the sort `a < INT_MAX / b` are present in the code. + (this instanceof MulExpr or this instanceof AssignMulExpr) and + count(StrictRelationalOperation rel | + globalValueNumber(rel.getAnOperand()) = i1 and + globalValueNumber(rel.getAnOperand().(DivExpr).getRightOperand()) = i2 + or + globalValueNumber(rel.getAnOperand()) = i2 and + globalValueNumber(rel.getAnOperand().(DivExpr).getRightOperand()) = i1 + ) >= 4 + ) + } + + /** + * Holds if there is a correct validity check after this expression which may overflow. + * + * Only holds for unsigned expressions, as signed overflow/underflow are undefined behavior. + */ + predicate hasValidPostCheck() { exists(getAValidPostCheck()) } + + /** + * Gets a correct validity check, `gc`, after this expression which may overflow. + */ + GuardCondition getAValidPostCheck() { + this.getType().(IntegralType).isUnsigned() and + ( + exists(RelationalOperation ro | + DataFlow::localExprFlow(this, ro.getLesserOperand()) and + globalValueNumber(ro.getGreaterOperand()) = globalValueNumber(this.getAnOperand()) and + (this instanceof AddExpr or this instanceof AssignAddExpr) and + result = ro + ) + or + exists(RelationalOperation ro | + DataFlow::localExprFlow(this, ro.getGreaterOperand()) and + globalValueNumber(ro.getLesserOperand()) = globalValueNumber(this.getAnOperand()) and + (this instanceof SubExpr or this instanceof AssignSubExpr) and + result = ro + ) + ) + } +} + +private class StrictRelationalOperation extends RelationalOperation { + StrictRelationalOperation() { this.getOperator() = [">", "<"] } +} + +class DivOrRemOperation extends Operation { + DivOrRemOperation() { + this instanceof DivExpr or + this instanceof RemExpr or + this instanceof AssignDivExpr or + this instanceof AssignRemExpr + } + + Expr getDividend() { + result = this.(BinaryOperation).getLeftOperand() or + result = this.(AssignArithmeticOperation).getLValue() + } + + Expr getDivisor() { + result = this.(BinaryOperation).getRightOperand() or + result = this.(AssignArithmeticOperation).getRValue() + } +} + +/** + * Module inspired by the IntMultToLong.ql query. + */ +private module MulExprAnalysis { + /** + * As SimpleRangeAnalysis does not support reasoning about multiplication + * we create a tiny abstract interpreter for handling multiplication, which + * we invoke only after weeding out of all of trivial cases that we do + * not care about. By default, the maximum and minimum values are computed + * using SimpleRangeAnalysis. + */ + class AnalyzableExpr extends Expr { + AnalyzableExpr() { + // A integer multiplication, or an expression within an integral expression + this.(MulExpr).getType().getUnspecifiedType() instanceof IntegralType or + this.getParent() instanceof AnalyzableExpr or + this.(Conversion).getExpr() instanceof AnalyzableExpr + } + + float maxValue() { result = upperBound(this.getFullyConverted()) } + + float minValue() { result = lowerBound(this.getFullyConverted()) } + } + + class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr { + override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() } + + override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() } + } + + class MulAnalyzableExpr extends AnalyzableExpr, MulExpr { + override float maxValue() { + exists(float x1, float y1, float x2, float y2 | + x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and + x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and + y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and + y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and + result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2) + ) + } + + override float minValue() { + exists(float x1, float x2, float y1, float y2 | + x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and + x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and + y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and + y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and + result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2) + ) + } + } + + /** + * Analyze add expressions directly. This handles the case where an add expression is contributed to + * by a multiplication. + */ + class AddAnalyzableExpr extends AnalyzableExpr, AddExpr { + override float maxValue() { + result = + this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() + + this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() + } + + override float minValue() { + result = + this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() + + this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() + } + } + + /** + * Analyze sub expressions directly. This handles the case where a sub expression is contributed to + * by a multiplication. + */ + class SubAnalyzableExpr extends AnalyzableExpr, SubExpr { + override float maxValue() { + result = + this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() - + this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() + } + + override float minValue() { + result = + this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() - + this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() + } + } + + predicate overflows(MulExpr me) { + me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me) + or + me.(MulAnalyzableExpr).minValue() < exprMinVal(me) + } +} + +/** + * An analysis on safe loop counters. + */ +module LoopCounterAnalysis { + newtype LoopBound = + LoopUpperBound() or + LoopLowerBound() + + predicate isLoopBounded( + CrementOperation cop, ForStmt fs, Variable loopCounter, Expr initializer, Expr counterBound, + LoopBound boundKind, boolean equals + ) { + // Initialization sets the loop counter + ( + loopCounter = fs.getInitialization().(DeclStmt).getADeclaration() and + initializer = loopCounter.getInitializer().getExpr() + or + loopCounter.getAnAssignment() = initializer and + initializer = fs.getInitialization().(ExprStmt).getExpr() + ) and + // Condition is a relation operation on the loop counter + exists(RelationalOperation relOp | + fs.getCondition() = relOp and + (if relOp.getOperator().charAt(1) = "=" then equals = true else equals = false) + | + relOp.getGreaterOperand() = loopCounter.getAnAccess() and + relOp.getLesserOperand() = counterBound and + cop instanceof DecrementOperation and + boundKind = LoopLowerBound() + or + relOp.getLesserOperand() = loopCounter.getAnAccess() and + relOp.getGreaterOperand() = counterBound and + cop instanceof IncrementOperation and + boundKind = LoopUpperBound() + ) and + // Update is a crement operation with the loop counter + fs.getUpdate() = cop and + cop.getOperand() = loopCounter.getAnAccess() + } + + /** + * Holds if the crement operation is safe from under/overflow. + */ + predicate isCrementSafeFromOverflow(CrementOperation op) { + exists( + Expr initializer, Expr counterBound, LoopBound boundKind, boolean equals, int equalsOffset + | + isLoopBounded(op, _, _, initializer, counterBound, boundKind, equals) and + ( + equals = true and equalsOffset = 1 + or + equals = false and equalsOffset = 0 + ) + | + boundKind = LoopUpperBound() and + // upper bound of the inccrement is smaller than the maximum value representable in the type + upperBound(counterBound) + equalsOffset <= typeUpperBound(op.getType().getUnspecifiedType()) + or + // the lower bound of the decrement is larger than the smal + boundKind = LoopLowerBound() and + lowerBound(counterBound) - equalsOffset >= typeLowerBound(op.getType().getUnspecifiedType()) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/PossiblyUnsafeStringOperation.qll b/cpp/common/src/codingstandards/cpp/PossiblyUnsafeStringOperation.qll index ab454957e1..b790a4e02d 100644 --- a/cpp/common/src/codingstandards/cpp/PossiblyUnsafeStringOperation.qll +++ b/cpp/common/src/codingstandards/cpp/PossiblyUnsafeStringOperation.qll @@ -37,18 +37,25 @@ class PossiblyUnsafeStringOperation extends FunctionCall { bwc.getTarget() instanceof StrcatFunction or // Case 2: Consider the `strncpy(dest, src, n)` function. We do not - // consider `strcpy` since it is a banned function. The behavior of - // strncpy(dest, src, n) is that it will copy null terminators only if n - // > sizeof(src). If `src` is null-terminated then it will be null - // terminated if n >= sizeof(src). We take the conservative approach and - // use strictly greater. Thus this can be violated under the condition - // that n < strlen(src). Note that a buffer overflow is possible if + // consider `strcpy` since it is a banned function. + // We cannot know if the string is already null terminated or not and thus + // the conservative assumption is that it is not + // The behavior of strncpy(dest, src, n) is that if sizeof(src) < n + // then it will fill remainder of dst with ‘\0’ characters + // ie it is only in this case that it is guaranteed to null terminate + // Otherwise, dst is not terminated + // If `src` is already null-terminated then it will be null + // terminated if n >= sizeof(src). but we do not assume on this. + // Note that a buffer overflow is possible if // `n` is greater than sizeof(dest). The point of this query is not to // check for buffer overflows but we would certainly want to indicate // this would be a case where a string will not be null terminated. bwc.getTarget() instanceof StrcpyFunction and ( - (bwc.getExplicitLimit() / bwc.getCharSize()) < getBufferSize(src, _) or + // n <= sizeof(src) might not null terminate + (bwc.getExplicitLimit() / bwc.getCharSize()) <= getBufferSize(src, _) + or + // sizeof(dest) < n might not null terminate getBufferSize(dest, _) < (bwc.getExplicitLimit() / bwc.getCharSize()) ) or diff --git a/cpp/common/src/codingstandards/cpp/PreprocessorDirective.qll b/cpp/common/src/codingstandards/cpp/PreprocessorDirective.qll index 4194ad65e3..ca943742d9 100644 --- a/cpp/common/src/codingstandards/cpp/PreprocessorDirective.qll +++ b/cpp/common/src/codingstandards/cpp/PreprocessorDirective.qll @@ -30,3 +30,78 @@ PreprocessorDirective isLocatedInAMacroInvocation(MacroInvocation m) { result = p ) } + +/** + * An `if` or `elif` preprocessor branch. + */ +class PreprocessorIfOrElif extends PreprocessorBranch { + PreprocessorIfOrElif() { + this instanceof PreprocessorIf or + this instanceof PreprocessorElif + } +} + +/** + * Holds if the preprocessor directive `m` is located at `filepath` and `startline`. + */ +pragma[noinline] +predicate hasPreprocessorLocation(PreprocessorDirective m, string filepath, int startline) { + m.getLocation().hasLocationInfo(filepath, startline, _, _, _) +} + +/** + * Holds if `first` and `second` are a pair of branch directives in the same file, such that they + * share the same root if condition. + */ +pragma[noinline] +private predicate isBranchDirectivePair( + PreprocessorBranchDirective first, PreprocessorBranchDirective second, string filepath, + int b1StartLocation, int b2StartLocation +) { + first.getIf() = second.getIf() and + not first = second and + hasPreprocessorLocation(first, filepath, b1StartLocation) and + hasPreprocessorLocation(second, filepath, b2StartLocation) and + b1StartLocation < b2StartLocation +} + +/** + * Holds if `bd` is a branch directive in the range `filepath`, `startline`, `endline`. + */ +pragma[noinline] +predicate isBranchDirectiveRange( + PreprocessorBranchDirective bd, string filepath, int startline, int endline +) { + hasPreprocessorLocation(bd, filepath, startline) and + exists(PreprocessorBranchDirective next | + next = bd.getNext() and + // Avoid referencing filepath here, otherwise the optimiser will try to join + // on it + hasPreprocessorLocation(next, _, endline) + ) +} + +/** + * Holds if the macro `m` is defined within the branch directive `bd`. + */ +pragma[noinline] +predicate isMacroDefinedWithinBranch(PreprocessorBranchDirective bd, Macro m) { + exists(string filepath, int startline, int endline, int macroline | + isBranchDirectiveRange(bd, filepath, startline, endline) and + hasPreprocessorLocation(m, filepath, macroline) and + startline < macroline and + endline > macroline + ) +} + +/** + * Holds if the pair of macros are "conditional" i.e. only one of the macros is followed in any + * particular compilation of the containing file. + */ +predicate mutuallyExclusiveBranchDirectiveMacros(Macro firstMacro, Macro secondMacro) { + exists(PreprocessorBranchDirective b1, PreprocessorBranchDirective b2 | + isBranchDirectivePair(b1, b2, _, _, _) and + isMacroDefinedWithinBranch(b1, firstMacro) and + isMacroDefinedWithinBranch(b2, secondMacro) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/ReadErrorsAndEOF.qll b/cpp/common/src/codingstandards/cpp/ReadErrorsAndEOF.qll index 2cf701c7f6..94e7f89796 100644 --- a/cpp/common/src/codingstandards/cpp/ReadErrorsAndEOF.qll +++ b/cpp/common/src/codingstandards/cpp/ReadErrorsAndEOF.qll @@ -1,11 +1,15 @@ import cpp -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.standardlibrary.FileAccess /** * any call to function `feof()` or `ferror()` */ -abstract class FeofFerrorCall extends FunctionCall { } +abstract class FeofFerrorCall extends FileAccess { + override VariableAccess getFileExpr() { + result = [this.getArgument(0), this.getArgument(0).(AddressOfExpr).getAnOperand()] + } +} class FeofCall extends FeofFerrorCall { FeofCall() { this.getTarget().hasGlobalName("feof") } @@ -15,11 +19,6 @@ class FerrorCall extends FeofFerrorCall { FerrorCall() { this.getTarget().hasGlobalName("ferror") } } -pragma[inline] -predicate accessSameTarget(VariableAccess va1, VariableAccess va2) { - va1.getTarget() = va2.getTarget() -} - predicate isShortCircuitedEdge(ControlFlowNode fst, ControlFlowNode snd) { fst = any(LogicalAndExpr andOp).getLeftOperand() and snd = fst.getAFalseSuccessor() or @@ -36,7 +35,7 @@ ControlFlowNode feofUnchecked(InBandErrorReadFunctionCall read) { not isShortCircuitedEdge(mid, result) and result = mid.getASuccessor() and //Stop recursion on call to feof/ferror on the correct file - not accessSameTarget(result.(FeofCall).getArgument(0), read.getFileExpr()) + not sameFileSource(result.(FeofCall), read) ) } @@ -50,7 +49,7 @@ ControlFlowNode ferrorUnchecked(InBandErrorReadFunctionCall read) { 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), read.getFileExpr()) + not sameFileSource(result.(FerrorCall), read) ) } @@ -112,6 +111,6 @@ predicate missingEOFWEOFChecks(InBandErrorReadFunctionCall read) { // another char is read before the comparison to EOF exists(FileReadFunctionCall fc | macroUnchecked(read) = fc and - accessSameTarget(read.getFileExpr(), fc.getFileExpr()) + sameFileSource(read, fc) ) } diff --git a/cpp/common/src/codingstandards/cpp/Realloc.qll b/cpp/common/src/codingstandards/cpp/Realloc.qll new file mode 100644 index 0000000000..71acb7d7b1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Realloc.qll @@ -0,0 +1,18 @@ +import cpp +import codingstandards.cpp.CodingStandards + +class ReallocCall extends FunctionCall { + ReallocCall() { getTarget().hasGlobalOrStdName("realloc") } + + Expr getSizeArgument() { result = getArgument(1) } + + predicate sizeIsExactlyZero() { + upperBound(getSizeArgument().getConversion()) = 0 and + lowerBound(getSizeArgument().getConversion()) = 0 + } + + predicate sizeMayBeZero() { + upperBound(getSizeArgument().getConversion()) >= 0 and + lowerBound(getSizeArgument().getConversion()) <= 0 + } +} diff --git a/cpp/common/src/codingstandards/cpp/RestrictedRangeAnalysis.qll b/cpp/common/src/codingstandards/cpp/RestrictedRangeAnalysis.qll new file mode 100644 index 0000000000..d92b46335d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/RestrictedRangeAnalysis.qll @@ -0,0 +1,2243 @@ +import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.valuenumbering.HashCons + +/** + * A fork of SimpleRangeAnalysis.qll, which is intended to only give results + * with a conservative basis. Forked from codeql/cpp-all@1.4.2. + * + * For instance, since range analysis is local, a function call (e.g. `f()`) is + * given the widest possible range in the original library. In this fork, we do + * not provide any result. + * + * Original library level doc comment from SimpleRangeAnalysis.qll: + * + * > Simple range analysis library. Range analysis is usually done as an + * > abstract interpretation over the lattice of range values. (A range is a + * > pair, containing a lower and upper bound for the value.) The problem + * > with this approach is that the lattice is very tall, which means it can + * > take an extremely large number of iterations to find the least fixed + * > point. This example illustrates the problem: + * + * > int count = 0; + * > for (; p; p = p->next) { + * > count = count+1; + * > } + * + * > The range of 'count' is initially (0,0), then (0,1) on the second + * > iteration, (0,2) on the third iteration, and so on until we eventually + * > reach maxInt. + * + * > This library uses a crude solution to the problem described above: if + * > the upper (or lower) bound of an expression might depend recursively on + * > itself then we round it up (down for lower bounds) to one of a fixed set + * > of values, such as 0, 1, 2, 256, and +Inf. This limits the height of the + * > lattice which ensures that the analysis will terminate in a reasonable + * > amount of time. This solution is similar to the abstract interpretation + * > technique known as 'widening', but it is less precise because we are + * > unable to inspect the bounds from the previous iteration of the fixed + * > point computation. For example, widening might be able to deduce that + * > the lower bound is -11 but we would approximate it to -16. + * + * > QL does not allow us to compute an aggregate over a recursive + * > sub-expression, so we cannot compute the minimum lower bound and maximum + * > upper bound during the recursive phase of the query. Instead, the + * > recursive phase computes a set of lower bounds and a set of upper bounds + * > for each expression. We compute the minimum lower bound and maximum + * > upper bound after the recursion is finished. This is another reason why + * > we need to limit the number of bounds per expression, because they will + * > all be stored until the recursive phase is finished. + * + * > The ranges are represented using a pair of floating point numbers. This + * > is simpler than using integers because floating point numbers cannot + * > overflow and wrap. It is also convenient because we can detect overflow + * > and negative overflow by looking for bounds that are outside the range + * > of the type. + * + * The differences between this library and the original are: + * - The `largeValue()` predicate, with a value of 1e15, used in place of + * `exprMaxVal()` and `exprMinVal()` in most places. + * - Support for range analysis extensions removed for simplicity. + * - Additional predicates have been added to check for non-zero values, and guards + * against values equalling zero. + * - Division by a constant value has been added as a supported operations. Division + * is always widened, as support for division introduces examples of significantly + * longer chains of dependent expressions than merely addition and multiplication. + * These long chains can introduce exponential growth in the number of candidate + * bounds, even without recursive binary operations, so widening is always applied. + * - Division operations where the range of the denominator includes zero (and its + * not guarded to be non-zero) and produce infinite upper and/or lower bounds. + * - Support for monotonically increasing and decreasing math functions has been + * added, including `log`, `exp`, `asin`, `atan`, `sinh`, and `sqrt`. If a math + * function increases or decreases monotonically, then the lower or upper bound of + * its input can be used to compute the lower or upper bound of the function call. + * Not all math functions increase or decrease monotonically. + */ +module RestrictedRangeAnalysis { + import cpp + private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils as Util + import semmle.code.cpp.rangeanalysis.RangeSSA + import SimpleRangeAnalysisCached + private import semmle.code.cpp.rangeanalysis.NanAnalysis + + float largeValue() { result = 1000000000000000.0 } + + /** + * This fixed set of lower bounds is used when the lower bounds of an + * expression are recursively defined. The inferred lower bound is rounded + * down to the nearest lower bound in the fixed set. This restricts the + * height of the lattice, which prevents the analysis from exploding. + * + * Note: these bounds were chosen fairly arbitrarily. Feel free to add more + * bounds to the set if it helps on specific examples and does not make + * performance dramatically worse on large codebases, such as libreoffice. + */ + private float wideningLowerBounds(ArithmeticType t) { + result = 2.0 or + result = 1.0 or + result = 0.0 or + result = -1.0 or + result = -2.0 or + result = -8.0 or + result = -16.0 or + result = -128.0 or + result = -256.0 or + result = -32768.0 or + result = -65536.0 or + result = -largeValue() or + result = Util::typeLowerBound(t) + //result = max(float v | v = Util::typeLowerBound(t) or v = -largeValue()) + } + + /** See comment for `wideningLowerBounds`, above. */ + private float wideningUpperBounds(ArithmeticType t) { + result = -2.0 or + result = -1.0 or + result = 0.0 or + result = 1.0 or + result = 2.0 or + result = 7.0 or + result = 15.0 or + result = 127.0 or + result = 255.0 or + result = 32767.0 or + result = 65535.0 or + result = largeValue() or + result = Util::typeUpperBound(t) + //result = min(float v | v = Util::typeLowerBound(t) or v = largeValue()) + } + + /** + * Gets the value of the expression `e`, if it is a constant. + * This predicate also handles the case of constant variables initialized in different + * compilation units, which doesn't necessarily have a getValue() result from the extractor. + */ + private string getValue(Expr e) { + if exists(e.getValue()) + then result = e.getValue() + else + /* + * It should be safe to propagate the initialization value to a variable if: + * The type of v is const, and + * The type of v is not volatile, and + * Either: + * v is a local/global variable, or + * v is a static member variable + */ + + exists(VariableAccess access, StaticStorageDurationVariable v | + not v.getUnderlyingType().isVolatile() and + v.getUnderlyingType().isConst() and + e = access and + v = access.getTarget() and + result = getValue(v.getAnAssignedValue()) + ) + } + + private float varMaxVal(Variable v) { + result = min(float f | f = Util::varMaxVal(v) or f = largeValue()) + } + + private float varMinVal(Variable v) { + result = max(float f | f = Util::varMinVal(v) or f = -largeValue()) + } + + private float exprMaxVal(Expr e) { + result = min(float f | f = Util::exprMaxVal(e) or f = largeValue()) + } + + private float exprMinVal(Expr e) { + result = max(float f | f = Util::exprMinVal(e) or f = -largeValue()) + } + + /** + * A bitwise `&` expression in which both operands are unsigned, or are effectively + * unsigned due to being a non-negative constant. + */ + private class UnsignedBitwiseAndExpr extends BitwiseAndExpr { + UnsignedBitwiseAndExpr() { + ( + this.getLeftOperand() + .getFullyConverted() + .getType() + .getUnderlyingType() + .(IntegralType) + .isUnsigned() or + getValue(this.getLeftOperand().getFullyConverted()).toInt() >= 0 + ) and + ( + this.getRightOperand() + .getFullyConverted() + .getType() + .getUnderlyingType() + .(IntegralType) + .isUnsigned() or + getValue(this.getRightOperand().getFullyConverted()).toInt() >= 0 + ) + } + } + + /** + * Gets the floor of `v`, with additional logic to work around issues with + * large numbers. + */ + bindingset[v] + float safeFloor(float v) { + // return the floor of v + v.abs() < 2.pow(31) and + result = v.floor() + or + // `floor()` doesn't work correctly on large numbers (since it returns an integer), + // so fall back to unrounded numbers at this scale. + not v.abs() < 2.pow(31) and + result = v + } + + /** A `MulExpr` where exactly one operand is constant. */ + private class MulByConstantExpr extends MulExpr { + float constant; + Expr operand; + + MulByConstantExpr() { + exists(Expr constantExpr | + this.hasOperands(constantExpr, operand) and + constant = getValue(constantExpr.getFullyConverted()).toFloat() and + not exists(getValue(operand.getFullyConverted()).toFloat()) + ) + } + + /** Gets the value of the constant operand. */ + float getConstant() { result = constant } + + /** Gets the non-constant operand. */ + Expr getOperand() { result = operand } + } + + private class UnsignedMulExpr extends MulExpr { + UnsignedMulExpr() { + this.getType().(IntegralType).isUnsigned() and + // Avoid overlap. It should be slightly cheaper to analyze + // `MulByConstantExpr`. + not this instanceof MulByConstantExpr + } + } + + /** + * Holds if `expr` is effectively a multiplication of `operand` with the + * positive constant `positive`. + */ + private predicate effectivelyMultipliesByPositive(Expr expr, Expr operand, float positive) { + operand = expr.(MulByConstantExpr).getOperand() and + positive = expr.(MulByConstantExpr).getConstant() and + positive >= 0.0 // includes positive zero + or + operand = expr.(UnaryPlusExpr).getOperand() and + positive = 1.0 + or + operand = expr.(CommaExpr).getRightOperand() and + positive = 1.0 + or + operand = expr.(StmtExpr).getResultExpr() and + positive = 1.0 + } + + /** + * Holds if `expr` is effectively a multiplication of `operand` with the + * negative constant `negative`. + */ + private predicate effectivelyMultipliesByNegative(Expr expr, Expr operand, float negative) { + operand = expr.(MulByConstantExpr).getOperand() and + negative = expr.(MulByConstantExpr).getConstant() and + negative < 0.0 // includes negative zero + or + operand = expr.(UnaryMinusExpr).getOperand() and + negative = -1.0 + } + + private class AssignMulByConstantExpr extends AssignMulExpr { + float constant; + + AssignMulByConstantExpr() { + constant = getValue(this.getRValue().getFullyConverted()).toFloat() + } + + float getConstant() { result = constant } + } + + private class AssignMulByPositiveConstantExpr extends AssignMulByConstantExpr { + AssignMulByPositiveConstantExpr() { constant >= 0.0 } + } + + private class AssignMulByNegativeConstantExpr extends AssignMulByConstantExpr { + AssignMulByNegativeConstantExpr() { constant < 0.0 } + } + + private class UnsignedAssignMulExpr extends AssignMulExpr { + UnsignedAssignMulExpr() { + this.getType().(IntegralType).isUnsigned() and + // Avoid overlap. It should be slightly cheaper to analyze + // `AssignMulByConstantExpr`. + not this instanceof AssignMulByConstantExpr + } + } + + /** + * Holds if `expr` is effectively a division of `operand` with the + * positive constant `positive`. + */ + private predicate dividesByPositive(DivExpr expr, Expr operand, float positive) { + operand = expr.(DivExpr).getLeftOperand() and + positive = expr.(DivExpr).getRightOperand().getValue().toFloat() and + positive > 0.0 // doesn't include zero + } + + /** + * Holds if `expr` is effectively a division of `operand` with the + * negative constant `negative`. + */ + private predicate dividesByNegative(Expr expr, Expr operand, float negative) { + operand = expr.(DivExpr).getLeftOperand() and + negative = getValue(expr.(DivExpr).getRightOperand().getFullyConverted()).toFloat() and + negative < 0.0 // doesn't include zero + } + + /** + * Holds if `expr` may divide by zero. Excludes dividing a constant zero divided by zero, + * which produces NaN instead of an infinite value. + */ + predicate dividesNonzeroByZero(Expr expr) { + exists(Expr divisor, Expr numerator | + divisor = expr.(DivExpr).getRightOperand() and + numerator = expr.(DivExpr).getLeftOperand() and + getTruncatedLowerBounds(divisor) <= 0.0 and + getTruncatedUpperBounds(divisor) >= 0.0 and + not isCheckedNotZero(divisor) and + not getValue(numerator).toFloat() = 0.0 + ) + } + + bindingset[name] + Function getMathVariants(string name) { + result.hasGlobalOrStdName([name, name + "f", name + "l"]) + } + + /** + * New support added for mathematical functions that either monotonically increase, or decrease, + * or that have a known lower or upper bound. + * + * For instance, log(x) monotonically increases over x, and acos(x) monotonically decreases, + * while sin(x) has a known output range of -1 to 1. + * + * `pow` is especially common so minimal work is done to support that here as well. `pow(c, x)` + * monotonically increases or decreases over `x` if `c` is a constant, though the reverse is not + * true except in special cases. + */ + newtype TSupportedMathFunctionCall = + /* A monotonically increasing function call. `extra` is a constant for `pow(x, c)`. */ + TMonotonicIncrease(FunctionCall fc, Expr input, float extra) { + // Note: Codeql has no default implementation in codeql for exp2, atanh, acosh, asinh, or + // log1p so we haven't taken the time to support them yet. + fc.getTarget() = + getMathVariants(["log", "log2", "log10", "exp", "asin", "atan", "sinh", "sqrt"]) and + input = fc.getArgument(0) and + extra = 0.0 + or + // Notes: pow is monotonic if the base argument is constant, increasing if the base is greater + // than 1 or between -1 and 0, and decreasing otherwise. A constant power is monotonic over the + // base in the positive or negative domain, but distinguishing those separately can introduce + // non-monotonic recursion errors. + fc.getTarget() = getMathVariants("pow") and + extra = fc.getArgument(0).getValue().toFloat() and + ( + extra > 1.0 + or + extra < 0.0 and extra > -1.0 + ) and + input = fc.getArgument(1) + } or + /* A monotonically decreasing function call. `extra` is a constant for `pow(x, c)`. */ + TMonotonicDecrease(FunctionCall fc, Expr input, float extra) { + fc.getTarget() = getMathVariants(["acos"]) and + input = fc.getArgument(0) and + extra = 0.0 + or + fc.getTarget() = getMathVariants("pow") and + extra = fc.getArgument(0).getValue().toFloat() and + ( + extra < -1.0 + or + extra > 0.0 and extra < 1.0 + ) and + input = fc.getArgument(1) + } or + /* A non-mononotic function call with a known lower bound. */ + TNonMonotonicLowerBound(FunctionCall fc, float lb) { + fc.getTarget() = getMathVariants("cosh") and + lb = 1.0 + or + fc.getTarget() = getMathVariants(["cos", "sin"]) and + lb = -1.0 + } or + /* A non-mononotic function call with a known upper bound. */ + TNonMonotonicUpperBound(FunctionCall fc, float lb) { + fc.getTarget() = getMathVariants(["cos", "sin"]) and + lb = 1.0 + } + + /** + * A function call that is supported by range analysis. + */ + class SupportedFunctionCall extends TSupportedMathFunctionCall { + string toString() { + exists(FunctionCall fc | + this = TMonotonicIncrease(fc, _, _) and + result = "Monotonic increase " + fc.getTarget().getName() + or + this = TMonotonicDecrease(fc, _, _) and + result = "Monotonic decrease " + fc.getTarget().getName() + or + this = TNonMonotonicLowerBound(fc, _) and + result = "Nonmonotonic lower bound " + fc.getTarget().getName() + or + this = TNonMonotonicUpperBound(fc, _) and + result = "Nonmonotonic upper bound " + fc.getTarget().getName() + ) + } + + /** Get the function call node this algebraic type corresponds to. */ + FunctionCall getFunctionCall() { + this = TMonotonicIncrease(result, _, _) + or + this = TMonotonicDecrease(result, _, _) + or + this = TNonMonotonicLowerBound(result, _) + or + this = TNonMonotonicUpperBound(result, _) + } + + /** Get the function name (`sin`, `pow`, etc.) without the `l` or `f` suffix. */ + bindingset[this, result] + string getBaseFunctionName() { getMathVariants(result) = getFunctionCall().getTarget() } + + /** + * Compute a result bound based on an input value and an extra constant value. + * + * The functions `getUpperBound()` and `getLowerBound()` automatically handle the differences + * between monotonically increasing and decreasing functions, and provide the input value. The + * `extra` float exists to support `pow(x, c)` for the constant `c`, otherwise it is `0.0`. + */ + bindingset[value, extra, this] + float compute(float value, float extra) { + exists(string name | name = getBaseFunctionName() | + name = "log" and + result = value.log() + or + name = "log2" and + result = value.log2() + or + name = "log10" and + result = value.log10() + or + name = "exp" and + result = value.exp() + or + name = "asin" and + result = value.asin() + or + name = "atan" and + result = value.atan() + or + name = "acos" and + result = value.acos() + or + name = "sinh" and + result = value.sinh() + or + name = "sqrt" and + result = value.sqrt() + or + name = "pow" and + result = extra.pow(value) + ) + } + + /** + * Get the lower bound of this function, based on its fixed range (if it has one) or based on + * the lower or upper bound of its input, if it is a monotonically increasing or decreasing + * function. + */ + float getLowerBound() { + this = TNonMonotonicLowerBound(_, result) + or + exists(Expr expr, float bound, float extra | + ( + this = TMonotonicIncrease(_, expr, extra) and + bound = getFullyConvertedLowerBounds(expr) + or + this = TMonotonicDecrease(_, expr, extra) and + bound = getFullyConvertedUpperBounds(expr) + ) and + result = compute(bound, extra) + ) + } + + /** + * Get the lower bound of this function, based on its fixed range (if it has one) or based on + * the lower or upper bound of its input, if it is a monotonically increasing or decreasing + * function. + */ + float getUpperBound() { + this = TNonMonotonicUpperBound(_, result) + or + exists(Expr expr, float bound, float extra | + ( + this = TMonotonicIncrease(_, expr, extra) and + bound = getFullyConvertedUpperBounds(expr) + or + this = TMonotonicDecrease(_, expr, extra) and + bound = getFullyConvertedLowerBounds(expr) + ) and + result = compute(bound, extra) + ) + } + } + + predicate supportedMathFunction(FunctionCall fc) { + exists(SupportedFunctionCall sfc | sfc.getFunctionCall() = fc) + } + + /** + * Holds if `expr` is checked with a guard to not be zero. + * + * Since our range analysis only tracks an upper and lower bound, that means if a variable has + * range [-10, 10], its range includes zero. In the body of an if statement that checks it's not + * equal to zero, we cannot update the range to reflect that as the upper and lower bounds are + * not changed. This problem is not the case for gt, lt, gte, lte, or ==, as these can be used to + * create a new subset range that does not include zero. + * + * It is important to know if an expr may be zero to avoid division by zero creating infinities. + */ + predicate isCheckedNotZero(Expr expr) { + exists(RangeSsaDefinition def, StackVariable v, VariableAccess guardVa, Expr guard | + // This is copied from getGuardedUpperBound, which says its only an approximation. This is + // indeed wrong in many cases. + def.isGuardPhi(v, guardVa, guard, _) and + exists(unique(BasicBlock b | b = def.(BasicBlock).getAPredecessor())) and + expr = def.getAUse(v) and + isNEPhi(v, def, guardVa, 0) + ) + or + guardedHashConsNotEqualZero(expr) + } + + predicate guardedHashConsNotEqualZero(Expr e) { + /* Note Boolean cmpEq, false means cmpNeq */ + exists(Expr check, Expr val, int valVal, GuardCondition guard, boolean cmpEq | + hashCons(check) = hashCons(e) and + valVal = getValue(val).toFloat() and + guard.controls(e.getBasicBlock(), cmpEq) and + ( + guard.comparesEq(check, val, -valVal, false, cmpEq) or + guard.comparesEq(val, check, -valVal, false, cmpEq) + ) + ) + } + + /** Set of expressions which we know how to analyze. */ + predicate analyzableExpr(Expr e) { + // The type of the expression must be arithmetic. We reuse the logic in + // `exprMinVal` to check this. + exists(Util::exprMinVal(e)) and + ( + exists(getValue(e).toFloat()) + or + effectivelyMultipliesByPositive(e, _, _) + or + effectivelyMultipliesByNegative(e, _, _) + or + dividesByPositive(e, _, _) + or + dividesByNegative(e, _, _) + or + // Introduces non-monotonic recursion. However, analysis mostly works with this + // commented out. + // or + // dividesNonzeroByZero(e) + e instanceof DivExpr // TODO: confirm this is OK + or + supportedMathFunction(e) + or + e instanceof MinExpr + or + e instanceof MaxExpr + or + e instanceof ConditionalExpr + or + e instanceof AddExpr + or + e instanceof SubExpr + or + e instanceof UnsignedMulExpr + or + e instanceof AssignExpr + or + e instanceof AssignAddExpr + or + e instanceof AssignSubExpr + or + e instanceof UnsignedAssignMulExpr + or + e instanceof AssignMulByConstantExpr + or + e instanceof CrementOperation + or + e instanceof RemExpr + or + // A conversion is analyzable, provided that its child has an arithmetic + // type. (Sometimes the child is a reference type, and so does not get + // any bounds.) Rather than checking whether the type of the child is + // arithmetic, we reuse the logic that is already encoded in + // `exprMinVal`. + exists(Util::exprMinVal(e.(Conversion).getExpr())) + or + // Also allow variable accesses, provided that they have SSA + // information. + exists(RangeSsaDefinition def | e = def.getAUse(_)) + or + e instanceof UnsignedBitwiseAndExpr + or + // `>>` by a constant + exists(getValue(e.(RShiftExpr).getRightOperand())) + ) + } + + /** + * Set of definitions that this definition depends on. The transitive + * closure of this relation is used to detect definitions which are + * recursively defined, so that we can prevent the analysis from exploding. + * + * The structure of `defDependsOnDef` and its helper predicates matches the + * structure of `getDefLowerBoundsImpl` and + * `getDefUpperBoundsImpl`. Therefore, if changes are made to the structure + * of the main analysis algorithm then matching changes need to be made + * here. + */ + private predicate defDependsOnDef( + RangeSsaDefinition def, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar + ) { + // Definitions with a defining value. + exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar)) + or + // Assignment operations with a defining value + exists(AssignOperation assignOp | + analyzableExpr(assignOp) and + def = assignOp and + def.getAVariable() = v and + exprDependsOnDef(assignOp, srcDef, srcVar) + ) + or + exists(CrementOperation crem | + def = crem and + def.getAVariable() = v and + exprDependsOnDef(crem.getOperand(), srcDef, srcVar) + ) + or + // Phi nodes. + phiDependsOnDef(def, v, srcDef, srcVar) + } + + /** + * Helper predicate for `defDependsOnDef`. This predicate matches + * the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`. + */ + private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVariable srcVar) { + exists(Expr operand | + effectivelyMultipliesByNegative(e, operand, _) and + exprDependsOnDef(operand, srcDef, srcVar) + ) + or + exists(Expr operand | + effectivelyMultipliesByPositive(e, operand, _) and + exprDependsOnDef(operand, srcDef, srcVar) + ) + or + exists(Expr operand | + (dividesByPositive(e, operand, _) or dividesByNegative(e, operand, _)) and + exprDependsOnDef(operand, srcDef, srcVar) + ) + or + exists(DivExpr div | div = e | exprDependsOnDef(div.getAnOperand(), srcDef, srcVar)) + or + exists(MinExpr minExpr | e = minExpr | exprDependsOnDef(minExpr.getAnOperand(), srcDef, srcVar)) + or + exists(MaxExpr maxExpr | e = maxExpr | exprDependsOnDef(maxExpr.getAnOperand(), srcDef, srcVar)) + or + exists(ConditionalExpr condExpr | e = condExpr | + exprDependsOnDef(condExpr.getAnOperand(), srcDef, srcVar) + ) + or + exists(AddExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getAnOperand(), srcDef, srcVar)) + or + exists(SubExpr subExpr | e = subExpr | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar)) + or + exists(UnsignedMulExpr mulExpr | e = mulExpr | + exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar) + ) + or + exists(AssignExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getRValue(), srcDef, srcVar)) + or + exists(AssignAddExpr addExpr | e = addExpr | + exprDependsOnDef(addExpr.getAnOperand(), srcDef, srcVar) + ) + or + exists(AssignSubExpr subExpr | e = subExpr | + exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar) + ) + or + exists(UnsignedAssignMulExpr mulExpr | e = mulExpr | + exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar) + ) + or + exists(AssignMulByConstantExpr mulExpr | e = mulExpr | + exprDependsOnDef(mulExpr.getLValue(), srcDef, srcVar) + ) + or + exists(CrementOperation crementExpr | e = crementExpr | + exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar) + ) + or + exists(RemExpr remExpr | e = remExpr | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar)) + or + exists(Conversion convExpr | e = convExpr | + exprDependsOnDef(convExpr.getExpr(), srcDef, srcVar) + ) + or + // unsigned `&` + exists(UnsignedBitwiseAndExpr andExpr | + andExpr = e and + exprDependsOnDef(andExpr.getAnOperand(), srcDef, srcVar) + ) + or + // `>>` by a constant + exists(RShiftExpr rs | + rs = e and + exists(getValue(rs.getRightOperand())) and + exprDependsOnDef(rs.getLeftOperand(), srcDef, srcVar) + ) + or + e = srcDef.getAUse(srcVar) + } + + /** + * Helper predicate for `defDependsOnDef`. This predicate matches + * the structure of `getPhiLowerBounds` and `getPhiUpperBounds`. + */ + private predicate phiDependsOnDef( + RangeSsaDefinition phi, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar + ) { + exists(VariableAccess access, Expr guard | phi.isGuardPhi(v, access, guard, _) | + exprDependsOnDef(guard.(ComparisonOperation).getAnOperand(), srcDef, srcVar) or + exprDependsOnDef(access, srcDef, srcVar) + ) + or + srcDef = phi.getAPhiInput(v) and srcVar = v + } + + /** The transitive closure of `defDependsOnDef`. */ + private predicate defDependsOnDefTransitively( + RangeSsaDefinition def, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar + ) { + defDependsOnDef(def, v, srcDef, srcVar) + or + exists(RangeSsaDefinition midDef, StackVariable midVar | + defDependsOnDef(def, v, midDef, midVar) + | + defDependsOnDefTransitively(midDef, midVar, srcDef, srcVar) + ) + } + + /** The set of definitions that depend recursively on themselves. */ + private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) { + defDependsOnDefTransitively(def, v, def, v) + } + + /** + * Holds if the bounds of `e` depend on a recursive definition, meaning that + * `e` is likely to have many candidate bounds during the main recursion. + */ + private predicate isRecursiveExpr(Expr e) { + exists(RangeSsaDefinition def, StackVariable v | exprDependsOnDef(e, def, v) | + isRecursiveDef(def, v) + ) + } + + /** + * Holds if `binop` is a binary operation that's likely to be assigned a + * quadratic (or more) number of candidate bounds during the analysis. This can + * happen when two conditions are satisfied: + * 1. It is likely there are many more candidate bounds for `binop` than for + * its operands. For example, the number of candidate bounds for `x + y`, + * denoted here nbounds(`x + y`), will be O(nbounds(`x`) * nbounds(`y`)). + * In contrast, nbounds(`b ? x : y`) is only O(nbounds(`x`) + nbounds(`y`)). + * 2. Both operands of `binop` are recursively determined and are therefore + * likely to have a large number of candidate bounds. + */ + private predicate isRecursiveBinary(BinaryOperation binop) { + ( + binop instanceof UnsignedMulExpr + or + binop instanceof AddExpr + or + binop instanceof SubExpr + ) and + isRecursiveExpr(binop.getLeftOperand()) and + isRecursiveExpr(binop.getRightOperand()) + } + + private predicate applyWideningToBinary(BinaryOperation op) { + // Original behavior: + isRecursiveBinary(op) + or + // As we added support for DivExpr, we found cases of combinatorial explosion that are not + // caused by recursion. Given expr `x` that depends on a phi node that has evaluated y unique + // values, `x + x` will in the worst case evaluate to y^2 unique values, even if `x` is not + // recursive. By adding support for division, we have revealed certain pathological cases in + // open source code, for instance `posix_time_from_utc` from boringssl. We can reduce this + // greatly by widening, and targeting division effectively reduces the chains of evaluations + // that cause this issue while preserving the original behavior. + // + // There is also a set of functions intended to estimate the combinations of phi nodes each + // expression depends on, which could be used to accurately widen only expensive nodes. However, + // that estimation is more involved than it may seem, and hasn't yet resulted in a net + // improvement. See `estimatedPhiCombinationsExpr` and `estimatedPhiCombinationsDef`. + // + // This approach currently has the best performance. + op instanceof DivExpr + } + + /** + * Recursively scan this expr to see how many phi nodes it depends on. Binary expressions + * induce a combination effect, so `a + b` where `a` depends on 3 phi nodes and `b` depends on 4 + * will induce 3*4 = 12 phi node combinations. + * + * This currently requires additional optimization to be useful in practice. + */ + int estimatedPhiCombinationsExpr(Expr expr) { + if isRecursiveExpr(expr) + then + // Assume 10 values were computed to analyze recursive expressions. + result = 10 + else ( + exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | + def.isPhiNode(v) and + result = estimatedPhiCombinationsDef(def, v) + ) + or + exists(BinaryOperation binop | + binop = expr and + result = + estimatedPhiCombinationsExpr(binop.getLeftOperand()) * + estimatedPhiCombinationsExpr(binop.getRightOperand()) + ) + or + not expr instanceof BinaryOperation and + exists(RangeSsaDefinition def, StackVariable v | exprDependsOnDef(expr, def, v) | + result = estimatedPhiCombinationsDef(def, v) + ) + or + not expr instanceof BinaryOperation and + not exprDependsOnDef(expr, _, _) and + result = 1 + ) + } + + /** + * Recursively scan this def to see how many phi nodes it depends on. + * + * If this def is a phi node, it sums its downstream cost and adds one to account for itself, + * which is not exactly correct. + * + * This def may also be a crement expression (not currently supported), or an assign expr + * (currently not supported), or an unanalyzable expression which is the root of the recursion + * and given a value of 1. + */ + language[monotonicAggregates] + int estimatedPhiCombinationsDef(RangeSsaDefinition def, StackVariable v) { + if isRecursiveDef(def, v) + then + // Assume 10 values were computed to analyze recursive expressions. + result = 10 + else ( + if def.isPhiNode(v) + then + exists(Expr e | e = def.getAUse(v) | + result = + 1 + + sum(RangeSsaDefinition srcDef | + srcDef = def.getAPhiInput(v) + | + estimatedPhiCombinationsDef(srcDef, v) + ) + ) + else ( + exists(Expr expr | assignmentDef(def, v, expr) | + result = estimatedPhiCombinationsExpr(expr) + ) + or + v = def.getAVariable() and + not assignmentDef(def, v, _) and + result = 1 + ) + ) + } + + /** + * We distinguish 3 kinds of RangeSsaDefinition: + * + * 1. Definitions with a defining value. + * For example: x = y+3 is a definition of x with defining value y+3. + * + * 2. Phi nodes: x3 = phi(x0,x1,x2) + * + * 3. Unanalyzable definitions. + * For example: a parameter is unanalyzable because we know nothing + * about its value. We assign these range [-largeValue(), largeValue()] + * + * This predicate finds all the definitions in the first set. + */ + private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) { + Util::getVariableRangeType(v) instanceof ArithmeticType and + ( + def = v.getInitializer().getExpr() and def = expr + or + exists(AssignExpr assign | + def = assign and + assign.getLValue() = v.getAnAccess() and + expr = assign.getRValue() + ) + ) + } + + /** See comment above assignmentDef. */ + private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) { + assignmentDef(def, v, _) + or + analyzableExpr(def.(AssignOperation)) and + v = def.getAVariable() + or + analyzableExpr(def.(CrementOperation)) and + v = def.getAVariable() + or + phiDependsOnDef(def, v, _, _) + } + + predicate canBoundExpr(Expr e) { + exists(RangeSsaDefinition def, StackVariable v | e = def.getAUse(v) | analyzableDef(def, v)) + or + analyzableExpr(e) + or + exists(getGuardedUpperBound(e)) + or + lowerBoundFromGuard(e, _, _, _) + } + + /** + * Computes a normal form of `x` where -0.0 has changed to +0.0. This can be + * needed on the lesser side of a floating-point comparison or on both sides of + * a floating point equality because QL does not follow IEEE in floating-point + * comparisons but instead defines -0.0 to be less than and distinct from 0.0. + */ + bindingset[x] + private float normalizeFloatUp(float x) { result = x + 0.0 } + + /** + * Computes `x + y`, rounded towards +Inf. This is the general case where both + * `x` and `y` may be large numbers. + */ + bindingset[x, y] + private float addRoundingUp(float x, float y) { + if normalizeFloatUp((x + y) - x) < y or normalizeFloatUp((x + y) - y) < x + then result = (x + y).nextUp() + else result = (x + y) + } + + /** + * Computes `x + y`, rounded towards -Inf. This is the general case where both + * `x` and `y` may be large numbers. + */ + bindingset[x, y] + private float addRoundingDown(float x, float y) { + if (x + y) - x > normalizeFloatUp(y) or (x + y) - y > normalizeFloatUp(x) + then result = (x + y).nextDown() + else result = (x + y) + } + + /** + * Computes `x + small`, rounded towards +Inf, where `small` is a small + * constant. + */ + bindingset[x, small] + private float addRoundingUpSmall(float x, float small) { + if (x + small) - x < small then result = (x + small).nextUp() else result = (x + small) + } + + /** + * Computes `x + small`, rounded towards -Inf, where `small` is a small + * constant. + */ + bindingset[x, small] + private float addRoundingDownSmall(float x, float small) { + if (x + small) - x > small then result = (x + small).nextDown() else result = (x + small) + } + + private predicate lowerBoundableExpr(Expr expr) { + (analyzableExpr(expr) or dividesNonzeroByZero(expr)) and + getUpperBoundsImpl(expr) <= Util::exprMaxVal(expr) and + not exists(getValue(expr).toFloat()) + } + + /** + * Gets the lower bounds of the expression. + * + * Most of the work of computing the lower bounds is done by + * `getLowerBoundsImpl`. However, the lower bounds computed by + * `getLowerBoundsImpl` may not be representable by the result type of the + * expression. For example, if `x` and `y` are of type `int32` and each + * have lower bound -2147483648, then getLowerBoundsImpl` will compute a + * lower bound -4294967296 for the expression `x+y`, even though + * -4294967296 cannot be represented as an `int32`. Such unrepresentable + * bounds are replaced with `exprMinVal(expr)`. This predicate also adds + * `exprMinVal(expr)` as a lower bound if the expression might overflow + * positively, or if it is unanalyzable. + * + * Note: most callers should use `getFullyConvertedLowerBounds` rather than + * this predicate. + */ + private float getTruncatedLowerBounds(Expr expr) { + // If the expression evaluates to a constant, then there is no + // need to call getLowerBoundsImpl. + analyzableExpr(expr) and + result = getValue(expr).toFloat() + or + // Some of the bounds computed by getLowerBoundsImpl might + // overflow, so we replace invalid bounds with exprMinVal. + exists(float newLB | newLB = normalizeFloatUp(getLowerBoundsImpl(expr)) | + if Util::exprMinVal(expr) <= newLB and newLB <= Util::exprMaxVal(expr) + then + // Apply widening where we might get a combinatorial explosion. + if applyWideningToBinary(expr) + then + result = + max(float widenLB | + widenLB = wideningLowerBounds(expr.getUnspecifiedType()) and + not widenLB > newLB + ) + else result = newLB + else result = Util::exprMinVal(expr) + ) and + lowerBoundableExpr(expr) + or + // The expression might overflow and wrap. If so, the + // lower bound is exprMinVal. + analyzableExpr(expr) and + exprMightOverflowPositively(expr) and + not result = getValue(expr).toFloat() and + result = Util::exprMinVal(expr) + or + // The expression is not analyzable, so its lower bound is + // unknown. Note that the call to exprMinVal restricts the + // expressions to just those with arithmetic types. There is no + // need to return results for non-arithmetic expressions. + not analyzableExpr(expr) and + result = exprMinVal(expr) + } + + /** + * Gets the upper bounds of the expression. + * + * Most of the work of computing the upper bounds is done by + * `getUpperBoundsImpl`. However, the upper bounds computed by + * `getUpperBoundsImpl` may not be representable by the result type of the + * expression. For example, if `x` and `y` are of type `int32` and each + * have upper bound 2147483647, then getUpperBoundsImpl` will compute an + * upper bound 4294967294 for the expression `x+y`, even though 4294967294 + * cannot be represented as an `int32`. Such unrepresentable bounds are + * replaced with `exprMaxVal(expr)`. This predicate also adds + * `exprMaxVal(expr)` as an upper bound if the expression might overflow + * negatively, or if it is unanalyzable. + * + * Note: most callers should use `getFullyConvertedUpperBounds` rather than + * this predicate. + */ + private float getTruncatedUpperBounds(Expr expr) { + (analyzableExpr(expr) or dividesNonzeroByZero(expr)) and + ( + // If the expression evaluates to a constant, then there is no + // need to call getUpperBoundsImpl. + if + exists(getValue(expr).toFloat()) and + not getValue(expr) = "NaN" + then result = getValue(expr).toFloat() + else ( + // Some of the bounds computed by `getUpperBoundsImpl` + // might overflow, so we replace invalid bounds with + // `exprMaxVal`. + exists(float newUB | newUB = normalizeFloatUp(getUpperBoundsImpl(expr)) | + if Util::exprMinVal(expr) <= newUB and newUB <= Util::exprMaxVal(expr) + then + // Apply widening where we might get a combinatorial explosion. + if applyWideningToBinary(expr) + then + result = + min(float widenUB | + widenUB = wideningUpperBounds(expr.getUnspecifiedType()) and + not widenUB < newUB + ) + else result = newUB + else result = Util::exprMaxVal(expr) + ) + or + // The expression might overflow negatively and wrap. If so, + // the upper bound is `exprMaxVal`. + exprMightOverflowNegatively(expr) and + result = Util::exprMaxVal(expr) + ) + ) + or + not analyzableExpr(expr) and + // The expression is not analyzable, so its upper bound is + // unknown. Note that the call to exprMaxVal restricts the + // expressions to just those with arithmetic types. There is no + // need to return results for non-arithmetic expressions. + result = exprMaxVal(expr) + } + + /** Only to be called by `getTruncatedLowerBounds`. */ + private float getLowerBoundsImpl(Expr expr) { + ( + exists(Expr operand, float operandLow, float positive | + effectivelyMultipliesByPositive(expr, operand, positive) and + operandLow = getFullyConvertedLowerBounds(operand) and + result = positive * operandLow + ) + or + exists(Expr operand, float operandHigh, float negative | + effectivelyMultipliesByNegative(expr, operand, negative) and + operandHigh = getFullyConvertedUpperBounds(operand) and + result = negative * operandHigh + ) + or + exists(Expr operand, float operandLow, float positive | + dividesByPositive(expr, operand, positive) and + operandLow = getFullyConvertedLowerBounds(operand) and + result = operandLow / positive + ) + or + exists(Expr operand, float operandLow, float negative | + dividesByNegative(expr, operand, negative) and + operandLow = getFullyConvertedUpperBounds(operand) and + result = operandLow / negative + ) + or + exists(DivExpr div | expr = div | + dividesNonzeroByZero(expr) and + result = getFullyConvertedLowerBounds(div.getLeftOperand()) / 0 + ) + or + exists(SupportedFunctionCall sfc | sfc.getFunctionCall() = expr | + result = sfc.getLowerBound() + ) + or + exists(MinExpr minExpr | + expr = minExpr and + // Return the union of the lower bounds from both children. + result = getFullyConvertedLowerBounds(minExpr.getAnOperand()) + ) + or + exists(MaxExpr maxExpr | + expr = maxExpr and + // Compute the cross product of the bounds from both children. We are + // using this mathematical property: + // + // max (minimum{X}, minimum{Y}) + // = minimum { max(x,y) | x in X, y in Y } + exists(float x, float y | + x = getFullyConvertedLowerBounds(maxExpr.getLeftOperand()) and + y = getFullyConvertedLowerBounds(maxExpr.getRightOperand()) and + if x >= y then result = x else result = y + ) + ) + or + // ConditionalExpr (true branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionUpperBound` to determine whether the condition + // might evaluate to `true`. + boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and + result = getFullyConvertedLowerBounds(condExpr.getThen()) + ) + or + // ConditionalExpr (false branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionLowerBound` to determine whether the condition + // might evaluate to `false`. + boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and + result = getFullyConvertedLowerBounds(condExpr.getElse()) + ) + or + exists(AddExpr addExpr, float xLow, float yLow | + expr = addExpr and + xLow = getFullyConvertedLowerBounds(addExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(addExpr.getRightOperand()) and + result = addRoundingDown(xLow, yLow) + ) + or + exists(SubExpr subExpr, float xLow, float yHigh | + expr = subExpr and + xLow = getFullyConvertedLowerBounds(subExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(subExpr.getRightOperand()) and + result = addRoundingDown(xLow, -yHigh) + ) + or + exists(UnsignedMulExpr mulExpr, float xLow, float yLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(mulExpr.getRightOperand()) and + result = xLow * yLow + ) + or + exists(AssignExpr assign | + expr = assign and + result = getFullyConvertedLowerBounds(assign.getRValue()) + ) + or + exists(AssignAddExpr addExpr, float xLow, float yLow | + expr = addExpr and + xLow = getFullyConvertedLowerBounds(addExpr.getLValue()) and + yLow = getFullyConvertedLowerBounds(addExpr.getRValue()) and + result = addRoundingDown(xLow, yLow) + ) + or + exists(AssignSubExpr subExpr, float xLow, float yHigh | + expr = subExpr and + xLow = getFullyConvertedLowerBounds(subExpr.getLValue()) and + yHigh = getFullyConvertedUpperBounds(subExpr.getRValue()) and + result = addRoundingDown(xLow, -yHigh) + ) + or + exists(UnsignedAssignMulExpr mulExpr, float xLow, float yLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and + yLow = getFullyConvertedLowerBounds(mulExpr.getRValue()) and + result = xLow * yLow + ) + or + exists(AssignMulByPositiveConstantExpr mulExpr, float xLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and + result = xLow * mulExpr.getConstant() + ) + or + exists(AssignMulByNegativeConstantExpr mulExpr, float xHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and + result = xHigh * mulExpr.getConstant() + ) + or + exists(PrefixIncrExpr incrExpr, float xLow | + expr = incrExpr and + xLow = getFullyConvertedLowerBounds(incrExpr.getOperand()) and + result = xLow + 1 + ) + or + exists(PrefixDecrExpr decrExpr, float xLow | + expr = decrExpr and + xLow = getFullyConvertedLowerBounds(decrExpr.getOperand()) and + result = addRoundingDownSmall(xLow, -1) + ) + or + // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their + // operand. The incrementing/decrementing behavior is handled in + // `getDefLowerBoundsImpl`. + exists(PostfixIncrExpr incrExpr | + expr = incrExpr and + result = getFullyConvertedLowerBounds(incrExpr.getOperand()) + ) + or + exists(PostfixDecrExpr decrExpr | + expr = decrExpr and + result = getFullyConvertedLowerBounds(decrExpr.getOperand()) + ) + or + exists(RemExpr remExpr | expr = remExpr | + // If both inputs are positive then the lower bound is zero. + result = 0 + or + // If either input could be negative then the output could be + // negative. If so, the lower bound of `x%y` is `-abs(y) + 1`, which is + // equal to `min(-y + 1,y - 1)`. + exists(float childLB | + childLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and + not childLB >= 0 + | + result = getFullyConvertedLowerBounds(remExpr.getRightOperand()) - 1 + or + exists(float rhsUB | rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) | + result = -rhsUB + 1 + ) + ) + ) + or + // If the conversion is to an arithmetic type then we just return the + // lower bound of the child. We do not need to handle truncation and + // overflow here, because that is done in `getTruncatedLowerBounds`. + // Conversions to `bool` need to be handled specially because they test + // whether the value of the expression is equal to 0. + exists(Conversion convExpr | expr = convExpr | + if convExpr.getUnspecifiedType() instanceof BoolType + then result = boolConversionLowerBound(convExpr.getExpr()) + else result = getTruncatedLowerBounds(convExpr.getExpr()) + ) + or + // Use SSA to get the lower bounds for a variable use. + exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | + result = getDefLowerBounds(def, v) + ) + or + // unsigned `&` (tighter bounds may exist) + exists(UnsignedBitwiseAndExpr andExpr | + andExpr = expr and + result = 0.0 + ) + or + // `>>` by a constant + exists(RShiftExpr rsExpr, float left, int right | + rsExpr = expr and + left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and + right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and + result = safeFloor(left / 2.pow(right)) + ) + ) + } + + /** Only to be called by `getTruncatedUpperBounds`. */ + private float getUpperBoundsImpl(Expr expr) { + ( + exists(Expr operand, float operandHigh, float positive | + effectivelyMultipliesByPositive(expr, operand, positive) and + operandHigh = getFullyConvertedUpperBounds(operand) and + result = positive * operandHigh + ) + or + exists(Expr operand, float operandLow, float negative | + effectivelyMultipliesByNegative(expr, operand, negative) and + operandLow = getFullyConvertedLowerBounds(operand) and + result = negative * operandLow + ) + or + exists(Expr operand, float operandHigh, float positive | + dividesByPositive(expr, operand, positive) and + operandHigh = getFullyConvertedUpperBounds(operand) and + result = operandHigh / positive + ) + or + exists(Expr operand, float operandHigh, float negative | + dividesByNegative(expr, operand, negative) and + operandHigh = getFullyConvertedLowerBounds(operand) and + result = operandHigh / negative + ) + or + exists(DivExpr div | expr = div | + dividesNonzeroByZero(expr) and + result = getFullyConvertedUpperBounds(div.getLeftOperand()) / 0 + ) + or + exists(SupportedFunctionCall sfc | sfc.getFunctionCall() = expr | + result = sfc.getUpperBound() + ) + or + exists(MaxExpr maxExpr | + expr = maxExpr and + // Return the union of the upper bounds from both children. + result = getFullyConvertedUpperBounds(maxExpr.getAnOperand()) + ) + or + exists(MinExpr minExpr | + expr = minExpr and + // Compute the cross product of the bounds from both children. We are + // using this mathematical property: + // + // min (maximum{X}, maximum{Y}) + // = maximum { min(x,y) | x in X, y in Y } + exists(float x, float y | + x = getFullyConvertedUpperBounds(minExpr.getLeftOperand()) and + y = getFullyConvertedUpperBounds(minExpr.getRightOperand()) and + if x <= y then result = x else result = y + ) + ) + or + // ConditionalExpr (true branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionUpperBound` to determine whether the condition + // might evaluate to `true`. + boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and + result = getFullyConvertedUpperBounds(condExpr.getThen()) + ) + or + // ConditionalExpr (false branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionLowerBound` to determine whether the condition + // might evaluate to `false`. + boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and + result = getFullyConvertedUpperBounds(condExpr.getElse()) + ) + or + exists(AddExpr addExpr, float xHigh, float yHigh | + expr = addExpr and + xHigh = getFullyConvertedUpperBounds(addExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(addExpr.getRightOperand()) and + result = addRoundingUp(xHigh, yHigh) + ) + or + exists(SubExpr subExpr, float xHigh, float yLow | + expr = subExpr and + xHigh = getFullyConvertedUpperBounds(subExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(subExpr.getRightOperand()) and + result = addRoundingUp(xHigh, -yLow) + ) + or + exists(UnsignedMulExpr mulExpr, float xHigh, float yHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(mulExpr.getRightOperand()) and + result = xHigh * yHigh + ) + or + exists(AssignExpr assign | + expr = assign and + result = getFullyConvertedUpperBounds(assign.getRValue()) + ) + or + exists(AssignAddExpr addExpr, float xHigh, float yHigh | + expr = addExpr and + xHigh = getFullyConvertedUpperBounds(addExpr.getLValue()) and + yHigh = getFullyConvertedUpperBounds(addExpr.getRValue()) and + result = addRoundingUp(xHigh, yHigh) + ) + or + exists(AssignSubExpr subExpr, float xHigh, float yLow | + expr = subExpr and + xHigh = getFullyConvertedUpperBounds(subExpr.getLValue()) and + yLow = getFullyConvertedLowerBounds(subExpr.getRValue()) and + result = addRoundingUp(xHigh, -yLow) + ) + or + exists(UnsignedAssignMulExpr mulExpr, float xHigh, float yHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and + yHigh = getFullyConvertedUpperBounds(mulExpr.getRValue()) and + result = xHigh * yHigh + ) + or + exists(AssignMulByPositiveConstantExpr mulExpr, float xHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and + result = xHigh * mulExpr.getConstant() + ) + or + exists(AssignMulByNegativeConstantExpr mulExpr, float xLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and + result = xLow * mulExpr.getConstant() + ) + or + exists(PrefixIncrExpr incrExpr, float xHigh | + expr = incrExpr and + xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and + result = addRoundingUpSmall(xHigh, 1) + ) + or + exists(PrefixDecrExpr decrExpr, float xHigh | + expr = decrExpr and + xHigh = getFullyConvertedUpperBounds(decrExpr.getOperand()) and + result = xHigh - 1 + ) + or + // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their operand. + // The incrementing/decrementing behavior is handled in + // `getDefUpperBoundsImpl`. + exists(PostfixIncrExpr incrExpr | + expr = incrExpr and + result = getFullyConvertedUpperBounds(incrExpr.getOperand()) + ) + or + exists(PostfixDecrExpr decrExpr | + expr = decrExpr and + result = getFullyConvertedUpperBounds(decrExpr.getOperand()) + ) + or + exists(RemExpr remExpr, float rhsUB | + expr = remExpr and + rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) + | + result = rhsUB - 1 + or + // If the right hand side could be negative then we need to take its + // absolute value. Since `abs(x) = max(-x,x)` this is equivalent to + // adding `-rhsLB` to the set of upper bounds. + exists(float rhsLB | + rhsLB = getFullyConvertedLowerBounds(remExpr.getRightOperand()) and + not rhsLB >= 0 + | + result = -rhsLB + 1 + ) + ) + or + // If the conversion is to an arithmetic type then we just return the + // upper bound of the child. We do not need to handle truncation and + // overflow here, because that is done in `getTruncatedUpperBounds`. + // Conversions to `bool` need to be handled specially because they test + // whether the value of the expression is equal to 0. + exists(Conversion convExpr | expr = convExpr | + if convExpr.getUnspecifiedType() instanceof BoolType + then result = boolConversionUpperBound(convExpr.getExpr()) + else result = getTruncatedUpperBounds(convExpr.getExpr()) + ) + or + // Use SSA to get the upper bounds for a variable use. + exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | + result = getDefUpperBounds(def, v) + ) + or + // unsigned `&` (tighter bounds may exist) + exists(UnsignedBitwiseAndExpr andExpr, float left, float right | + andExpr = expr and + left = getFullyConvertedUpperBounds(andExpr.getLeftOperand()) and + right = getFullyConvertedUpperBounds(andExpr.getRightOperand()) and + result = left.minimum(right) + ) + or + // `>>` by a constant + exists(RShiftExpr rsExpr, float left, int right | + rsExpr = expr and + left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and + right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and + result = safeFloor(left / 2.pow(right)) + ) + ) + } + + /** + * Holds if `expr` is converted to `bool` or if it is the child of a + * logical operation. + * + * The purpose of this predicate is to optimize `boolConversionLowerBound` + * and `boolConversionUpperBound` by preventing them from computing + * unnecessary results. In other words, `exprIsUsedAsBool(expr)` holds if + * `expr` is an expression that might be passed as an argument to + * `boolConversionLowerBound` or `boolConversionUpperBound`. + */ + private predicate exprIsUsedAsBool(Expr expr) { + expr = any(BinaryLogicalOperation op).getAnOperand().getFullyConverted() + or + expr = any(UnaryLogicalOperation op).getOperand().getFullyConverted() + or + expr = any(ConditionalExpr c).getCondition().getFullyConverted() + or + exists(Conversion cast | cast.getUnspecifiedType() instanceof BoolType | expr = cast.getExpr()) + } + + /** + * Gets the lower bound of the conversion `(bool)expr`. If we can prove that + * the value of `expr` is never 0 then `lb = 1`. Otherwise `lb = 0`. + */ + private float boolConversionLowerBound(Expr expr) { + // Case 1: if the range for `expr` includes the value 0, + // then `result = 0`. + exprIsUsedAsBool(expr) and + exists(float lb | lb = getTruncatedLowerBounds(expr) and not lb > 0) and + exists(float ub | ub = getTruncatedUpperBounds(expr) and not ub < 0) and + result = 0 + or + // Case 2a: if the range for `expr` does not include the value 0, + // then `result = 1`. + exprIsUsedAsBool(expr) and getTruncatedLowerBounds(expr) > 0 and result = 1 + or + // Case 2b: if the range for `expr` does not include the value 0, + // then `result = 1`. + exprIsUsedAsBool(expr) and getTruncatedUpperBounds(expr) < 0 and result = 1 + or + // Case 3: the type of `expr` is not arithmetic. For example, it might + // be a pointer. + exprIsUsedAsBool(expr) and not exists(Util::exprMinVal(expr)) and result = 0 + } + + /** + * Gets the upper bound of the conversion `(bool)expr`. If we can prove that + * the value of `expr` is always 0 then `ub = 0`. Otherwise `ub = 1`. + */ + private float boolConversionUpperBound(Expr expr) { + // Case 1a: if the upper bound of the operand is <= 0, then the upper + // bound might be 0. + exprIsUsedAsBool(expr) and getTruncatedUpperBounds(expr) <= 0 and result = 0 + or + // Case 1b: if the upper bound of the operand is not <= 0, then the upper + // bound is 1. + exprIsUsedAsBool(expr) and + exists(float ub | ub = getTruncatedUpperBounds(expr) and not ub <= 0) and + result = 1 + or + // Case 2a: if the lower bound of the operand is >= 0, then the upper + // bound might be 0. + exprIsUsedAsBool(expr) and getTruncatedLowerBounds(expr) >= 0 and result = 0 + or + // Case 2b: if the lower bound of the operand is not >= 0, then the upper + // bound is 1. + exprIsUsedAsBool(expr) and + exists(float lb | lb = getTruncatedLowerBounds(expr) and not lb >= 0) and + result = 1 + or + // Case 3: the type of `expr` is not arithmetic. For example, it might + // be a pointer. + exprIsUsedAsBool(expr) and not exists(Util::exprMaxVal(expr)) and result = 1 + } + + /** + * This predicate computes the lower bounds of a phi definition. If the + * phi definition corresponds to a guard, then the guard is used to + * deduce a better lower bound. + * For example: + * + * def: x = y % 10; + * guard: if (x >= 2) { + * block: f(x) + * } + * + * In this example, the lower bound of x is 0, but we can + * use the guard to deduce that the lower bound is 2 inside the block. + */ + private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) { + exists(VariableAccess access, Expr guard, boolean branch, float defLB, float guardLB | + phi.isGuardPhi(v, access, guard, branch) and + lowerBoundFromGuard(guard, access, guardLB, branch) and + defLB = getFullyConvertedLowerBounds(access) + | + // Compute the maximum of `guardLB` and `defLB`. + if guardLB > defLB then result = guardLB else result = defLB + ) + or + exists(VariableAccess access, float neConstant, float lower | + isNEPhi(v, phi, access, neConstant) and + lower = getTruncatedLowerBounds(access) and + if lower = neConstant then result = lower + 1 else result = lower + ) + or + exists(VariableAccess access | + isUnsupportedGuardPhi(v, phi, access) and + result = getTruncatedLowerBounds(access) + ) + or + result = getDefLowerBounds(phi.getAPhiInput(v), v) + } + + /** See comment for `getPhiLowerBounds`, above. */ + private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) { + exists(VariableAccess access, Expr guard, boolean branch, float defUB, float guardUB | + phi.isGuardPhi(v, access, guard, branch) and + upperBoundFromGuard(guard, access, guardUB, branch) and + defUB = getFullyConvertedUpperBounds(access) + | + // Compute the minimum of `guardUB` and `defUB`. + if guardUB < defUB then result = guardUB else result = defUB + ) + or + exists(VariableAccess access, float neConstant, float upper | + isNEPhi(v, phi, access, neConstant) and + upper = getTruncatedUpperBounds(access) and + if upper = neConstant then result = upper - 1 else result = upper + ) + or + exists(VariableAccess access | + isUnsupportedGuardPhi(v, phi, access) and + result = getTruncatedUpperBounds(access) + ) + or + result = getDefUpperBounds(phi.getAPhiInput(v), v) + } + + /** Only to be called by `getDefLowerBounds`. */ + private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) { + // Definitions with a defining value. + exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedLowerBounds(expr)) + or + // Assignment operations with a defining value + exists(AssignOperation assignOp | + def = assignOp and + assignOp.getLValue() = v.getAnAccess() and + result = getTruncatedLowerBounds(assignOp) + ) + or + exists(IncrementOperation incr, float newLB | + def = incr and + incr.getOperand() = v.getAnAccess() and + newLB = getFullyConvertedLowerBounds(incr.getOperand()) and + result = newLB + 1 + ) + or + exists(DecrementOperation decr, float newLB | + def = decr and + decr.getOperand() = v.getAnAccess() and + newLB = getFullyConvertedLowerBounds(decr.getOperand()) and + result = addRoundingDownSmall(newLB, -1) + ) + or + // Phi nodes. + result = getPhiLowerBounds(v, def) + or + // Unanalyzable definitions. + unanalyzableDefBounds(def, v, result, _) + } + + /** Only to be called by `getDefUpperBounds`. */ + private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) { + // Definitions with a defining value. + exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedUpperBounds(expr)) + or + // Assignment operations with a defining value + exists(AssignOperation assignOp | + def = assignOp and + assignOp.getLValue() = v.getAnAccess() and + result = getTruncatedUpperBounds(assignOp) + ) + or + exists(IncrementOperation incr, float newUB | + def = incr and + incr.getOperand() = v.getAnAccess() and + newUB = getFullyConvertedUpperBounds(incr.getOperand()) and + result = addRoundingUpSmall(newUB, 1) + ) + or + exists(DecrementOperation decr, float newUB | + def = decr and + decr.getOperand() = v.getAnAccess() and + newUB = getFullyConvertedUpperBounds(decr.getOperand()) and + result = newUB - 1 + ) + or + // Phi nodes. + result = getPhiUpperBounds(v, def) + or + // Unanalyzable definitions. + unanalyzableDefBounds(def, v, _, result) + } + + /** + * Helper for `getDefLowerBounds` and `getDefUpperBounds`. Find the set of + * unanalyzable definitions (such as function parameters) and make their + * bounds unknown. + */ + private predicate unanalyzableDefBounds( + RangeSsaDefinition def, StackVariable v, float lb, float ub + ) { + v = def.getAVariable() and + not analyzableDef(def, v) and + lb = varMinVal(v) and + ub = varMaxVal(v) + } + + /** + * Holds if in the `branch` branch of a guard `guard` involving `v`, + * we know that `v` is not NaN, and therefore it is safe to make range + * inferences about `v`. + */ + bindingset[guard, v, branch] + predicate nonNanGuardedVariable(Expr guard, VariableAccess v, boolean branch) { + Util::getVariableRangeType(v.getTarget()) instanceof IntegralType + or + Util::getVariableRangeType(v.getTarget()) instanceof FloatingPointType and + v instanceof NonNanVariableAccess + or + // The reason the following case is here is to ensure that when we say + // `if (x > 5) { ...then... } else { ...else... }` + // it is ok to conclude that `x > 5` in the `then`, (though not safe + // to conclude that x <= 5 in `else`) even if we had no prior + // knowledge of `x` not being `NaN`. + nanExcludingComparison(guard, branch) + } + + /** + * If the guard is a comparison of the form `p*v + q r`, then this + * predicate uses the bounds information for `r` to compute a lower bound + * for `v`. + */ + private predicate lowerBoundFromGuard(Expr guard, VariableAccess v, float lb, boolean branch) { + exists(float childLB, Util::RelationStrictness strictness | + boundFromGuard(guard, v, childLB, true, strictness, branch) + | + if nonNanGuardedVariable(guard, v, branch) + then + if + strictness = Util::Nonstrict() or + not Util::getVariableRangeType(v.getTarget()) instanceof IntegralType + then lb = childLB + else lb = childLB + 1 + else lb = varMinVal(v.getTarget()) + ) + } + + /** + * If the guard is a comparison of the form `p*v + q r`, then this + * predicate uses the bounds information for `r` to compute a upper bound + * for `v`. + */ + private predicate upperBoundFromGuard(Expr guard, VariableAccess v, float ub, boolean branch) { + exists(float childUB, Util::RelationStrictness strictness | + boundFromGuard(guard, v, childUB, false, strictness, branch) + | + if nonNanGuardedVariable(guard, v, branch) + then + if + strictness = Util::Nonstrict() or + not Util::getVariableRangeType(v.getTarget()) instanceof IntegralType + then ub = childUB + else ub = childUB - 1 + else ub = varMaxVal(v.getTarget()) + ) + } + + /** + * This predicate simplifies the results returned by + * `linearBoundFromGuard`. + */ + private predicate boundFromGuard( + Expr guard, VariableAccess v, float boundValue, boolean isLowerBound, + Util::RelationStrictness strictness, boolean branch + ) { + exists(float p, float q, float r, boolean isLB | + linearBoundFromGuard(guard, v, p, q, r, isLB, strictness, branch) and + boundValue = (r - q) / p + | + // If the multiplier is negative then the direction of the comparison + // needs to be flipped. + p > 0 and isLowerBound = isLB + or + p < 0 and isLowerBound = isLB.booleanNot() + ) + or + // When `!e` is true, we know that `0 <= e <= 0` + exists(float p, float q, Expr e | + Util::linearAccess(e, v, p, q) and + Util::eqZeroWithNegate(guard, e, true, branch) and + boundValue = (0.0 - q) / p and + isLowerBound = [false, true] and + strictness = Util::Nonstrict() + ) + } + + /** + * This predicate finds guards of the form `p*v + q < r or p*v + q == r` + * and decomposes them into a tuple of values which can be used to deduce a + * lower or upper bound for `v`. + */ + private predicate linearBoundFromGuard( + ComparisonOperation guard, VariableAccess v, float p, float q, float boundValue, + boolean isLowerBound, // Is this a lower or an upper bound? + Util::RelationStrictness strictness, boolean branch // Which control-flow branch is this bound valid on? + ) { + // For the comparison x < RHS, we create two bounds: + // + // 1. x < upperbound(RHS) + // 2. x >= typeLowerBound(RHS.getUnspecifiedType()) + // + exists(Expr lhs, Expr rhs, Util::RelationDirection dir, Util::RelationStrictness st | + Util::linearAccess(lhs, v, p, q) and + Util::relOpWithSwapAndNegate(guard, lhs, rhs, dir, st, branch) + | + isLowerBound = Util::directionIsGreater(dir) and + strictness = st and + getBounds(rhs, boundValue, isLowerBound) + or + isLowerBound = Util::directionIsLesser(dir) and + strictness = Util::Nonstrict() and + exprTypeBounds(rhs, boundValue, isLowerBound) + ) + or + // For x == RHS, we create the following bounds: + // + // 1. x <= upperbound(RHS) + // 2. x >= lowerbound(RHS) + // + exists(Expr lhs, Expr rhs | + Util::linearAccess(lhs, v, p, q) and + Util::eqOpWithSwapAndNegate(guard, lhs, rhs, true, branch) and + getBounds(rhs, boundValue, isLowerBound) and + strictness = Util::Nonstrict() + ) + // x != RHS and !x are handled elsewhere + } + + /** Utility for `linearBoundFromGuard`. */ + private predicate getBounds(Expr expr, float boundValue, boolean isLowerBound) { + isLowerBound = true and boundValue = getFullyConvertedLowerBounds(expr) + or + isLowerBound = false and boundValue = getFullyConvertedUpperBounds(expr) + } + + /** Utility for `linearBoundFromGuard`. */ + private predicate exprTypeBounds(Expr expr, float boundValue, boolean isLowerBound) { + isLowerBound = true and boundValue = exprMinVal(expr.getFullyConverted()) + or + isLowerBound = false and boundValue = exprMaxVal(expr.getFullyConverted()) + } + + /** + * Holds if `(v, phi)` ensures that `access` is not equal to `neConstant`. For + * example, the condition `if (x + 1 != 3)` ensures that `x` is not equal to 2. + * Only integral types are supported. + */ + private predicate isNEPhi( + Variable v, RangeSsaDefinition phi, VariableAccess access, float neConstant + ) { + exists( + ComparisonOperation cmp, boolean branch, Expr linearExpr, Expr rExpr, float p, float q, + float r + | + phi.isGuardPhi(v, access, cmp, branch) and + Util::eqOpWithSwapAndNegate(cmp, linearExpr, rExpr, false, branch) and + v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!=` is too imprecise + r = getValue(rExpr).toFloat() and + Util::linearAccess(linearExpr, access, p, q) and + neConstant = (r - q) / p + ) + or + exists(Expr op, boolean branch, Expr linearExpr, float p, float q | + phi.isGuardPhi(v, access, op, branch) and + Util::eqZeroWithNegate(op, linearExpr, false, branch) and + v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!` is too imprecise + Util::linearAccess(linearExpr, access, p, q) and + neConstant = (0.0 - q) / p + ) + } + + /** + * Holds if `(v, phi)` constrains the value of `access` but in a way that + * doesn't allow this library to constrain the upper or lower bounds of + * `access`. An example is `if (x != y)` if neither `x` nor `y` is a + * compile-time constant. + */ + private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, VariableAccess access) { + exists(Expr cmp, boolean branch | + Util::eqOpWithSwapAndNegate(cmp, _, _, false, branch) + or + Util::eqZeroWithNegate(cmp, _, false, branch) + | + phi.isGuardPhi(v, access, cmp, branch) and + not isNEPhi(v, phi, access, _) + ) + } + + /** + * Gets the upper bound of the expression, if the expression is guarded. + * An upper bound can only be found, if a guard phi node can be found, and the + * expression has only one immediate predecessor. + */ + private float getGuardedUpperBound(VariableAccess guardedAccess) { + exists( + RangeSsaDefinition def, StackVariable v, VariableAccess guardVa, Expr guard, boolean branch + | + def.isGuardPhi(v, guardVa, guard, branch) and + // If the basic block for the variable access being examined has + // more than one predecessor, the guard phi node could originate + // from one of the predecessors. This is because the guard phi + // node is attached to the block at the end of the edge and not on + // the actual edge. It is therefore not possible to determine which + // edge the guard phi node belongs to. The predicate below ensures + // that there is one predecessor, albeit somewhat conservative. + exists(unique(BasicBlock b | b = def.(BasicBlock).getAPredecessor())) and + guardedAccess = def.getAUse(v) and + result = max(float ub | upperBoundFromGuard(guard, guardVa, ub, branch)) and + not convertedExprMightOverflow(guard.getAChild+()) + ) + } + + cached + private module SimpleRangeAnalysisCached { + /** + * Gets the lower bound of the expression. + * + * Note: expressions in C/C++ are often implicitly or explicitly cast to a + * different result type. Such casts can cause the value of the expression + * to overflow or to be truncated. This predicate computes the lower bound + * of the expression without including the effect of the casts. To compute + * the lower bound of the expression after all the casts have been applied, + * call `lowerBound` like this: + * + * `lowerBound(expr.getFullyConverted())` + */ + cached + float lowerBound(Expr expr) { + // Combine the lower bounds returned by getTruncatedLowerBounds into a + // single minimum value. + result = min(float lb | lb = getTruncatedLowerBounds(expr) | lb) + } + + /** + * Gets the upper bound of the expression. + * + * Note: expressions in C/C++ are often implicitly or explicitly cast to a + * different result type. Such casts can cause the value of the expression + * to overflow or to be truncated. This predicate computes the upper bound + * of the expression without including the effect of the casts. To compute + * the upper bound of the expression after all the casts have been applied, + * call `upperBound` like this: + * + * `upperBound(expr.getFullyConverted())` + */ + cached + float upperBound(Expr expr) { + // Combine the upper bounds returned by getTruncatedUpperBounds and + // getGuardedUpperBound into a single maximum value + result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)]) + } + + /** Holds if the upper bound of `expr` may have been widened. This means the upper bound is in practice likely to be overly wide. */ + cached + predicate upperBoundMayBeWidened(Expr e) { + isRecursiveExpr(e) and + // Widening is not a problem if the post-analysis in `getGuardedUpperBound` has overridden the widening. + // Note that the RHS of `<` may be multi-valued. + not getGuardedUpperBound(e) < getTruncatedUpperBounds(e) + } + + /** + * Holds if `expr` has a provably empty range. For example: + * + * 10 < expr and expr < 5 + * + * The range of an expression can only be empty if it can never be + * executed. For example: + * + * if (10 < x) { + * if (x < 5) { + * // Unreachable code + * return x; // x has an empty range: 10 < x && x < 5 + * } + * } + */ + cached + predicate exprWithEmptyRange(Expr expr) { + analyzableExpr(expr) and + ( + not exists(lowerBound(expr)) or + not exists(upperBound(expr)) or + lowerBound(expr) > upperBound(expr) + ) + } + + /** Holds if the definition might overflow negatively. */ + cached + predicate defMightOverflowNegatively(RangeSsaDefinition def, StackVariable v) { + getDefLowerBoundsImpl(def, v) < Util::varMinVal(v) + } + + /** Holds if the definition might overflow positively. */ + cached + predicate defMightOverflowPositively(RangeSsaDefinition def, StackVariable v) { + getDefUpperBoundsImpl(def, v) > Util::varMaxVal(v) + } + + /** + * Holds if the definition might overflow (either positively or + * negatively). + */ + cached + predicate defMightOverflow(RangeSsaDefinition def, StackVariable v) { + defMightOverflowNegatively(def, v) or + defMightOverflowPositively(def, v) + } + + /** + * Holds if `e` is an expression where the concept of overflow makes sense. + * This predicate is used to filter out some of the unanalyzable expressions + * from `exprMightOverflowPositively` and `exprMightOverflowNegatively`. + */ + pragma[inline] + private predicate exprThatCanOverflow(Expr e) { + e instanceof UnaryArithmeticOperation or + e instanceof BinaryArithmeticOperation or + e instanceof AssignArithmeticOperation or + e instanceof LShiftExpr or + e instanceof AssignLShiftExpr + } + + /** + * Holds if the expression might overflow negatively. This predicate + * does not consider the possibility that the expression might overflow + * due to a conversion. + */ + cached + predicate exprMightOverflowNegatively(Expr expr) { + getLowerBoundsImpl(expr) < Util::exprMinVal(expr) + or + // The lower bound of the expression `x--` is the same as the lower + // bound of `x`, so the standard logic (above) does not work for + // detecting whether it might overflow. + getLowerBoundsImpl(expr.(PostfixDecrExpr)) = Util::exprMinVal(expr) + or + // We can't conclude that any unanalyzable expression might overflow. This + // is because there are many expressions that the range analysis doesn't + // handle, but where the concept of overflow doesn't make sense. + exprThatCanOverflow(expr) and not analyzableExpr(expr) + } + + /** + * Holds if the expression might overflow negatively. Conversions + * are also taken into account. For example the expression + * `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than + * due to the addition. + */ + cached + predicate convertedExprMightOverflowNegatively(Expr expr) { + exprMightOverflowNegatively(expr) or + convertedExprMightOverflowNegatively(expr.getConversion()) + } + + /** + * Holds if the expression might overflow positively. This predicate + * does not consider the possibility that the expression might overflow + * due to a conversion. + */ + cached + predicate exprMightOverflowPositively(Expr expr) { + getUpperBoundsImpl(expr) > Util::exprMaxVal(expr) + or + // The upper bound of the expression `x++` is the same as the upper + // bound of `x`, so the standard logic (above) does not work for + // detecting whether it might overflow. + getUpperBoundsImpl(expr.(PostfixIncrExpr)) = Util::exprMaxVal(expr) + } + + /** + * Holds if the expression might overflow positively. Conversions + * are also taken into account. For example the expression + * `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than + * due to the addition. + */ + cached + predicate convertedExprMightOverflowPositively(Expr expr) { + exprMightOverflowPositively(expr) or + convertedExprMightOverflowPositively(expr.getConversion()) + } + + /** + * Holds if the expression might overflow (either positively or + * negatively). The possibility that the expression might overflow + * due to an implicit or explicit cast is also considered. + */ + cached + predicate convertedExprMightOverflow(Expr expr) { + convertedExprMightOverflowNegatively(expr) or + convertedExprMightOverflowPositively(expr) + } + } + + /** + * Gets the truncated lower bounds of the fully converted expression. + */ + float getFullyConvertedLowerBounds(Expr expr) { + result = getTruncatedLowerBounds(expr.getFullyConverted()) + } + + /** + * Gets the truncated upper bounds of the fully converted expression. + */ + float getFullyConvertedUpperBounds(Expr expr) { + result = getTruncatedUpperBounds(expr.getFullyConverted()) + } + + /** + * Get the lower bounds for a `RangeSsaDefinition`. Most of the work is + * done by `getDefLowerBoundsImpl`, but this is where widening is applied + * to prevent the analysis from exploding due to a recursive definition. + */ + float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) { + exists(float newLB, float truncatedLB | + newLB = getDefLowerBoundsImpl(def, v) and + if Util::varMinVal(v) <= newLB and newLB <= Util::varMaxVal(v) + then truncatedLB = newLB + else truncatedLB = Util::varMinVal(v) + | + // Widening: check whether the new lower bound is from a source which + // depends recursively on the current definition. + if isRecursiveDef(def, v) + then + // The new lower bound is from a recursive source, so we round + // down to one of a limited set of values to prevent the + // recursion from exploding. + result = + max(float widenLB | + widenLB = wideningLowerBounds(Util::getVariableRangeType(v)) and + not widenLB > truncatedLB + | + widenLB + ) + else result = truncatedLB + ) + or + // The definition might overflow positively and wrap. If so, the lower + // bound is `typeLowerBound`. + defMightOverflowPositively(def, v) and result = Util::varMinVal(v) + } + + /** See comment for `getDefLowerBounds`, above. */ + float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) { + exists(float newUB, float truncatedUB | + newUB = getDefUpperBoundsImpl(def, v) and + if Util::varMinVal(v) <= newUB and newUB <= Util::varMaxVal(v) + then truncatedUB = newUB + else truncatedUB = Util::varMaxVal(v) + | + // Widening: check whether the new upper bound is from a source which + // depends recursively on the current definition. + if isRecursiveDef(def, v) + then + // The new upper bound is from a recursive source, so we round + // up to one of a fixed set of values to prevent the recursion + // from exploding. + result = + min(float widenUB | + widenUB = wideningUpperBounds(Util::getVariableRangeType(v)) and + not widenUB < truncatedUB + | + widenUB + ) + else result = truncatedUB + ) + or + // The definition might overflow negatively and wrap. If so, the upper + // bound is `typeUpperBound`. + defMightOverflowNegatively(def, v) and result = Util::varMaxVal(v) + } +} diff --git a/cpp/common/src/codingstandards/cpp/Scope.qll b/cpp/common/src/codingstandards/cpp/Scope.qll index d39478d784..5438c17133 100644 --- a/cpp/common/src/codingstandards/cpp/Scope.qll +++ b/cpp/common/src/codingstandards/cpp/Scope.qll @@ -3,47 +3,86 @@ */ import cpp +import codingstandards.cpp.ConstHelpers /** - * Gets the parent scope of this `Element`, if any. - * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `Block`, a `Function`, - * or certain kinds of `Statement`. - * Differs from `Element::getParentScope` when `e` is a `LoopControlVariable` + * a `Variable` that is nonvolatile and const + * and of type `IntegralOrEnumType` */ -private Element getParentScope(Element e) { - /* - * A `Block` cannot have a `ForStmt` as its parent scope so we have to special case - * for loop bodies to ensure that identifiers inside the loop bodies have the for stmt as a parent scope. - * If this is not the case then `i2` in the following example cannot be in `i1`'s potential scope, because - * the parent scope of `i1` is the `ForStmt` while the transitive closure of the parent scope for `i2` doesn't include - * outer scope. Blocks can only have blocks as parent scopes. - * void f() { - * for( int i1; ... ) { - * for (int i2; ...) { - * } - * } - * } +class NonVolatileConstIntegralOrEnumVariable extends Variable { + NonVolatileConstIntegralOrEnumVariable() { + not this.isVolatile() and + this.isConst() and + this.getUnspecifiedType() instanceof IntegralOrEnumType + } +} + +/** + * Internal module, exposed for testing. + */ +module Internal { + /** + * Gets the parent scope of this `Element`, if any. + * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `Block`, a `Function`, + * or certain kinds of `Statement`. + * Differs from `Element::getParentScope` when `e` is a `LoopControlVariable` */ + Element getParentScope(Element e) { + /* + * A `Block` cannot have a `ForStmt` as its parent scope so we have to special case + * for loop bodies to ensure that identifiers inside the loop bodies have the for stmt as a parent scope. + * If this is not the case then `i2` in the following example cannot be in `i1`'s potential scope, because + * the parent scope of `i1` is the `ForStmt` while the transitive closure of the parent scope for `i2` doesn't include + * outer scope. Blocks can only have blocks as parent scopes. + * void f() { + * for( int i1; ... ) { + * for (int i2; ...) { + * } + * } + * } + */ - exists(Loop loop | loop.getStmt() = e and result = loop) - or - exists(IfStmt ifStmt | - (ifStmt.getThen() = e or ifStmt.getElse() = e) and - result = ifStmt - ) - or - exists(SwitchStmt switchStmt | switchStmt.getStmt() = e and result = switchStmt) - or - not result.(Loop).getStmt() = e and - not result.(IfStmt).getThen() = e and - not result.(IfStmt).getElse() = e and - not result.(SwitchStmt).getStmt() = e and - if exists(e.getParentScope()) - then result = e.getParentScope() - else ( - // Statements do no have a parent scope, so return the enclosing block. - result = e.(Stmt).getEnclosingBlock() or result = e.(Expr).getEnclosingBlock() - ) + exists(Loop loop | loop.getAChild() = e and result = loop) + or + exists(IfStmt ifStmt | + (ifStmt.getThen() = e or ifStmt.getElse() = e) and + result = ifStmt + ) + or + exists(SwitchStmt switchStmt | switchStmt.getStmt() = e and result = switchStmt) + or + // A catch-block parameter, whose parent is the `Handler` + exists(CatchBlock c | c.getParameter() = e and result = c.getParent()) + or + // A catch-block `Handler`, whose parent is the `TryStmt` + e.(Handler).getParent() = result + or + // The parent scope of a lambda is the scope in which the lambda expression is defined. + // + // Lambda functions are defined in a generated `Closure` class, as the `operator()` function. We choose the + // enclosing statement of the lambda expression as the parent scope of the lambda function. This is so we can + // determine the order of definition if a variable is defined in the same scope as the lambda expression. + exists(Closure lambdaClosure | + lambdaClosure.getLambdaFunction() = e and + lambdaClosure.getLambdaExpression().getEnclosingStmt() = result + ) + or + not exists(Loop loop | loop.getAChild() = e) and + not exists(IfStmt ifStmt | ifStmt.getThen() = e or ifStmt.getElse() = e) and + not exists(SwitchStmt switchStmt | switchStmt.getStmt() = e) and + not exists(CatchBlock c | c.getParameter() = e) and + not e instanceof Handler and + not exists(Closure lambdaClosure | lambdaClosure.getLambdaFunction() = e) and + if exists(e.getParentScope()) + then result = e.getParentScope() + else ( + // Statements do not have a parent scope, so return the enclosing block. + result = e.(Stmt).getEnclosingBlock() + or + // Expressions do not have a parent scope, so return the enclosing block. + result = e.(Expr).getEnclosingBlock() + ) + } } /** A variable which is defined by the user, rather than being from a third party or compiler generated. */ @@ -59,19 +98,29 @@ class UserVariable extends Variable { /** An element which is the parent scope of at least one other element in the program. */ class Scope extends Element { - Scope() { this = getParentScope(_) } + Scope() { this = Internal::getParentScope(_) } - UserVariable getAVariable() { getParentScope(result) = this } + UserVariable getAVariable() { Internal::getParentScope(result) = this } + + /** + * Gets the `Variable` with the given `name` that is declared in this scope. + */ + UserVariable getVariable(string name) { + result = getAVariable() and + result.getName() = name + } int getNumberOfVariables() { result = count(getAVariable()) } Scope getAnAncestor() { result = this.getStrictParent+() } - Scope getStrictParent() { result = getParentScope(this) } + Scope getAChildScope() { result.getStrictParent() = this } + + Scope getStrictParent() { result = Internal::getParentScope(this) } - Declaration getADeclaration() { getParentScope(result) = this } + Declaration getADeclaration() { Internal::getParentScope(result) = this } - Expr getAnExpr() { this = getParentScope(result) } + Expr getAnExpr() { this = Internal::getParentScope(result) } private predicate getLocationInfo( PreprocessorBranchDirective pbd, string file1, string file2, int startline1, int startline2 @@ -103,31 +152,136 @@ class Scope extends Element { predicate isGenerated() { this instanceof GeneratedBlockStmt } int getDepth() { - exists(Scope parent | parent = getParentScope(this) and result = 1 + parent.getDepth()) + exists(Scope parent | + parent = Internal::getParentScope(this) and result = 1 + parent.getDepth() + ) or - not exists(getParentScope(this)) and result = 0 + not exists(Internal::getParentScope(this)) and result = 0 } -} -class GeneratedBlockStmt extends BlockStmt { - GeneratedBlockStmt() { this.getLocation() instanceof UnknownLocation } -} + /** + * Holds if `name` is declared in this scope, or in a nested scope. + */ + private predicate isNameDeclaredInThisOrNestedScope(string name) { + name = getAVariable().getName() + or + isNameDeclaredInNestedScope(name) + } -/** Gets a variable that is in the potential scope of variable `v`. */ -private UserVariable getPotentialScopeOfVariable_candidate(UserVariable v) { - exists(Scope s | - result = s.getAVariable() and + /** + * Holds if `name` is declared in a nested scope. + */ + private predicate isNameDeclaredInNestedScope(string name) { + this.getAChildScope().isNameDeclaredInThisOrNestedScope(name) + } + + /** + * Holds if `name` is declared in this scope and is hidden in a child scope. + */ + private predicate isDeclaredNameHiddenByChild(string name) { + isNameDeclaredInNestedScope(name) and + name = getAVariable().getName() + } + + /** + * Gets a variable with `name` which is potentially hidden in at least one nested scope. + */ + private UserVariable getAPotentiallyHiddenVariable(string name) { + result = getAVariable() and + result.getName() = name and + isDeclaredNameHiddenByChild(name) + } + + /** + * Holds if `name` is declared above this scope and hidden by this or a nested scope. + */ + UserVariable getAVariableHiddenByThisOrNestedScope(string name) { + exists(Scope parent | parent = this.getStrictParent() | + result = parent.getAPotentiallyHiddenVariable(name) or + result = parent.getAVariableHiddenByThisOrNestedScope(name) + ) and + isNameDeclaredInThisOrNestedScope(name) + } + + /** + * Holds if `hiddenVariable` and `hidingVariable` are a candidate hiding pair at this scope. + */ + private predicate hidesCandidate( + UserVariable hiddenVariable, UserVariable hidingVariable, string name + ) { ( - // Variable in an ancestor scope, but only if there are less than 100 variables in this scope - v = s.getAnAncestor().getAVariable() and - s.getNumberOfVariables() < 100 + // Declared in this scope + hidingVariable = getVariable(name) and + hiddenVariable = getAVariableHiddenByThisOrNestedScope(name) or - // In the same scope, but not the same variable, and choose just one to report - v = s.getAVariable() and - not result = v and - v.getName() <= result.getName() + // Declared in a child scope + exists(Scope child | + getAChildScope() = child and + child.hidesCandidate(hiddenVariable, hidingVariable, name) + ) ) - ) + } + + /** + * Holds if `hiddenVariable` is declared in this scope and hidden by `hidingVariable`. + */ + predicate hides(UserVariable hiddenVariable, UserVariable hidingVariable, Scope childScope) { + exists(string name | + hiddenVariable = getAPotentiallyHiddenVariable(name) and + childScope = getAChildScope() and + childScope.hidesCandidate(hiddenVariable, hidingVariable, name) + ) + } +} + +/** + * A scope representing the generated `operator()` of a lambda function. + */ +class LambdaScope extends Scope { + Closure closure; + + LambdaScope() { this = closure.getLambdaFunction() } + + override UserVariable getAVariableHiddenByThisOrNestedScope(string name) { + // Handle special cases for lambdas + exists(UserVariable hiddenVariable, LambdaExpression lambdaExpr | + // Find the variable that is potentially hidden inside the lambda + hiddenVariable = super.getAVariableHiddenByThisOrNestedScope(name) and + result = hiddenVariable and + lambdaExpr = closure.getLambdaExpression() + | + // A definition can be hidden if it is in scope and it is captured by the lambda, + exists(LambdaCapture cap | + lambdaExpr.getACapture() = cap and + // The outer declaration is captured by the lambda + hiddenVariable.getAnAccess() = cap.getInitializer() + ) + or + // it is is non-local, + hiddenVariable instanceof GlobalVariable + or + // it has static or thread local storage duration, + (hiddenVariable.isThreadLocal() or hiddenVariable.isStatic()) + or + //it is a reference that has been initialized with a constant expression. + hiddenVariable.getType().stripTopLevelSpecifiers() instanceof ReferenceType and + hiddenVariable.getInitializer().getExpr() instanceof Literal + or + // //it const non-volatile integral or enumeration type and has been initialized with a constant expression + hiddenVariable instanceof NonVolatileConstIntegralOrEnumVariable and + hiddenVariable.getInitializer().getExpr() instanceof Literal + or + //it is constexpr and has no mutable members + hiddenVariable.isConstexpr() and + not exists(Class c | + c = hiddenVariable.getType() and not c.getAMember() instanceof MutableVariable + ) + ) + } +} + +class GeneratedBlockStmt extends BlockStmt { + GeneratedBlockStmt() { this.getLocation() instanceof UnknownLocation } } /** Holds if there exists a translation unit that includes both `f1` and `f2`. */ @@ -140,12 +294,17 @@ predicate inSameTranslationUnit(File f1, File f2) { } /** - * Gets a user variable which occurs in the "potential scope" of variable `v`. + * Holds if there exists a translation unit that includes both `f1` and `f2`. + * + * This version is late bound. */ -cached -UserVariable getPotentialScopeOfVariable(UserVariable v) { - result = getPotentialScopeOfVariable_candidate(v) and - inSameTranslationUnit(v.getFile(), result.getFile()) +bindingset[f1, f2] +pragma[inline_late] +predicate inSameTranslationUnitLate(File f1, File f2) { + exists(TranslationUnit c | + c.getAUserFile() = f1 and + c.getAUserFile() = f2 + ) } /** A file that is a C/C++ source file */ @@ -173,22 +332,36 @@ class TranslationUnit extends SourceFile { } } -/** Holds if `v2` may hide `v1`. */ -private predicate hides_candidate(UserVariable v1, UserVariable v2) { - not v1 = v2 and - v2 = getPotentialScopeOfVariable(v1) and - v1.getName() = v2.getName() and - // Member variables cannot hide other variables nor be hidden because the can be referenced through their qualified name. +/** Holds if `v2` strictly (`v2` is in an inner scope compared to `v1`) hides `v1`. */ +predicate hides_candidateStrict(UserVariable v1, UserVariable v2) { + exists(Scope parentScope, Scope childScope | parentScope.hides(v1, v2, childScope) | + // If v1 is a local variable defined in a `DeclStmt` ensure that it is declared before `v2`, + // otherwise it would not be hidden + ( + parentScope instanceof BlockStmt and + exists(DeclStmt ds | ds.getADeclaration() = v1) and + exists(parentScope.(BlockStmt).getIndexOfStmt(childScope)) + ) + implies + exists(BlockStmt bs, DeclStmt v1Stmt, Stmt v2Stmt | + bs = parentScope and + v2Stmt = childScope and + v1Stmt.getADeclaration() = v1 + | + bs.getIndexOfStmt(v1Stmt) <= bs.getIndexOfStmt(v2Stmt) + ) + ) and + inSameTranslationUnitLate(v1.getFile(), v2.getFile()) and not (v1.isMember() or v2.isMember()) } -/** Holds if `v2` hides `v1`. */ -predicate hides(UserVariable v1, UserVariable v2) { - hides_candidate(v1, v2) and +/** Holds if `v2` strictly (`v2` is in an inner scope compared to `v1`) hides `v1`. */ +predicate hidesStrict(UserVariable v1, UserVariable v2) { + hides_candidateStrict(v1, v2) and // Confirm that there's no closer candidate variable which `v2` hides not exists(UserVariable mid | - hides_candidate(v1, mid) and - hides_candidate(mid, v2) + hides_candidateStrict(v1, mid) and + hides_candidateStrict(mid, v2) ) } @@ -203,3 +376,20 @@ predicate hasClassScope(Declaration decl) { exists(decl.getDeclaringType()) } /** Holds if `decl` has block scope. */ predicate hasBlockScope(Declaration decl) { exists(BlockStmt b | b.getADeclaration() = decl) } + +/** + * identifiers in nested (named/nonglobal) namespaces are exceptions to hiding due to being able access via fully qualified ids + */ +bindingset[outerDecl, innerDecl] +pragma[inline_late] +predicate excludedViaNestedNamespaces(UserVariable outerDecl, UserVariable innerDecl) { + exists(Namespace inner, Namespace outer | + outer.getAChildNamespace+() = inner and + //outer is not global + not outer instanceof GlobalNamespace and + not outer.isAnonymous() and + not inner.isAnonymous() and + innerDecl.getNamespace() = inner and + outerDecl.getNamespace() = outer + ) +} diff --git a/cpp/common/src/codingstandards/cpp/SideEffect.qll b/cpp/common/src/codingstandards/cpp/SideEffect.qll index 4b78b5c818..883004e513 100644 --- a/cpp/common/src/codingstandards/cpp/SideEffect.qll +++ b/cpp/common/src/codingstandards/cpp/SideEffect.qll @@ -1,7 +1,7 @@ /** A module to reason about side effects. */ import cpp -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow private import exceptions.ExceptionFlow private import codingstandards.cpp.Expr private import codingstandards.cpp.Variable @@ -190,6 +190,8 @@ Expr getAnEffect(Expr base) { or exists(PointerDereferenceExpr e | e.getOperand() = base | result = getAnEffect(e)) or + exists(CrementOperation c | c.getOperand() = base | result = getAnEffect(c)) + or // local alias analysis, assume alias when data flows to derived type (pointer/reference) // auto ptr = &base; exists(VariableAccess va, AddressOfExpr addressOf | diff --git a/cpp/common/src/codingstandards/cpp/SimpleRangeAnalysisCustomizations.qll b/cpp/common/src/codingstandards/cpp/SimpleRangeAnalysisCustomizations.qll index c06b6584a6..5144f63dc2 100644 --- a/cpp/common/src/codingstandards/cpp/SimpleRangeAnalysisCustomizations.qll +++ b/cpp/common/src/codingstandards/cpp/SimpleRangeAnalysisCustomizations.qll @@ -151,6 +151,62 @@ private class CastEnumToIntegerSimpleRange extends SimpleRangeAnalysisExpr, Cast override predicate dependsOnChild(Expr child) { child = getExpr() } } +/** + * A range analysis extension that supports `%=`. + */ +private class RemAssignSimpleRange extends SimpleRangeAnalysisExpr, AssignRemExpr { + override float getLowerBounds() { + exists(float maxDivisorNegated, float dividendLowerBounds | + // Find the max divisor, negated e.g. `%= 32` would be `-31` + maxDivisorNegated = (getFullyConvertedUpperBounds(getRValue()).abs() - 1) * -1 and + // Find the lower bounds of the dividend + dividendLowerBounds = getFullyConvertedLowerBounds(getLValue()) and + // The lower bound is calculated in two steps: + // 1. Determine the maximum of the dividend lower bound and maxDivisorNegated. + // When the dividend is negative this will result in a negative result + // 2. Find the minimum with 0. If the divided is always >0 this will produce 0 + // otherwise it will produce the lowest negative number that can be held + // after the modulo. + result = 0.minimum(dividendLowerBounds.maximum(maxDivisorNegated)) + ) + } + + override float getUpperBounds() { + exists(float maxDivisor, float maxDividend | + // The maximum divisor value is the absolute value of the divisor minus 1 + maxDivisor = getFullyConvertedUpperBounds(getRValue()).abs() - 1 and + // value if > 0 otherwise 0 + maxDividend = getFullyConvertedUpperBounds(getLValue()).maximum(0) and + // In the case the numerator is definitely less than zero, the result could be negative + result = maxDividend.minimum(maxDivisor) + ) + } + + override predicate dependsOnChild(Expr expr) { expr = getAChild() } +} + +/** + * functions that read a character from the STDIN, + * or return EOF if it fails to do so. + * Their return type is `int` by their signatures, but + * they actually return either an unsigned char or an EOF. + */ +private class CtypeGetcharFunctionsRange extends SimpleRangeAnalysisExpr, FunctionCall { + CtypeGetcharFunctionsRange() { + this.getTarget().getFile().(HeaderFile).getBaseName() = "stdio.h" and + this.getTarget().getName().regexpMatch("(fgetc|getc|getchar|)") + } + + /* It can return an EOF, which is -1 on most implementations. */ + override float getLowerBounds() { result = -1 } + + /* Otherwise, it can return any unsigned char. */ + override float getUpperBounds() { result = 255 } + + /* No, its call does not depend on any of its child. */ + override predicate dependsOnChild(Expr expr) { none() } +} + /** * Gets the value of the expression `e`, if it is a constant. * diff --git a/cpp/common/src/codingstandards/cpp/SmartPointers.qll b/cpp/common/src/codingstandards/cpp/SmartPointers.qll index aa9ea420a3..a643b0bc2b 100644 --- a/cpp/common/src/codingstandards/cpp/SmartPointers.qll +++ b/cpp/common/src/codingstandards/cpp/SmartPointers.qll @@ -1,5 +1,5 @@ import cpp -import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.DataFlow // Local cached version of localExprFlow to avoid bad magic cached @@ -29,6 +29,11 @@ abstract class AutosarSmartPointer extends Class { ) } + FunctionCall getAGetCall() { + result.getTarget().hasName("get") and + result.getQualifier().getType().stripType() = this + } + FunctionCall getAnInitializerExpr() { result = any(FunctionCall fc | @@ -51,10 +56,25 @@ abstract class AutosarSmartPointer extends Class { AutosarSmartPointer ) } + + FunctionCall getAResetCall() { + result.getTarget().hasName("reset") and + result.getQualifier().getType().stripType() = this + } + + FunctionCall getAModifyingCall() { + result.getTarget().hasName(["operator=", "reset", "swap"]) and + result.getQualifier().getType().stripType() = this + } } class AutosarUniquePointer extends AutosarSmartPointer { AutosarUniquePointer() { this.hasQualifiedName("std", "unique_ptr") } + + FunctionCall getAReleaseCall() { + result.getTarget().hasName("release") and + result.getQualifier().getType().stripType() = this + } } class AutosarSharedPointer extends AutosarSmartPointer { diff --git a/cpp/common/src/codingstandards/cpp/StaticInitialization.qll b/cpp/common/src/codingstandards/cpp/StaticInitialization.qll index 0a8f5811e0..34c353726d 100644 --- a/cpp/common/src/codingstandards/cpp/StaticInitialization.qll +++ b/cpp/common/src/codingstandards/cpp/StaticInitialization.qll @@ -27,6 +27,15 @@ module StaticInitializationGraph { * - Create a `Node` instance for each injector type. */ + /** + * Gets an Expr directly or indirectly included in an initializer. + */ + private Expr getAnInitializerExpr(Initializer i) { + result = i.getExpr() + or + result = getAnInitializerExpr(i).getAChild() + } + newtype TNode = TInitializerNode(Initializer i) { // This is the initializer of a static storage duration variable @@ -48,7 +57,7 @@ module StaticInitializationGraph { } or TFunctionCallNode(FunctionCall fc) { // This is a function call that occurs in an initializer called during static initialization - exists(TInitializerNode(any(Initializer i | i.getExpr().getAChild*() = fc))) + exists(TInitializerNode(any(Initializer i | getAnInitializerExpr(i) = fc))) or // This is a function call that occurs in a function called during static initialization exists( @@ -56,13 +65,13 @@ module StaticInitializationGraph { f = fc.getEnclosingFunction() and // Not in an initializer of a local variable, where the desired flow is instead: // function -> initializer -> fc - not exists(Initializer i | i.getExpr().getAChild*() = fc) + not exists(Initializer i | getAnInitializerExpr(i) = fc) )) ) } or TVariableAccessNode(VariableAccess va) { // This is a variable that is accessed in an initializer called during static initialization - exists(TInitializerNode(any(Initializer i | i.getExpr().getAChild*() = va))) + exists(TInitializerNode(any(Initializer i | getAnInitializerExpr(i) = va))) or // This is a variable that is accessed in a function called during static initialization exists( @@ -70,7 +79,7 @@ module StaticInitializationGraph { f = va.getEnclosingFunction() and // Not in an initializer of a local variable, where the desired flow is instead: // function -> initializer -> va - not exists(Initializer i | i.getExpr().getAChild*() = va) + not exists(Initializer i | getAnInitializerExpr(i) = va) )) ) } @@ -149,9 +158,7 @@ module StaticInitializationGraph { or // Initializer steps exists(Initializer i | i = n1.(InitializerNode).getInitializer() | - i.getExpr().getAChild*() = n2.(FunctionCallNode).getFunctionCall() - or - i.getExpr().getAChild*() = n2.(VariableAccessNode).getVariableAccess() + getAnInitializerExpr(i) = n2.getExpr() ) or // FunctionCall steps @@ -169,7 +176,7 @@ module StaticInitializationGraph { f = n2.getExpr().getEnclosingFunction() and // But not in an initializer of a local variable, where the desired flow is instead: // function -> initializer -> expression - not exists(Initializer i | i.getExpr().getAChild*() = n2.getExpr()) + not exists(Initializer i | getAnInitializerExpr(i) = n2.getExpr()) or // `n2` is an initializer of a local scope variable within function `f` n2.(InitializerNode).getInitializer().getDeclaration().(LocalScopeVariable).getFunction() = f diff --git a/cpp/common/src/codingstandards/cpp/StdFunctionOrMacro.qll b/cpp/common/src/codingstandards/cpp/StdFunctionOrMacro.qll new file mode 100644 index 0000000000..ec4873d20e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/StdFunctionOrMacro.qll @@ -0,0 +1,200 @@ +/** + * This module intends to reduce the difficulty of handling the pattern where implementations + * implement a function as a macro: the class `StdFunctionOrMacro<...>::Call` matches both std + * function calls as well as std function macro expansions. + * + * For instance, `atomic_init` may be implemented as a function, but is also implemented as a + * complicated macro on some platforms. This module aids in finding calls to any standard function + * which may be a macro. + * + * Since a macro can be defined to expand to any expression, we cannot know generally which + * expanded expressions in `f(x, y)` correspond to arguments `x` or `y`. To handle this, implement + * the module `InferMacroExpansionArguments`. + * + * To match a function of a particular name create a predicate for the name you wish to match. For + * instance: + * + * ```codeql + * private string atomicInit() { result = "atomic_init" } + * + * from StdFunctionOrMacro::Call c + * select c.getArgument(0) + * ``` + */ + +import cpp as cpp + +private string atomicInit() { result = "atomic_init" } + +class AtomicInitCall = StdFunctionOrMacro::Call; + +/** Specify the name of your function as a predicate */ +private signature string getName(); + +/** Signature module to implement custom argument resolution behavior in expanded macros */ +private signature module InferMacroExpansionArguments { + bindingset[mi, argumentIdx] + cpp::Expr inferArgument(cpp::MacroInvocation mi, int argumentIdx); +} + +private module InferAtomicMacroArgs implements InferMacroExpansionArguments { + bindingset[pattern] + private cpp::Expr getMacroVarInitializer(cpp::MacroInvocation mi, string pattern) { + exists(cpp::VariableDeclarationEntry decl | + mi.getAGeneratedElement() = decl and + decl.getName().matches(pattern) and + result = decl.getDeclaration().getInitializer().getExpr() + ) + } + + bindingset[mi, argumentIdx] + cpp::Expr inferArgument(cpp::MacroInvocation mi, int argumentIdx) { + result = mi.getExpr().(cpp::FunctionCall).getArgument(argumentIdx) + or + if + argumentIdx = 0 and + exists(getMacroVarInitializer(mi, "__atomic_%_ptr")) + then result = getMacroVarInitializer(mi, "__atomic_%_ptr") + else + if + argumentIdx = [1, 2] and + exists(getMacroVarInitializer(mi, "__atomic_%_tmp")) + then result = getMacroVarInitializer(mi, "__atomic_%_tmp") + else + exists(cpp::FunctionCall fc | + fc = mi.getAnExpandedElement() and + fc.getTarget().getName().matches("%atomic_%") and + result = fc.getArgument(argumentIdx) + ) + } +} + +private string atomicReadOrWriteName() { + result = + [ + "atomic_load", + "atomic_store", + "atomic_fetch_" + ["add", "sub", "or", "xor", "and"], + "atomic_exchange", + "atomic_compare_exchange_" + ["strong", "weak"] + ] + ["", "_explicit"] +} + +class AtomicReadOrWriteCall = + StdFunctionOrMacro::Call; + +private string atomicallySequencedName() { + result = + [ + "atomic_thread_fence", + "atomic_signal_fence", + "atomic_flag_clear_explicit", + "atomic_flag_test_and_set_explicit", + ] + or + result = atomicReadOrWriteName() and + result.matches("%_explicit") +} + +/** A `stdatomic.h` function which accepts a `memory_order` value as a parameter. */ +class AtomicallySequencedCall extends StdFunctionOrMacro::Call +{ + cpp::Expr getAMemoryOrderArgument() { + if getName() = "atomic_compare_exchange_" + ["strong", "weak"] + "_explicit" + then result = getArgument(getNumberOfArguments() - [1, 2]) + else result = getArgument(getNumberOfArguments() - 1) + } +} + +/** + * A module to find calls to standard functions, or expansions of macros with the same name. + * + * To use this module, specify a name predicate and an inference strategy for correlating macro + * expansions to macro arguments. + * + * For example: + * + * ```codeql + * private string atomicInit() { result = "atomic_init" } + * from StdFunctionOrMacro::Call c + * select c.getArgument(0) + * ``` + */ +private module StdFunctionOrMacro +{ + final private class Expr = cpp::Expr; + + final private class FunctionCall = cpp::FunctionCall; + + final private class MacroInvocation = cpp::MacroInvocation; + + private newtype TStdCall = + TStdFunctionCall(FunctionCall fc) { fc.getTarget().hasName(getStdName()) } or + TStdMacroInvocation(MacroInvocation mi) { mi.getMacro().hasName(getStdName()) } + + /** + * A call to a standard function or an expansion of a macro with the same name. + */ + class Call extends TStdCall { + bindingset[this, argumentIdx] + Expr getArgument(int argumentIdx) { + exists(FunctionCall fc | + this = TStdFunctionCall(fc) and + result = fc.getArgument(argumentIdx) + ) + or + exists(MacroInvocation mi | + this = TStdMacroInvocation(mi) and + result = InferExpansion::inferArgument(mi, argumentIdx) + ) + } + + Expr getAnArgument() { + exists(int i | + i = [0 .. getNumberOfArguments()] and + result = getArgument(i) + ) + } + + string getName() { + exists(FunctionCall fc | + this = TStdFunctionCall(fc) and + result = fc.getTarget().getName() + ) + or + exists(MacroInvocation mi | + this = TStdMacroInvocation(mi) and + result = mi.getMacroName() + ) + } + + string toString() { + this = TStdFunctionCall(_) and + result = "Standard function call" + or + this = TStdMacroInvocation(_) and + result = "Invocation of a standard function implemented as a macro" + } + + int getNumberOfArguments() { + exists(FunctionCall fc | + this = TStdFunctionCall(fc) and + result = fc.getTarget().getNumberOfParameters() + ) + or + exists(MacroInvocation mi | + this = TStdMacroInvocation(mi) and + result = count(int i | i = [0 .. 10] and exists(InferExpansion::inferArgument(mi, i))) + ) + } + + Expr getExpr() { + this = TStdFunctionCall(result) + or + exists(MacroInvocation mi | + this = TStdMacroInvocation(mi) and + result = mi.getExpr() + ) + } + } +} diff --git a/cpp/common/src/codingstandards/cpp/StdNamespace.qll b/cpp/common/src/codingstandards/cpp/StdNamespace.qll new file mode 100644 index 0000000000..6a6574f262 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/StdNamespace.qll @@ -0,0 +1,14 @@ +import cpp + +/** + * A class that models the `std` namespace and its inline children + * (e.g. `std::_V2` `std::__cxx11` and `std::__1`) + */ +class StdNS extends Namespace { + StdNS() { + this instanceof StdNamespace + or + this.isInline() and + this.getParentNamespace() instanceof StdNS + } +} diff --git a/cpp/common/src/codingstandards/cpp/StructuralEquivalence.qll b/cpp/common/src/codingstandards/cpp/StructuralEquivalence.qll index 3423134ca1..0fbc5ce8e7 100644 --- a/cpp/common/src/codingstandards/cpp/StructuralEquivalence.qll +++ b/cpp/common/src/codingstandards/cpp/StructuralEquivalence.qll @@ -989,7 +989,7 @@ private module HashCons { analyzableClassAggregateLiteral(cal) and cal.getUnspecifiedType() = c and exists(Expr e | - e = cal.getFieldExpr(f).getFullyConverted() and + e = cal.getAFieldExpr(f).getFullyConverted() and f.getInitializationOrder() = i and ( hc = hashConsExpr(e) and @@ -1005,9 +1005,9 @@ private module HashCons { private predicate analyzableClassAggregateLiteral(ClassAggregateLiteral cal) { forall(int i | exists(cal.getChild(i)) | strictcount(cal.getChild(i).getFullyConverted()) = 1 and - strictcount(Field f | cal.getChild(i) = cal.getFieldExpr(f)) = 1 and + strictcount(Field f | cal.getChild(i) = cal.getAFieldExpr(f)) = 1 and strictcount(Field f, int j | - cal.getFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder() + cal.getAFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder() ) = 1 ) } diff --git a/cpp/common/src/codingstandards/cpp/SwitchStatement.qll b/cpp/common/src/codingstandards/cpp/SwitchStatement.qll index 1f055be570..7e6686b41a 100644 --- a/cpp/common/src/codingstandards/cpp/SwitchStatement.qll +++ b/cpp/common/src/codingstandards/cpp/SwitchStatement.qll @@ -4,6 +4,30 @@ import cpp +/** + * Class to differentiate between extractor generated blockstmt and actual blockstmt. The extractor + * will generate an artificial blockstmt when there is a single case and statement, e.g. + * ``` + * switch(x) + * case 1: + * f(); + * ``` + * This is because our AST model considers the `case` to be a statement in its own right, so the + * extractor needs an aritifical block to hold both the case and the statement. + */ +class ArtificialBlock extends BlockStmt { + ArtificialBlock() { + exists(Location block, Location firstStatement | + block = getLocation() and firstStatement = getStmt(0).getLocation() + | + // We can identify artificial blocks as those where the start of the statement is at the same + // location as the start of the first statement in the block i.e. there was no opening brace. + block.getStartLine() = firstStatement.getStartLine() and + block.getStartColumn() = firstStatement.getStartColumn() + ) + } +} + /* A `SwitchCase` that contains a 'SwitchCase' inside its body */ class NestedSwitchCase extends SwitchCase { NestedSwitchCase() { diff --git a/cpp/common/src/codingstandards/cpp/Type.qll b/cpp/common/src/codingstandards/cpp/Type.qll index a49e30e927..052096559a 100644 --- a/cpp/common/src/codingstandards/cpp/Type.qll +++ b/cpp/common/src/codingstandards/cpp/Type.qll @@ -1,24 +1,2 @@ -/** - * A module for representing different `Type`s. - */ - -import cpp - -/** - * A fundamental type, as defined by `[basic.fundamental]`. - */ -class FundamentalType extends BuiltInType { - FundamentalType() { - // A fundamental type is any `BuiltInType` except types indicating errors during extraction, or - // "unknown" types inserted into uninstantiated templates - not this instanceof ErroneousType and - not this instanceof UnknownType - } -} - -/** - * A type that is incomplete. - */ -class IncompleteType extends Class { - IncompleteType() { not hasDefinition() } -} +import codingstandards.cpp.types.Type +import codingstandards.cpp.types.Uses diff --git a/cpp/common/src/codingstandards/cpp/TypeUses.qll b/cpp/common/src/codingstandards/cpp/TypeUses.qll deleted file mode 100644 index 0033401098..0000000000 --- a/cpp/common/src/codingstandards/cpp/TypeUses.qll +++ /dev/null @@ -1,169 +0,0 @@ -/** - * A module for identifying uses of a `Type` within the source code. - * - * The strategy used within this module is to identify types directly used within the AST classes - * represented in the databases - for example, `Type`s associated with variables, function return - * types, casts/sizeofs, name qualifiers and so forth. A recursive approach is then used to unwrap - * those types to determine which types are used transitively, such as base types, type arguments - * and so forth. - * - * The analysis also makes some basic attempts to exclude type self references, to avoid marking a - * type as "used" if it is only referenced from itself. - */ - -import cpp - -/** - * Gets a typedef with the same qualified name and declared at the same location. - * - * A single typedef may occur in our database multiple times, if it is compiled into multiple - * separate link targets with a different context. This predicate implements a hueristic for - * identifying "equivalent" typedefs, so that we can say if one is "live", the others are live. - */ -pragma[noinline, nomagic] -private TypedefType getAnEquivalentTypeDef(TypedefType type) { - // pragmas due to bad magic, bad inlining - type.getQualifiedName() = result.getQualifiedName() and - type.getLocation() = result.getLocation() -} - -/** - * Gets a meaningful use of `type` in the source code. - * - * This reports all the places in the source checkout root that the type was used, directly or - * indirectly. The makes a basic attempt to exclude self-referential types where the only reference - * is from within the function signature or field declaration of the type itself. - */ -Locatable getATypeUse(Type type) { - result = getATypeUse_i(type) - or - // Identify `TypeMention`s of typedef types, where the underlying type is used. - // - // In principle, we shouldn't need to use `TypeMention` at all, because `getATypeUse_i` captures - // all the AST structures which reference types. Unfortunately, there is one case where the AST - // type structures don't capture full fidelity type information: type arguments of template - // instantiations and specializations which are typedef'd types. Instead of the type argument - // being represented as the typedef type, it is represented as the _underlying_ type. There is, - // however, a type mention in the appropriate place for the typedef'd type. We therefore use - // the TypeMention information in this one case only, restricting it to avoid largely duplicating - // the work already determined as part of `getATypeUse_i`. - // - // For example, in: - // ``` - // 1 | typedef X Y; - // 2 | std::list x; - // ``` - // The type of `x` is `std::list` not `std::list`. There is, however a `TypeMention` of `Y` - // on line 2, we determine `Y` is used - exists(TypeMention tm, TypedefType typedefType | - result = tm and - type = getAnEquivalentTypeDef(typedefType) and - tm.getMentionedType() = typedefType - | - exists(tm.getFile().getRelativePath()) and - exists(getATypeUse_i(typedefType.getUnderlyingType())) - ) -} - -private Locatable getATypeUse_i(Type type) { - ( - // Restrict to uses within the source checkout root - exists(result.getFile().getRelativePath()) - or - // Unless it's an alias template instantiation, as they do not have correct locations (last - // verified in CodeQL CLI 2.7.6) - result instanceof UsingAliasTypedefType and - not exists(result.getLocation()) - ) and - ( - // Used as a variable type - exists(Variable v | result = v | - type = v.getType() and - // Ignore self referential variables and parameters - not v.getDeclaringType().refersTo(type) and - not type = v.(Parameter).getFunction().getDeclaringType() - ) - or - // Used a function return type - exists(Function f | - result = f and - not f.isCompilerGenerated() and - not type = f.getDeclaringType() - | - type = f.getType() - or - type = f.getATemplateArgument() - ) - or - // Used either in a function call as a template argument, or as the declaring type - // of the function - exists(FunctionCall fc | result = fc | - type = fc.getTarget().getDeclaringType() - or - type = fc.getATemplateArgument() - ) - or - // Aliased in a user typedef - exists(TypedefType t | result = t | type = t.getBaseType()) - or - // A use in a `FunctionAccess` - exists(FunctionAccess fa | result = fa | type = fa.getTarget().getDeclaringType()) - or - // A use in a `sizeof` expr - exists(SizeofTypeOperator soto | result = soto | type = soto.getTypeOperand()) - or - // A use in a `Cast` - exists(Cast c | c = result | type = c.getType()) - or - // Use of the type name in source - exists(TypeName t | t = result | type = t.getType()) - or - // Access of an enum constant - exists(EnumConstantAccess eca | result = eca | type = eca.getTarget().getDeclaringEnum()) - or - // Accessing a field on the type - exists(FieldAccess fa | - result = fa and - type = fa.getTarget().getDeclaringType() - ) - or - // Name qualifiers - exists(NameQualifier nq | - result = nq and - type = nq.getQualifyingElement() - ) - ) - or - // Recursive case - used by a used type - exists(Type used | result = getATypeUse_i(used) | - // The `used` class has `type` as a base class - type = used.(DerivedType).getBaseType() - or - // The `used` class has `type` as a template argument - type = used.(Class).getATemplateArgument() - or - // A used class is derived from the type class - type = used.(Class).getABaseClass() - or - // This is a TemplateClass where one of the instantiations is used - type.(TemplateClass).getAnInstantiation() = used - or - // This is a TemplateClass where one of the specializations is used - type = used.(ClassTemplateSpecialization).getPrimaryTemplate() - or - // Alias templates - alias templates and instantiations are not properly captured by the - // extractor (last verified in CodeQL CLI 2.7.6). The only distinguishing factor is that - // instantiations of alias templates do not have a location. - exists(UsingAliasTypedefType template, UsingAliasTypedefType instantiation | - // Instantiation is a "use" of the template - used = instantiation and - type = template and - // The template has a location - exists(template.getLocation()) and - // The instantiation does not - not exists(instantiation.getLocation()) and - // Template and instantiation both have the same qualified name - template.getQualifiedName() = instantiation.getQualifiedName() - ) - ) -} diff --git a/cpp/common/src/codingstandards/cpp/UndefinedBehavior.qll b/cpp/common/src/codingstandards/cpp/UndefinedBehavior.qll new file mode 100644 index 0000000000..24bdd3e3f9 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/UndefinedBehavior.qll @@ -0,0 +1,65 @@ +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.controlflow.Guards +import codingstandards.cpp.Literals +import codingstandards.cpp.Expr +import codingstandards.cpp.Macro +import codingstandards.cpp.Type +import codingstandards.cpp.Function + +/** + * Library for modeling undefined behavior. + */ +abstract class UndefinedBehavior extends Locatable { + abstract string getReason(); +} + +abstract class CPPUndefinedBehavior extends UndefinedBehavior { } + +class ShiftByNegativeOrGreaterPrecisionOperand extends UndefinedBehavior, BitShiftExpr { + string reason; + + ShiftByNegativeOrGreaterPrecisionOperand() { + getPrecision(this.getLeftOperand().getExplicitlyConverted().getUnderlyingType()) <= + upperBound(this.getRightOperand()) and + reason = + "The operand '" + this.getLeftOperand() + "' is shifted by an expression '" + + this.getRightOperand() + "' whose upper bound (" + upperBound(this.getRightOperand()) + + ") is greater than or equal to the precision." and + /* + * this statement is not at a basic block where + * `this_rhs < PRECISION(...)` is ensured + */ + + not exists(GuardCondition gc, BasicBlock block, Expr precisionCall, Expr lTLhs | + block = this.getBasicBlock() and + ( + precisionCall.(FunctionCall).getTarget() instanceof PopCount + or + precisionCall = any(PrecisionMacro pm).getAnInvocation().getExpr() + ) + | + globalValueNumber(lTLhs) = globalValueNumber(this.getRightOperand()) and + gc.ensuresLt(lTLhs, precisionCall, 0, block, true) + ) + or + lowerBound(this.getRightOperand()) < 0 and + reason = + "The operand '" + this.getLeftOperand() + "' is shifted by an expression '" + + this.getRightOperand() + "' which may be negative." and + /* + * this statement is not at a basic block where + * `this_rhs > 0` is ensured + */ + + not exists(GuardCondition gc, BasicBlock block, Expr literalZero, Expr lTLhs | + block = this.getBasicBlock() and + literalZero instanceof LiteralZero and + globalValueNumber(lTLhs) = globalValueNumber(this.getRightOperand()) and + gc.ensuresLt(literalZero, lTLhs, 0, block, true) + ) + } + + override string getReason() { result = reason } +} diff --git a/cpp/common/src/codingstandards/cpp/Variable.qll b/cpp/common/src/codingstandards/cpp/Variable.qll index dba7af480a..47c6ca7f6c 100644 --- a/cpp/common/src/codingstandards/cpp/Variable.qll +++ b/cpp/common/src/codingstandards/cpp/Variable.qll @@ -5,3 +5,17 @@ import semmle.code.cpp.PODType03 class ScalarVariable extends Variable { ScalarVariable() { isScalarType03(this.getType()) } } + +/** + * Returns the target variable of a `VariableAccess`. + * If the access is a field access, then the target is the `Variable` of the qualifier. + * If the access is an array access, then the target is the array base. + */ +Variable getAddressOfExprTargetBase(AddressOfExpr expr) { + result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget() + or + not expr.getOperand() instanceof ValueFieldAccess and + result = expr.getOperand().(VariableAccess).getTarget() + or + result = expr.getOperand().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() +} diff --git a/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll b/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll new file mode 100644 index 0000000000..b41de3ef9a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll @@ -0,0 +1,426 @@ +import codingstandards.cpp.AlertReporting + +/** + * A configuration for deduplicating query results inside of macros. + * + * See doc comment on `DeduplicateMacroResults` module. + */ +signature module DeduplicateMacroConfigSig { + /** + * Stringify the `ResultElement`. All `ResultElement`s that share an "identity" should stringify + * to the same string to get proper results. + */ + string describe(ResultElement re); +} + +/** + * A configuration for generating reports from reports that may or may not be duplicated across + * macro expansions. + * + * See doc comment on `DeduplicateMacroResults` module. + * + * This signature is used to parameterize the module `DeduplicateMacroResults::Report`. + */ +signature module MacroReportConfigSig { + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description); + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m); + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(ResultElement element); + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro(ResultElement element, Locatable optExtraLoc1, string optExtraStr1); +} + +/** + * A module for taking the results of `MacroUnwrapper` and consolidating them. + * + * The module `MacroUnwrapper` is great for simple alerts such as usage of banned functions. In + * such cases, reporting "call to 'foo()' in macro 'M'" will only have one result even if the macro + * is expanded multiple times. + * + * However, other queries may have a dynamic message which can vary per macro call site due to + * token manipulation (`a ## b`), for instance, "Macro 'M' defines unused object 'generated_name_x'" + * which will lead to hundreds of results if there are hundreds of such generated names. + * + * This module can be used to find and easily report non-compliant behavior, grouped by the macro + * it originates in, regardless of whether the messages will exactly match. + * + * ## General Usage + * + * To use this macro, define a class for the relevant behavior, and a means of stringifying + * relevant elements as a config, to parameterize the `DeduplicateMacroResults` module. + * + * ``` + * class InvalidFoo extends Foo { + * InvalidFoo() { ... } + * } + * + * module DeduplicateFooInMacrosConfig implements DeduplicateMacroConfigSig { + * string describe(InvalidFoo foo) { result = ... } + * } + * + * import DeduplicateMacroResults as DeduplicateFooInMacros; + * ``` + * + * This module exposes the following classes: + * - `PrimaryMacroSameResultElementInAllInvocations extends Macro`: Every invocation of this macro + * generates an `InvalidFoo` which stringifies to the same thing. Use the method + * `getResultElementDescription()` to get that shared string. + * - `PrimaryMacroDifferentResultElementInAllInvocations extends Macro`: Every invocation of this + * macro generates an `InvalidFoo`, but they do not all stringify to the same thing. Use the + * method `getExampleResultElement()` to get an *single* example `InvalidFoo` to help users fix + * and understand the issue. + * - `IsolatedMacroExpansionWithResultElement extends MacroInvocation`: An invocation of a macro + * which in this particular instance generates an `InvalidFoo`, while other invocations of the + * same macro do not. + * + * The three above classes all attempt to resolve to the most *specific* macro to the issue at + * hand. For instance, if macro `M1` calls macro `M2` which expands to an `InvalidFoo`, then the + * problem may be with `M2` (it is the most specific macro call here), or the problem may be with + * `M2` (if all expansions of `M2` generate an `InvalidFoo` but not all expansions of `M1` do so). + * + * ## Generating Report Objects + * + * This module also can be used to more easily report issues across these cases, by implementing + * `MacroReportConfigSig` and importing `DeduplicateMacroResults::Report::ReportResult`. + * + * ``` + * module InvalidFooInMacroReportConfig implements MacroReportConfigSig { + * + * // ***Take care that usage of $@ is correct in the following predicates***!!!! + * bindingset[description] + * string getMessageSameResultInAllExpansions(Macro m, string description) { + * result = "Macro " + m.getName() + " always has invalid foo " + description + * } + * + * string getMessageVariedResultInAllExpansions(Macro m) { + * result = "Macro " + m.getName() + " always has invalid foo, for example '$@'." + * } + * + * string getMessageResultInIsolatedExpansion(InvalidFoo foo) { + * result = "Invocation of macro $@ has invalid foo '" + foo.getName() + "'." + * } + * + * string getMessageNotInMacro(ResultElement element) { + * result = "Invalid foo '" + element.getName() + "'." + * } + * } + * + * import DeduplicateFooInMacros::Report as Report; + * + * from Report::ReportResult report + * where not excluded(report.getPrimaryElement(), ...) + * select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), + * report.getOptionalPlaceholderMessage() + * ``` + * + * Note that this does *not* (currently) generate a result for elements not contained by a macro. + * To do report such cases, either add support for that in this module, or write a separate query + * that reports `InvalidFoo` cases where not `.isInMacroExpansion()`. + */ +module DeduplicateMacroResults< + ResultType ResultElement, DeduplicateMacroConfigSig Config> +{ + /* A final class so that we may extend Macro. */ + final private class FinalMacro = Macro; + + /* Helper final class import so that we may reference/extend it. */ + final private class ResultMacroExpansion = MacroUnwrapper::ResultMacroExpansion; + + /** + * A macro for which all of its invocations produce an element that is described the same way. + * + * This is not necessarily the "primary" / most specific macro for these result elements. + * This difference is captured in `PrimarySameResultElementInAllMacroInvocations`, and the two + * classes are only separate to avoid non-monotonic recursion. + */ + private class SameResultElementInAllMacroInvocations extends FinalMacro { + string resultElementDescription; + + SameResultElementInAllMacroInvocations() { + forex(MacroInvocation mi | mi = getAnInvocation() | + Config::describe(mi.(ResultMacroExpansion).getResultElement()) = resultElementDescription + ) + } + + string getResultElementDescription() { result = resultElementDescription } + + ResultElement getAResultElement() { + result = getAnInvocation().(ResultMacroExpansion).getResultElement() + } + } + + /** + * A macro for which all of its invocations produce an element that is described the same way. + * + * This is the necessarily the "primary" / most specific macro for these result elements. + */ + class PrimaryMacroSameResultElementInAllInvocations extends SameResultElementInAllMacroInvocations + { + PrimaryMacroSameResultElementInAllInvocations() { + not exists(MacroInvocation inner | + inner.getParentInvocation() = getAnInvocation() and + inner.getMacro() instanceof SameResultElementInAllMacroInvocations + ) + } + } + + /** + * A expansion that generates a `ResultElement` that is uniquely described by the config. + * + * This is used so that we can find a single example invocation site to report as an example for + * macros which generate an array of different `ResultElement`s that are described differently. + * + * For example, two macro invocations may be given the same arguments, and generate the same + * `ResultElement`, while a third macro invocation is unique and generates a unique + * `ResultElement`. We wish to direct the user to that unique example or we will show the user + * two different reports for one underlying issue. + */ + private class UniqueResultMacroExpansion extends ResultMacroExpansion { + UniqueResultMacroExpansion() { + not exists(ResultMacroExpansion other | + not this = other and + this.getMacroName() = other.getMacroName() and + Config::describe(this.getResultElement()) = Config::describe(other.getResultElement()) + ) + } + } + + /** + * A macro for which all of its invocations produce an element, but they are not all described the + * same way. + * + * This is not necessarily the "primary" / most specific macro for these result elements. + * This difference is captured in `PrimaryDiferentResultElementInAllMacroInvocations`, and the two + * classes are only separate to avoid non-monotonic recursion. + */ + private class DifferentResultElementInAllMacroInvocations extends FinalMacro { + ResultElement exampleResultElement; + + DifferentResultElementInAllMacroInvocations() { + forex(MacroInvocation mi | mi = getAnInvocation() | mi instanceof ResultMacroExpansion) and + count(getAnInvocation().(ResultMacroExpansion).getResultElement()) > 1 and + exists(string description | + description = + rank[1](Config::describe(getAnInvocation().(UniqueResultMacroExpansion).getResultElement()) + ) and + Config::describe(exampleResultElement) = description and + exampleResultElement = getAnInvocation().(ResultMacroExpansion).getResultElement() + ) + } + + ResultElement getExampleResultElement() { result = exampleResultElement } + + ResultElement getAResultElement() { + result = getAnInvocation().(ResultMacroExpansion).getResultElement() + } + } + + /** + * A macro for which all of its invocations produce an element, but they are not all described the + * same way. + * + * This is "primary" / most specific macro for these result elements. + */ + class PrimaryMacroDifferentResultElementInAllInvocations extends DifferentResultElementInAllMacroInvocations + { + PrimaryMacroDifferentResultElementInAllInvocations() { + not exists(MacroInvocation inner | + inner.getParentInvocation() = getAnInvocation() and + inner.getMacro() instanceof DifferentResultElementInAllMacroInvocations + ) + } + } + + /** + * Convenience predicate to know when invalid macro expansions have been reported at their macro + * definition. + */ + private predicate reported(Macro macro) { + macro instanceof PrimaryMacroSameResultElementInAllInvocations or + macro instanceof PrimaryMacroDifferentResultElementInAllInvocations + } + + /** + * A macro invocation for which the target macro does not always produce a `ResultElement`, but + * this specific invocation of it does. + * + * This is the "primary" / most specific macro for these result elements. It also does not match + * `MacroInvocation`s inside of a `MacroInvocation` of a `Macro` which always produces a + * `ResultElement`, indicating that the real problem lies with that other `Macro` instead of with + * this particular invocation. + */ + class IsolatedMacroExpansionWithResultElement extends ResultMacroExpansion { + IsolatedMacroExpansionWithResultElement() { + not reported(getParentInvocation*().getMacro()) and + not exists(MacroInvocation inner | + reported(inner.getMacro()) and + inner.getParentInvocation*() = this + ) and + not exists(ResultMacroExpansion moreSpecific | + moreSpecific.getResultElement() = getResultElement() and + moreSpecific.getParentInvocation+() = this + ) + } + } + + /** + * A module for generating reports across the various cases of problematic macros, problematic + * macro invocations. + * + * See the doc comment for the `DeduplicateMacroResults` module for more info. + */ + module Report ReportConfig> { + newtype TReportResult = + TReportMacroResultWithSameName(PrimaryMacroSameResultElementInAllInvocations def) or + TReportMacroResultWithVariedName(PrimaryMacroDifferentResultElementInAllInvocations def) or + TReportIsolatedMacroResult(IsolatedMacroExpansionWithResultElement def) or + TReportNotInMacro(ResultElement def) { + not exists(ResultMacroExpansion macroExpansion | macroExpansion.getResultElement() = def) + } + + /** + * An instance of a `ResultElement` to be reported to a user. + * + * To show a report, use the following methods: + * - `report.getPrimaryElement()` + * - `report.getMessage()` + * - `report.getOptionalPlaceholderLocatable()` + * - `report.getOptionalPlaceholderMessage()` + * + * The values returned by these methods are configured by the `MacroReportConfigSig` + * signature parameter. + */ + class ReportResult extends TReportResult { + string toString() { + this = TReportMacroResultWithVariedName(_) and + result = + "Macro that always expands to a result element with invocation-dependent description" + or + this = TReportMacroResultWithSameName(_) and + result = "Macro that always expands to a result element with a constant description" + or + this = TReportIsolatedMacroResult(_) and + result = + "Specific macro expansion which produces a result element, but not all expansions do" + or + this = TReportNotInMacro(_) and + result = "Result element that is not in a macro" + } + + string getMessage() { + exists(PrimaryMacroDifferentResultElementInAllInvocations def | + this = TReportMacroResultWithVariedName(def) and + result = ReportConfig::getMessageVariedResultInAllExpansions(def) + ) + or + exists(PrimaryMacroSameResultElementInAllInvocations def | + this = TReportMacroResultWithSameName(def) and + result = + ReportConfig::getMessageSameResultInAllExpansions(def, def.getResultElementDescription()) + ) + or + exists(IsolatedMacroExpansionWithResultElement def | + this = TReportIsolatedMacroResult(def) and + result = ReportConfig::getMessageResultInIsolatedExpansion(def.getResultElement()) + ) + or + exists(ResultElement def | + this = TReportNotInMacro(def) and + result = ReportConfig::getMessageNotInMacro(def, _, _) + ) + } + + Element getPrimaryElement() { + this = TReportMacroResultWithSameName(result) + or + this = TReportMacroResultWithVariedName(result) + or + this = TReportIsolatedMacroResult(result) + or + this = TReportNotInMacro(result) + } + + Locatable getOptionalPlaceholderLocatable() { + exists(PrimaryMacroDifferentResultElementInAllInvocations def | + this = TReportMacroResultWithVariedName(def) and + result = def.getExampleResultElement() + ) + or + exists(PrimaryMacroSameResultElementInAllInvocations def | + this = TReportMacroResultWithSameName(def) and + result = def + ) + or + exists(IsolatedMacroExpansionWithResultElement def | + this = TReportIsolatedMacroResult(def) and + result = def.getMacro() + ) + or + exists(ResultElement def | + this = TReportNotInMacro(def) and + exists(ReportConfig::getMessageNotInMacro(def, result, _)) + ) + } + + string getOptionalPlaceholderMessage() { + exists(PrimaryMacroDifferentResultElementInAllInvocations def | + this = TReportMacroResultWithVariedName(def) and + result = Config::describe(def.getExampleResultElement()) + ) + or + this = TReportMacroResultWithSameName(_) and + result = "(ignored)" + or + this = TReportIsolatedMacroResult(_) and + result = getMacro().getName() + or + exists(ResultElement def | + this = TReportNotInMacro(def) and + exists(ReportConfig::getMessageNotInMacro(def, _, result)) + ) + } + + Macro getMacro() { + this = TReportMacroResultWithVariedName(result) + or + this = TReportMacroResultWithSameName(result) + or + exists(IsolatedMacroExpansionWithResultElement def | + this = TReportIsolatedMacroResult(def) and + result = def.getMacro() + ) + } + + ResultMacroExpansion getAResultMacroExpansion() { + exists(PrimaryMacroDifferentResultElementInAllInvocations def | + this = TReportMacroResultWithVariedName(def) and + result = def.getAnInvocation() + ) + or + exists(PrimaryMacroSameResultElementInAllInvocations def | + this = TReportMacroResultWithSameName(def) and + result = def.getAnInvocation() + ) + or + this = TReportIsolatedMacroResult(result) + } + + ResultElement getAResultElement() { + result = getAResultMacroExpansion().getResultElement() + or + this = TReportNotInMacro(result) + } + } + } +} diff --git a/cpp/common/src/codingstandards/cpp/alertreporting/HoldsForAllCopies.qll b/cpp/common/src/codingstandards/cpp/alertreporting/HoldsForAllCopies.qll new file mode 100644 index 0000000000..1d47e833dc --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/alertreporting/HoldsForAllCopies.qll @@ -0,0 +1,117 @@ +/** + * A module for considering whether a result occurs in all copies of the code at a given location. + * + * Multiple copies of an element at the same location can occur for two main reasons: + * 1. Instantiations of a template + * 2. Re-compilation of a file under a different context + * This module helps ensure that a particular condition holds for all copies of a particular logical + * element. For example, this can be used to determine whether a line of code is dead in all copies + * of a piece of code. + * + * This module is parameterized by a set of _candidate_ elements in the program. For each candidate + * element, we determine whether all other elements in the same element set that occur at the same + * location in the program are also part of the same set, ignoring any results generated by macros. + * + * We do so by reporting a new type of result, `LogicalResultElement`, which represents a logical result + * where all instances of a element at a given location are considered to be part of the same set. + */ + +import cpp + +/** + * Holds if the `Element` `e` is not within a macro expansion, i.e. generated by a macro, but not + * the outermost `Element` or `Expr` generated by the macro. + */ +predicate isNotWithinMacroExpansion(Element e) { + not e.isInMacroExpansion() + or + exists(MacroInvocation mi | + mi.getStmt() = e + or + mi.getExpr() = e + or + mi.getStmt().(ExprStmt).getExpr() = e + | + not exists(mi.getParentInvocation()) + ) +} + +/** + * A type representing a set of Element's in the program that satisfy some condition. + * + * `HoldsForAllCopies::LogicalResultElement` will represent an element in this set + * iff all copies of that element satisfy the condition. + */ +signature class CandidateElementSig extends Element; + +/** The super set of relevant elements. */ +signature class ElementSetSig extends Element; + +/** + * A module for considering whether a result occurs in all copies of the code at a given location. + */ +module HoldsForAllCopies { + private predicate hasLocation( + ElementSet s, string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + s.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + final private class MyElement = ElementSet; + + /** + * A `Element` that appears at the same location as a candidate element. + */ + private class RelevantElement extends MyElement { + CandidateElement e; + + RelevantElement() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + hasLocation(this, filepath, startline, startcolumn, endline, endcolumn) and + hasLocation(e, filepath, startline, startcolumn, endline, endcolumn) + ) and + // Not within a macro expansion, as we cannot match up instances by location in that + // case + isNotWithinMacroExpansion(this) and + // Ignore catch handlers, as they occur at the same location as the catch block + not this instanceof Handler + } + + CandidateElement getCandidateElement() { result = e } + } + + newtype TResultElements = + TLogicalResultElement( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(CandidateElement s | + hasLocation(s, filepath, startline, startcolumn, endline, endcolumn) and + // All relevant elements that occur at the same location are candidates + forall(RelevantElement relevantElement | s = relevantElement.getCandidateElement() | + relevantElement instanceof CandidateElement + ) + ) + } + + /** + * A logical result element representing all copies of an element that occur at the same + * location, iff they all belong to the `CandidateElement` set. + */ + class LogicalResultElement extends TLogicalResultElement { + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this = TLogicalResultElement(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets a copy instance of this logical result element. */ + CandidateElement getAnElementInstance() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + this = TLogicalResultElement(filepath, startline, startcolumn, endline, endcolumn) and + hasLocation(result, filepath, startline, startcolumn, endline, endcolumn) + ) + } + + string toString() { result = getAnElementInstance().toString() } + } +} diff --git a/cpp/common/src/codingstandards/cpp/allocations/PlacementNew.qll b/cpp/common/src/codingstandards/cpp/allocations/PlacementNew.qll index 1812397c8a..2c9139d0ae 100644 --- a/cpp/common/src/codingstandards/cpp/allocations/PlacementNew.qll +++ b/cpp/common/src/codingstandards/cpp/allocations/PlacementNew.qll @@ -158,20 +158,20 @@ class AllocationExprPlacementNewOrigin extends PlacementNewMemoryOrigin { * A data flow configuration that identifies the origin of the placement argument to a placement * new expression. */ -class PlacementNewOriginConfig extends DataFlow::Configuration { - PlacementNewOriginConfig() { this = "PlacementNewOrigin" } +module PlacementNewOriginConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof PlacementNewMemoryOrigin } - override predicate isSource(DataFlow::Node source) { source instanceof PlacementNewMemoryOrigin } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(PlacementNewExpr pne).getPlacementExpr() // TODO direct calls to placement operator new? } - override predicate isAdditionalFlowStep(DataFlow::Node stepFrom, DataFlow::Node stepTo) { + predicate isAdditionalFlowStep(DataFlow::Node stepFrom, DataFlow::Node stepTo) { // Slightly surprisingly, we can't see the `StaticOrCStyleCast`s as a source out-of-the-box with data // flow - it's only reported under taint tracking. We therefore add a step through static // casts so that we can see them as sources. stepTo.asExpr().(StaticOrCStyleCast).getExpr() = stepFrom.asExpr() } } + +module PlacementNewOriginFlow = DataFlow::Global; diff --git a/cpp/common/src/codingstandards/cpp/concurrency/Atomic.qll b/cpp/common/src/codingstandards/cpp/concurrency/Atomic.qll new file mode 100644 index 0000000000..44101f08bb --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/Atomic.qll @@ -0,0 +1,43 @@ +import cpp + +/** + * Models calls to routines `atomic_compare_exchange_weak` and + * `atomic_compare_exchange_weak_explicit` in the `stdatomic` library. + * Note that these are typically implemented as macros within Clang and + * GCC's standard libraries. + */ +class AtomicCompareExchange extends MacroInvocation { + AtomicCompareExchange() { + getMacroName() = "atomic_compare_exchange_weak" + or + // some compilers model `atomic_compare_exchange_weak` as a macro that + // expands to `atomic_compare_exchange_weak_explicit` so this defeats that + // and other similar modeling. + getMacroName() = "atomic_compare_exchange_weak_explicit" and + not exists(MacroInvocation m | + m.getMacroName() = "atomic_compare_exchange_weak" and + m.getAnExpandedElement() = getAnExpandedElement() + ) + } +} + +/** + * Models calls to routines `atomic_store` and + * `atomic_store_explicit` in the `stdatomic` library. + * Note that these are typically implemented as macros within Clang and + * GCC's standard libraries. + */ +class AtomicStore extends MacroInvocation { + AtomicStore() { + getMacroName() = "atomic_store" + or + // some compilers model `atomic_compare_exchange_weak` as a macro that + // expands to `atomic_compare_exchange_weak_explicit` so this defeats that + // and other similar modeling. + getMacroName() = "atomic_store_explicit" and + not exists(MacroInvocation m | + m.getMacroName() = "atomic_store" and + m.getAnExpandedElement() = getAnExpandedElement() + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/CConditionOperation.qll b/cpp/common/src/codingstandards/cpp/concurrency/CConditionOperation.qll new file mode 100644 index 0000000000..adf230f08d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/CConditionOperation.qll @@ -0,0 +1,31 @@ +import cpp + +/** + * Models a function which uses a c condition variable. Not integrated into the thread aware CFG. + */ +class CConditionOperation extends FunctionCall { + CConditionOperation() { + getTarget().hasName(["cnd_broadcast", "cnd_signal", "cnd_timedwait", "cnd_wait", "cnd_init"]) + } + + predicate isInit() { getTarget().hasName("cnd_init") } + + predicate isUse() { not isInit() } + + Expr getConditionExpr() { result = getArgument(0) } + + /* Note: only holds for `cnd_wait()` and `cnd_timedwait()` */ + Expr getMutexExpr() { result = getArgument(1) } +} + +/** + * Models C style condition destruction via `cnd_destroy`. + */ +class C11ConditionDestroyer extends FunctionCall { + C11ConditionDestroyer() { getTarget().getName() = "cnd_destroy" } + + /** + * Returns the `Expr` being destroyed. + */ + Expr getConditionExpr() { result = getArgument(0) } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ConditionalWait.qll b/cpp/common/src/codingstandards/cpp/concurrency/ConditionalWait.qll new file mode 100644 index 0000000000..e69ea2fee5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ConditionalWait.qll @@ -0,0 +1,26 @@ +import cpp + +/** + * Models a function that conditionally waits. + */ +abstract class ConditionalWait extends FunctionCall { } + +/** + * Models a function in CPP that will conditionally wait. + */ +class CPPConditionalWait extends ConditionalWait { + CPPConditionalWait() { + exists(MemberFunction mf | + mf = getTarget() and + mf.getDeclaringType().hasQualifiedName("std", "condition_variable") and + mf.getName() in ["wait", "wait_for", "wait_until"] + ) + } +} + +/** + * Models a function in C that will conditionally wait. + */ +class CConditionalWait extends ConditionalWait { + CConditionalWait() { getTarget().getName() in ["cnd_wait"] } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ControlFlow.qll b/cpp/common/src/codingstandards/cpp/concurrency/ControlFlow.qll new file mode 100644 index 0000000000..15f8ab5a61 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ControlFlow.qll @@ -0,0 +1,101 @@ +import cpp +private import codingstandards.cpp.concurrency.ThreadedFunction + +/** + * Models a control flow node within a function that may be executed by some + * thread. + */ +class ThreadedCFN extends ControlFlowNode { + ThreadedCFN() { + exists(ThreadedFunction tf | this = getAThreadContextAwareSuccessor(tf.getEntryPoint())) + } +} + +/** + * Models CFG nodes which should be added to a thread context. + */ +abstract class ThreadedCFGPathExtension extends ControlFlowNode { + /** + * Returns the next `ControlFlowNode` in this thread context. + */ + abstract ControlFlowNode getNext(); +} + +/** + * Models a `FunctionCall` invoked from a threaded context. + */ +class ThreadContextFunctionCall extends FunctionCall, ThreadedCFGPathExtension { + override ControlFlowNode getNext() { getTarget().getEntryPoint() = result } +} + +/** + * Models a specialized `FunctionCall` that may create a thread. + */ +abstract class ThreadCreationFunction extends FunctionCall, ThreadedCFGPathExtension { + /** + * Returns the function that will be invoked. + */ + abstract Function getFunction(); +} + +/** + * The thread-aware predecessor function is defined in terms of the thread aware + * successor function. This is because it is simpler to construct the forward + * paths of a thread's execution than the backwards paths. For this reason we + * require a `start` and `end` node. + * + * The logic of this function is that a thread aware predecessor is one that + * follows a `start` node, is not equal to the ending node, and does not follow + * the `end` node. Such nodes can only be predecessors of `end`. + * + * For this reason this function requires a `start` node from which to start + * considering something a predecessor of `end`. + */ +pragma[inline] +ControlFlowNode getAThreadContextAwarePredecessor(ControlFlowNode start, ControlFlowNode end) { + result = getAThreadContextAwareSuccessor(start) and + not result = getAThreadContextAwareSuccessor(end) and + not result = end +} + +/** + * A predicate for finding successors of `ControlFlowNode`s that are aware of + * the objects that my flow into a thread's context. This is achieved by adding + * additional edges to thread entry points and function calls. + */ +ControlFlowNode getAThreadContextAwareSuccessorR(ControlFlowNode cfn) { + result = cfn.getASuccessor() + or + result = cfn.(ThreadedCFGPathExtension).getNext() +} + +ControlFlowNode getAThreadContextAwareSuccessor(ControlFlowNode m) { + result = getAThreadContextAwareSuccessorR*(m) and + // for performance reasons we handle back edges by enforcing a lexical + // ordering restriction on these nodes if they are both in + // the same loop. One way of doing this is as follows: + // + // ````and ( + // exists(Loop loop | + // loop.getAChild*() = m and + // loop.getAChild*() = result + // ) + // implies + // not result.getLocation().isBefore(m.getLocation()) + // )``` + // In this implementation we opt for the more generic form below + // which seems to have reasonable performance. + ( + m.getEnclosingStmt().getParentStmt*() = result.getEnclosingStmt().getParentStmt*() + implies + not exists(Location l1, Location l2 | + l1 = result.getLocation() and + l2 = m.getLocation() + | + l1.getEndLine() < l2.getStartLine() + or + l1.getStartLine() = l2.getEndLine() and + l1.getEndColumn() < l2.getStartColumn() + ) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/LockProtectedControlFlow.qll b/cpp/common/src/codingstandards/cpp/concurrency/LockProtectedControlFlow.qll new file mode 100644 index 0000000000..a828ec8768 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/LockProtectedControlFlow.qll @@ -0,0 +1,49 @@ +import cpp +private import codingstandards.cpp.concurrency.ControlFlow +private import codingstandards.cpp.concurrency.LockingOperation + +/** + * Models a `ControlFlowNode` that is protected by some sort of lock. + */ +class LockProtectedControlFlowNode extends ThreadedCFN { + FunctionCall lockingFunction; + + LockProtectedControlFlowNode() { + exists(LockingOperation lock | + // there is a node that is a lock + lockingFunction = lock and + lock.isLock() and + // this node should be a successor of this lock + this = getAThreadContextAwareSuccessor(lock) and + // and there should not exist a predecessor of this + // node that is an unlock. Since we are doing thread context + // aware tracking it is easier to go forwards than backwards + // in constructing the call graph. Thus we can define predecessor + // in terms of a node that is a successor of the lock but NOT a + // successor of the current node. + not exists(ControlFlowNode unlock | + // it's an unlock + unlock = getAThreadContextAwarePredecessor(lock, this) and + unlock.(MutexFunctionCall).isUnlock() and + // note that we don't check that it's the same lock -- this is left + // to the caller to enforce this condition. + // Because of the way that `getAThreadContextAwarePredecessor` works, it is possible + // for operations PAST it to be technically part of the predecessors. + // Thus, we need to make sure that this node is a + // successor of the unlock in the CFG + getAThreadContextAwareSuccessor(unlock) = this + ) and + (lock instanceof MutexFunctionCall implies not this.(MutexFunctionCall).isUnlock()) + ) + } + + /** + * The `MutexFunctionCall` holding the lock that locks this node. + */ + FunctionCall coveredByLock() { result = lockingFunction } + + /** + * The lock underlying this `LockProtectedControlFlowNode`. + */ + Variable getAProtectingLock() { result = lockingFunction.(LockingOperation).getLock() } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/LockingOperation.qll b/cpp/common/src/codingstandards/cpp/concurrency/LockingOperation.qll new file mode 100644 index 0000000000..0aab11a269 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/LockingOperation.qll @@ -0,0 +1,235 @@ +import cpp +private import semmle.code.cpp.dataflow.TaintTracking + +abstract class LockingOperation extends FunctionCall { + /** + * Returns the target of the lock underlying this RAII-style lock. + */ + abstract Variable getLock(); + + /** + * Returns the lock underlying this RAII-style lock. + */ + abstract Expr getLockExpr(); + + /** + * Holds if this is a lock operation + */ + abstract predicate isLock(); + + /** + * Holds if this is an unlock operation + */ + abstract predicate isUnlock(); + + /** + * Holds if this locking operation is really a locking operation within a + * designated locking operation. This library assumes the underlying locking + * operations are implemented correctly in that calling a `LockingOperation` + * results in the creation of a singular lock. + */ + predicate isLockingOperationWithinLockingOperation(LockingOperation inner) { + exists(LockingOperation outer | outer.getTarget() = inner.getEnclosingFunction()) + } +} + +/** + * Common base class providing an interface into function call + * based mutex locks. + */ +abstract class MutexFunctionCall extends LockingOperation { + abstract predicate isRecursive(); + + abstract predicate isSpeculativeLock(); + + abstract predicate unlocks(MutexFunctionCall fc); +} + +/** + * Models calls to various mutex types found in CPP. + */ +class CPPMutexFunctionCall extends MutexFunctionCall { + VariableAccess var; + + CPPMutexFunctionCall() { + getTarget() + .(MemberFunction) + .getDeclaringType() + .hasQualifiedName("std", + ["mutex", "timed_mutex", "shared_timed_mutex", "recursive_mutex", "recursive_timed_mutex"]) and + var = getQualifier() + } + + /** + * Holds if this mutex is a recursive mutex. + */ + override predicate isRecursive() { + getTarget() + .(MemberFunction) + .getDeclaringType() + .hasQualifiedName("std", ["recursive_mutex", "recursive_timed_mutex"]) + } + + /** + * Holds if this `CPPMutexFunctionCall` is a lock. + */ + override predicate isLock() { + not isLockingOperationWithinLockingOperation(this) and + getTarget().getName() = "lock" + } + + /** + * Holds if this `CPPMutexFunctionCall` is a speculative lock, defined as calling + * one of the speculative locking functions such as `try_lock`. + */ + override predicate isSpeculativeLock() { + getTarget().getName() in [ + "try_lock", "try_lock_for", "try_lock_until", "try_lock_shared_for", "try_lock_shared_until" + ] + } + + /** + * Returns the lock to which this `CPPMutexFunctionCall` refers to. + */ + override Variable getLock() { result = getQualifier().(VariableAccess).getTarget() } + + /** + * Returns the qualifier for this `CPPMutexFunctionCall`. + */ + override Expr getLockExpr() { result = var } + + /** + * Holds if this is a `unlock` and *may* unlock the previously locked `MutexFunctionCall`. + * This predicate does not check that the mutex is currently locked. + */ + override predicate unlocks(MutexFunctionCall fc) { + isUnlock() and + fc.getQualifier().(VariableAccess).getTarget() = getQualifier().(VariableAccess).getTarget() + } + + /** + * Holds if this is an unlock call. + */ + override predicate isUnlock() { getTarget().getName() = "unlock" } +} + +/** + * Models calls to various mutex types specialized to C code. + */ +class CMutexFunctionCall extends MutexFunctionCall { + Expr arg; + + CMutexFunctionCall() { + // the non recursive kinds + getTarget().getName() = ["mtx_lock", "mtx_unlock", "mtx_timedlock", "mtx_trylock"] and + arg = getArgument(0) + } + + /** + * Holds if this mutex is a recursive mutex. + */ + override predicate isRecursive() { none() } + + /** + * Holds if this `CMutexFunctionCall` is a lock. + */ + override predicate isLock() { + not isLockingOperationWithinLockingOperation(this) and + getTarget().getName() = ["mtx_lock", "mtx_timedlock", "mtx_trylock"] + } + + /** + * Holds if this `CMutexFunctionCall` is a speculative lock, defined as calling + * one of the speculative locking functions such as `try_lock`. + */ + override predicate isSpeculativeLock() { + getTarget().getName() in ["mtx_timedlock", "mtx_trylock"] + } + + /** + * Returns the `Variable` to which this `CMutexFunctionCall` refers to. For this + * style of lock it can reference a number of different variables. + */ + override Variable getLock() { + exists(VariableAccess va | + TaintTracking::localTaint(DataFlow::exprNode(va), DataFlow::exprNode(getLockExpr())) and + result = va.getTarget() + ) + } + + /** + * Returns the expression for this `CMutexFunctionCall`. + */ + override Expr getLockExpr() { result = arg } + + /** + * Holds if this is a `unlock` and *may* unlock the previously locked `CMutexFunctionCall`. + * This predicate does not check that the mutex is currently locked. + */ + override predicate unlocks(MutexFunctionCall fc) { + isUnlock() and + fc.getLock() = getLock() + } + + /** + * Holds if this is an unlock call. + */ + override predicate isUnlock() { getTarget().getName() = "mtx_unlock" } +} + +/** + * Models a RAII-Style lock. + */ +class RAIIStyleLock extends LockingOperation { + VariableAccess lock; + + RAIIStyleLock() { + ( + getTarget().getDeclaringType().hasQualifiedName("std", "lock_guard") or + getTarget().getDeclaringType().hasQualifiedName("std", "unique_lock") or + getTarget().getDeclaringType().hasQualifiedName("std", "scoped_lock") + ) and + ( + lock = getArgument(0).getAChild*() + or + this instanceof DestructorCall and + exists(RAIIStyleLock constructor | + constructor = getQualifier().(VariableAccess).getTarget().getInitializer().getExpr() and + lock = constructor.getArgument(0).getAChild*() + ) + ) + } + + /** + * Holds if this is a lock operation + */ + override predicate isLock() { + not isLockingOperationWithinLockingOperation(this) and + this instanceof ConstructorCall and + lock = getArgument(0).getAChild*() and + // defer_locks don't cause a lock + not exists(Expr exp | + exp = getArgument(1) and + exp.(VariableAccess) + .getTarget() + .getUnderlyingType() + .(Class) + .hasQualifiedName("std", "defer_lock_t") + ) + } + + /** + * Holds if this is an unlock operation + */ + override predicate isUnlock() { this instanceof DestructorCall } + + /** + * Returns the target of the lock underlying this RAII-style lock. + */ + override Variable getLock() { result = lock.getTarget() } + + /** + * Returns the lock underlying this RAII-style lock. + */ + override Expr getLockExpr() { result = lock } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/MutexDestroyer.qll b/cpp/common/src/codingstandards/cpp/concurrency/MutexDestroyer.qll new file mode 100644 index 0000000000..915efc6077 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/MutexDestroyer.qll @@ -0,0 +1,73 @@ +import cpp + +/** + * Models expressions that destroy mutexes. + */ +abstract class MutexDestroyer extends StmtParent { + /** + * Gets the expression that references the mutex being destroyed. + */ + abstract Expr getMutexExpr(); +} + +/** + * Models C style mutex destruction via `mtx_destroy`. + */ +class C11MutexDestroyer extends MutexDestroyer, FunctionCall { + C11MutexDestroyer() { getTarget().getName() = "mtx_destroy" } + + /** + * Returns the `Expr` being destroyed. + */ + override Expr getMutexExpr() { result = getArgument(0) } +} + +/** + * Models a delete expression -- note it is necessary to add this in + * addition to destructors to handle certain implementations of the + * standard library which obscure the destructors of mutexes. + */ +class DeleteMutexDestroyer extends MutexDestroyer { + DeleteMutexDestroyer() { this instanceof DeleteExpr } + + override Expr getMutexExpr() { this.(DeleteExpr).getExpr() = result } +} + +/** + * Models a possible mutex variable that if it goes + * out of scope would destroy an underlying mutex. + */ +class LocalMutexDestroyer extends MutexDestroyer { + Expr assignedValue; + + LocalMutexDestroyer() { + exists(LocalVariable lv | + // static types aren't destroyers + not lv.isStatic() and + // neither are pointers + not lv.getType() instanceof PointerType and + lv.getAnAssignedValue() = assignedValue and + // map the location to the return statements of the + // enclosing function + exists(ReturnStmt rs | + rs.getEnclosingFunction() = assignedValue.getEnclosingFunction() and + rs = this + ) + ) + } + + override Expr getMutexExpr() { result = assignedValue } +} + +/** + * Models implicit or explicit calls to the destructor of a mutex, either via + * a `delete` statement or a variable going out of scope. + */ +class DestructorMutexDestroyer extends MutexDestroyer, DestructorCall { + DestructorMutexDestroyer() { getTarget().getDeclaringType().hasQualifiedName("std", "mutex") } + + /** + * Returns the `Expr` being deleted. + */ + override Expr getMutexExpr() { getQualifier() = result } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ThreadCreation.qll b/cpp/common/src/codingstandards/cpp/concurrency/ThreadCreation.qll new file mode 100644 index 0000000000..4499b993ad --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ThreadCreation.qll @@ -0,0 +1,62 @@ +import cpp +private import codingstandards.cpp.concurrency.ControlFlow + +/** + * Models a call to a thread constructor via `std::thread`. + */ +class ThreadConstructorCall extends ConstructorCall, ThreadCreationFunction { + Function f; + + ThreadConstructorCall() { + getTarget().getDeclaringType().hasQualifiedName("std", "thread") and + f = getArgument(0).(FunctionAccess).getTarget() + } + + /** + * Returns the function that will be invoked by this `std::thread`. + */ + override Function getFunction() { result = f } + + override ControlFlowNode getNext() { result = getFunction().getEntryPoint() } +} + +/** + * Models a call to a thread creation via `thrd_create` or `pthread_create`. + */ +class CThreadCreateCall extends FunctionCall { + Function f; + int fArgIdx; + + CThreadCreateCall() { + ( + getTarget().getName() = "thrd_create" and + fArgIdx = 1 + or + getTarget().getName() = "pthread_create" and + fArgIdx = 2 + ) and + ( + f = getArgument(fArgIdx).(FunctionAccess).getTarget() or + f = getArgument(fArgIdx).(AddressOfExpr).getOperand().(FunctionAccess).getTarget() + ) + } + + /** + * Returns the function that will be invoked by this thread. + */ + Function getFunction() { result = f } +} + +/** + * Models a call to a thread constructor via `thrd_create`. + */ +class C11ThreadCreateCall extends ThreadCreationFunction, CThreadCreateCall { + C11ThreadCreateCall() { getTarget().getName() = "thrd_create" } + + /** + * Returns the function that will be invoked by this thread. + */ + override Function getFunction() { result = f } + + override ControlFlowNode getNext() { result = getFunction().getEntryPoint() } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ThreadDependentMutex.qll b/cpp/common/src/codingstandards/cpp/concurrency/ThreadDependentMutex.qll new file mode 100644 index 0000000000..f86e94566f --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ThreadDependentMutex.qll @@ -0,0 +1,246 @@ +import cpp +import semmle.code.cpp.dataflow.TaintTracking +private import codingstandards.cpp.concurrency.ControlFlow +private import codingstandards.cpp.concurrency.ThreadedFunction + +abstract class MutexSource extends FunctionCall { } + +/** + * Models a C++ style mutex. + */ +class CPPMutexSource extends MutexSource, ConstructorCall { + CPPMutexSource() { getTarget().getDeclaringType().hasQualifiedName("std", "mutex") } +} + +/** + * Models a C11 style mutex. + */ +class C11MutexSource extends MutexSource, FunctionCall { + C11MutexSource() { getTarget().hasName("mtx_init") } + + Expr getMutexExpr() { result = getArgument(0) } + + Expr getMutexTypeExpr() { result = getArgument(1) } + + predicate isRecursive() { + exists(EnumConstantAccess recursive | + recursive = getMutexTypeExpr().getAChild*() and + recursive.getTarget().hasName("mtx_recursive") + ) + } +} + +/** + * Models a thread dependent mutex. A thread dependent mutex is a mutex + * that is used by a thread. This dependency is established either by directly + * passing in a mutex or by referencing a mutex that is in the local scope. The utility + * of this class is it captures the `DataFlow::Node` source at which the mutex + * came from. For example, if it is passed in from a local function to a thread. + * This functionality is critical, since it allows one to inspect how the thread + * behaves with respect to the owner of a resource. + * + * To model the myriad ways this can happen, the subclasses of this class are + * responsible for implementing the various usage patterns. + */ +abstract class ThreadDependentMutex extends DataFlow::Node { + DataFlow::Node sink; + + DataFlow::Node getASource() { + // the source is either the thing that declared + // the mutex + result = this + or + // or the thread we are using it in + result = getAThreadSource() + } + + /** + * Gets the dataflow nodes corresponding to thread local usages of the + * dependent mutex. + */ + DataFlow::Node getAThreadSource() { + // here we line up the actual parameter at the thread creation + // site with the formal parameter in the target thread. + // Note that there are differences between the C and C++ versions + // of the argument ordering in the thread creation function. However, + // since the C version only takes one parameter (as opposed to multiple) + // we can simplify this search by considering only the first argument. + exists(FunctionCall fc, Function f, int n | + // Get the argument to which the mutex flowed. + fc.getArgument(n) = sink.asExpr() and + // Get the thread function we are calling. + f = fc.getArgument(0).(FunctionAccess).getTarget() and + // in C++, there is an extra argument to the `std::thread` call + // so we must subtract 1 since this is not passed to the thread. + ( + result = DataFlow::exprNode(f.getParameter(n - 1).getAnAccess()) + or + // In C, only one argument is allowed. Thus IF the flow predicate holds, + // it will be to the first argument + result = DataFlow::exprNode(f.getParameter(0).getAnAccess()) + ) + ) + } + + /** + * Produces the set of dataflow nodes to thread creation for threads + * that are dependent on this mutex. + */ + DataFlow::Node getADependentThreadCreationExpr() { + exists(FunctionCall fc | + fc.getAnArgument() = sink.asExpr() and + result = DataFlow::exprNode(fc) + ) + } + + /** + * Gets a set of usages of this mutex in both the local and thread scope. + * In the case of scoped usage, this also captures typical accesses of variables. + */ + DataFlow::Node getAUsage() { TaintTracking::localTaint(getASource(), result) } +} + +/** + * This class models the type of thread/mutex dependency that is established + * through the typical parameter passing mechanisms found in C++. + */ +class FlowBasedThreadDependentMutex extends ThreadDependentMutex { + FlowBasedThreadDependentMutex() { + // some sort of dataflow, likely through parameter passing. + ThreadDependentMutexFlow::flow(this, sink) + } +} + +/** + * This class models the type of thread/mutex dependency that is established by + * either scope based accesses (e.g., global variables) or block scope differences. + */ +class AccessBasedThreadDependentMutex extends ThreadDependentMutex { + Variable variableSource; + + AccessBasedThreadDependentMutex() { + // encapsulates usages from outside scopes not directly expressed + // in dataflow. + exists(MutexSource mutexSrc, ThreadedFunction f | + DataFlow::exprNode(mutexSrc) = this and + // find a variable that was assigned the mutex + TaintTracking::localTaint(DataFlow::exprNode(mutexSrc), + DataFlow::exprNode(variableSource.getAnAssignedValue())) and + // find all subsequent accesses of that variable that are within a + // function and set those to the sink + exists(VariableAccess va | + va = variableSource.getAnAccess() and + va.getEnclosingFunction() = f and + sink = DataFlow::exprNode(va) + ) + ) + } + + override DataFlow::Node getAUsage() { DataFlow::exprNode(variableSource.getAnAccess()) = result } +} + +/** + * In the typical C thread model, a mutex is a created by a function that is not responsible + * for creating the variable. Thus this class encodes a slightly different semantics + * wherein the usage pattern is that of variables that have been both initialized + * and then subsequently passed into a thread directly. + */ +class DeclarationInitBasedThreadDependentMutex extends ThreadDependentMutex { + Variable variableSource; + + DeclarationInitBasedThreadDependentMutex() { + exists(MutexSource ms, ThreadCreationFunction tcf | + this = DataFlow::exprNode(ms) and + // accessed as a mutex source + TaintTracking::localTaint(DataFlow::exprNode(variableSource.getAnAccess()), + DataFlow::exprNode(ms.getAnArgument())) and + // subsequently passed to a thread creation function (order not strictly + // enforced for performance reasons) + sink = DataFlow::exprNode(tcf.getAnArgument()) and + TaintTracking::localTaint(DataFlow::exprNode(variableSource.getAnAccess()), sink) + ) + } + + override DataFlow::Node getAUsage() { + TaintTracking::localTaint(getASource(), result) or + DataFlow::exprNode(variableSource.getAnAccess()) = result + } + + override DataFlow::Node getASource() { + // the source is either the thing that declared + // the mutex + result = this + or + // or the thread we are using it in + result = getAThreadSource() + } + + DataFlow::Node getSink() { result = sink } + + /** + * Gets the dataflow nodes corresponding to thread local usages of the + * dependent mutex. + */ + override DataFlow::Node getAThreadSource() { + // here we line up the actual parameter at the thread creation + // site with the formal parameter in the target thread. + // Note that there are differences between the C and C++ versions + // of the argument ordering in the thread creation function. However, + // since the C version only takes one parameter (as opposed to multiple) + // we can simplify this search by considering only the first argument. + exists( + FunctionCall fc, Function f, int n // CPP Version + | + fc.getArgument(n) = sink.asExpr() and + f = fc.getArgument(0).(FunctionAccess).getTarget() and + // in C++, there is an extra argument to the `std::thread` call + // so we must subtract 1 since this is not passed to the thread. + result = DataFlow::exprNode(f.getParameter(n - 1).getAnAccess()) + ) + or + exists( + FunctionCall fc, Function f // C Version + | + fc.getAnArgument() = sink.asExpr() and + // in C, the second argument is the function + f = fc.getArgument(1).(FunctionAccess).getTarget() and + // in C, the passed argument is always the zeroth argument + result = DataFlow::exprNode(f.getParameter(0).getAnAccess()) + ) + } +} + +/** + * In the typical C model, another way to use mutexes is to work with global variables + * that can be initialized at various points -- one of which must be inside a thread. + * This class encapsulates this pattern. + */ +class DeclarationInitAccessBasedThreadDependentMutex extends ThreadDependentMutex { + Variable variableSource; + + DeclarationInitAccessBasedThreadDependentMutex() { + exists(MutexSource ms, ThreadedFunction tf, VariableAccess va | + this = DataFlow::exprNode(ms) and + // accessed as a mutex source + TaintTracking::localTaint(DataFlow::exprNode(variableSource.getAnAccess()), + DataFlow::exprNode(ms.getAnArgument())) and + // is accessed somewhere else + va = variableSource.getAnAccess() and + sink = DataFlow::exprNode(va) and + // one of which must be a thread + va.getEnclosingFunction() = tf + ) + } + + override DataFlow::Node getAUsage() { result = DataFlow::exprNode(variableSource.getAnAccess()) } +} + +module ThreadDependentMutexConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.asExpr() instanceof MutexSource } + + predicate isSink(DataFlow::Node node) { + exists(ThreadCreationFunction f | f.getAnArgument() = node.asExpr()) + } +} + +module ThreadDependentMutexFlow = TaintTracking::Global; diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ThreadSpecificStorage.qll b/cpp/common/src/codingstandards/cpp/concurrency/ThreadSpecificStorage.qll new file mode 100644 index 0000000000..aa7daf972c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ThreadSpecificStorage.qll @@ -0,0 +1,59 @@ +import cpp +private import semmle.code.cpp.dataflow.DataFlow +private import codingstandards.cpp.concurrency.ThreadCreation + +/** + * Models calls to thread specific storage function calls. + */ +abstract class ThreadSpecificStorageFunctionCall extends FunctionCall { + /** + * Gets the key to which this call references. + */ + Expr getKey() { getArgument(0) = result } +} + +/** + * Models calls to `tss_get`. + */ +class TSSGetFunctionCall extends ThreadSpecificStorageFunctionCall { + TSSGetFunctionCall() { getTarget().getName() = "tss_get" } +} + +/** + * Models calls to `tss_set`. + */ +class TSSSetFunctionCall extends ThreadSpecificStorageFunctionCall { + TSSSetFunctionCall() { getTarget().getName() = "tss_set" } +} + +/** + * Models calls to `tss_create` + */ +class TSSCreateFunctionCall extends ThreadSpecificStorageFunctionCall { + TSSCreateFunctionCall() { getTarget().getName() = "tss_create" } + + predicate hasDeallocator() { + not exists(MacroInvocation mi, NullMacro nm | + getArgument(1) = mi.getExpr() and + mi = nm.getAnInvocation() + ) + } +} + +/** + * Models calls to `tss_delete` + */ +class TSSDeleteFunctionCall extends ThreadSpecificStorageFunctionCall { + TSSDeleteFunctionCall() { getTarget().getName() = "tss_delete" } +} + +/** + * Gets a call to `DeallocationExpr` that deallocates memory owned by thread specific + * storage. + */ +predicate getAThreadSpecificStorageDeallocationCall(C11ThreadCreateCall tcc, DeallocationExpr dexp) { + exists(TSSGetFunctionCall tsg | + tcc.getFunction().getEntryPoint().getASuccessor*() = tsg and + DataFlow::localFlow(DataFlow::exprNode(tsg), DataFlow::exprNode(dexp.getFreedExpr())) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ThreadWaitDetach.qll b/cpp/common/src/codingstandards/cpp/concurrency/ThreadWaitDetach.qll new file mode 100644 index 0000000000..6898dc54df --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ThreadWaitDetach.qll @@ -0,0 +1,41 @@ +import cpp + +/** + * Models thread waiting functions. + */ +abstract class ThreadWait extends FunctionCall { } + +/** + * Models a call to a `std::thread` join. + */ +class CPPThreadWait extends ThreadWait { + VariableAccess var; + + CPPThreadWait() { + getTarget().(MemberFunction).getDeclaringType().hasQualifiedName("std", "thread") and + getTarget().getName() = "join" + } +} + +/** + * Models a call to `thrd_join` in C11. + */ +class C11ThreadWait extends ThreadWait { + VariableAccess var; + + C11ThreadWait() { getTarget().getName() = "thrd_join" } +} + +/** + * Models thread detach functions. + */ +abstract class ThreadDetach extends FunctionCall { } + +/** + * Models a call to `thrd_detach` in C11. + */ +class C11ThreadDetach extends ThreadWait { + VariableAccess var; + + C11ThreadDetach() { getTarget().getName() = "thrd_detach" } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/ThreadedFunction.qll b/cpp/common/src/codingstandards/cpp/concurrency/ThreadedFunction.qll new file mode 100644 index 0000000000..a8d2c609c5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/ThreadedFunction.qll @@ -0,0 +1,37 @@ +import cpp +private import codingstandards.cpp.concurrency.ThreadCreation + +/** + * Models a function that may be executed by some thread. + */ +abstract class ThreadedFunctionBase extends Function { + abstract Expr getSpawnExpr(); + + predicate isMultiplySpawned() { getSpawnExpr().getBasicBlock().inLoop() } +} + +final class ThreadedFunction = ThreadedFunctionBase; + +/** + * Models a function that may be executed by some thread via + * C++ standard classes. + */ +class CPPThreadedFunction extends ThreadedFunctionBase { + ThreadConstructorCall tcc; + + CPPThreadedFunction() { tcc.getFunction() = this } + + override Expr getSpawnExpr() { result = tcc } +} + +/** + * Models a function that may be executed by some thread via + * C11 standard functions. + */ +class C11ThreadedFunction extends ThreadedFunctionBase { + C11ThreadCreateCall cc; + + C11ThreadedFunction() { cc.getFunction() = this } + + override Expr getSpawnExpr() { result = cc } +} diff --git a/cpp/common/src/codingstandards/cpp/concurrency/Types.qll b/cpp/common/src/codingstandards/cpp/concurrency/Types.qll new file mode 100644 index 0000000000..3b865d5171 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/concurrency/Types.qll @@ -0,0 +1,29 @@ +import cpp + +class C11MutexType extends TypedefType { + C11MutexType() { this.hasName("mtx_t") } +} + +class C11ThreadType extends TypedefType { + C11ThreadType() { this.hasName("thrd_t") } +} + +class C11ConditionType extends TypedefType { + C11ConditionType() { this.hasName("cnd_t") } +} + +class C11ThreadStorageType extends TypedefType { + C11ThreadStorageType() { this.hasName("tss_t") } +} + +class C11ThreadingObjectType extends TypedefType { + C11ThreadingObjectType() { + this instanceof C11MutexType + or + this instanceof C11ThreadType + or + this instanceof C11ConditionType + or + this instanceof C11ThreadStorageType + } +} diff --git a/cpp/common/src/codingstandards/cpp/deadcode/UnusedFunctions.qll b/cpp/common/src/codingstandards/cpp/deadcode/UnusedFunctions.qll index b01b80208e..fdd713b436 100644 --- a/cpp/common/src/codingstandards/cpp/deadcode/UnusedFunctions.qll +++ b/cpp/common/src/codingstandards/cpp/deadcode/UnusedFunctions.qll @@ -13,6 +13,7 @@ import cpp import codingstandards.cpp.DynamicCallGraph import codingstandards.cpp.EncapsulatingFunctions import codingstandards.cpp.FunctionEquivalence +import codingstandards.cpp.Class module UnusedFunctions { /** @@ -119,7 +120,12 @@ module UnusedFunctions { class UnusedFunction extends UsableFunction { UnusedFunction() { // This function, or an equivalent function, is not reachable from any entry point - not exists(EntryPoint ep | getAnEquivalentFunction(this) = ep.getAReachableFunction()) + not exists(EntryPoint ep | getAnEquivalentFunction(this) = ep.getAReachableFunction()) and + // and it is not a constexpr. Refer issue #646. + // The usages of constexpr is not well tracked and hence + // to avoid false positives, this is added. In case there is an improvement in + // handling constexpr in CodeQL, we can consider removing it. + not this.isConstexpr() } string getDeadCodeType() { @@ -128,4 +134,7 @@ module UnusedFunctions { else result = "never called." } } + + /** A `SpecialMemberFunction` which is an `UnusedFunction`. */ + class UnusedSplMemberFunction extends UnusedFunction, SpecialMemberFunction { } } diff --git a/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll b/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll new file mode 100644 index 0000000000..94ae16ec4f --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll @@ -0,0 +1,71 @@ +import cpp +import codingstandards.cpp.deadcode.UnusedVariables +import codingstandards.cpp.alertreporting.HoldsForAllCopies +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +/** + * An unused object definition is an object, meaning a place in memory, whose definition could be + * removed and the program would still compile. + * + * Technically, parameters may be considered objects, but they are covered by their own rule. + * Similarly, members of structs are an addressable place in memory, and may be considered objects. + * However, the member declaration is nothing but a layout offset, which is not an object. + * + * This therefore only reports variables (local or top level) which have a definition, and are + * unused. + */ +class UnusedObjectDefinition extends VariableDeclarationEntry { + UnusedObjectDefinition() { + ( + getVariable() instanceof BasePotentiallyUnusedLocalVariable + or + getVariable() instanceof BasePotentiallyUnusedGlobalOrNamespaceVariable + ) and + not exists(VariableAccess access | access.getTarget() = getVariable()) and + getVariable().getDefinition() = this + } + + /* Dead objects with these attributes are reported in the "strict" queries. */ + predicate hasAttrUnused() { + getVariable().getAnAttribute().hasName(["unused", "used", "maybe_unused", "cleanup"]) + } +} + +/* Configuration to use the `DedupMacroResults` module to reduce alert noise */ +module UnusedObjectDefinitionDedupeConfig implements + DeduplicateMacroConfigSig +{ + string describe(UnusedObjectDefinition def) { result = def.getName() } +} + +import DeduplicateMacroResults as DeduplicateUnusedMacroObjects + +/* Module config to use the `DeduplicateUnusedMacroObjects::Report` module */ +module ReportDeadObjectConfig implements MacroReportConfigSig { + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = "Macro '" + m.getName() + "' defines unused object '" + description + "'." + } + + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Macro '" + m.getName() + + "' defines unused object with an invocation-dependent name, for example, '$@'." + } + + string getMessageResultInIsolatedExpansion(UnusedObjectDefinition unused) { + result = "Invocation of macro '$@' defines unused object '" + unused.getName() + "'." + } + + string getMessageNotInMacro(UnusedObjectDefinition unused, Locatable optLoc1, string optStr1) { + result = "Unused object '" + unused.getName() + "'." and + optLoc1 = unused and + optStr1 = "(ignored)" + } +} + +/* The object to report in queries of dead objects used in macros */ +class ReportDeadObject extends DeduplicateUnusedMacroObjects::Report::ReportResult +{ + predicate hasAttrUnused() { getAResultElement().hasAttrUnused() } +} diff --git a/cpp/common/src/codingstandards/cpp/deadcode/UnusedVariables.qll b/cpp/common/src/codingstandards/cpp/deadcode/UnusedVariables.qll index 3437051cac..a7accd5252 100644 --- a/cpp/common/src/codingstandards/cpp/deadcode/UnusedVariables.qll +++ b/cpp/common/src/codingstandards/cpp/deadcode/UnusedVariables.qll @@ -1,5 +1,6 @@ import cpp import codingstandards.cpp.FunctionEquivalence +import codingstandards.cpp.Scope /** * A type that contains a template parameter type (doesn't count pointers or references). @@ -35,11 +36,11 @@ predicate declarationHasSideEffects(Variable v) { v.getType() instanceof TemplateDependentType } -/** A `LocalVariable` which is a candidate for being unused. */ -class PotentiallyUnusedLocalVariable extends LocalVariable { - PotentiallyUnusedLocalVariable() { - // Ignore variables declared in macro expansions - not exists(DeclStmt ds | ds.getADeclaration() = this and ds.isInMacroExpansion()) and +/** + * A `LocalVariable` which is a candidate for being unused, and may or may not be defined in a macro. + */ +class BasePotentiallyUnusedLocalVariable extends LocalVariable { + BasePotentiallyUnusedLocalVariable() { // Ignore variables where initializing the variable has side effects not declarationHasSideEffects(this) and // TODO non POD types with initializers? Also, do something different with templates? exists(Function f | f = getFunction() | @@ -47,7 +48,21 @@ class PotentiallyUnusedLocalVariable extends LocalVariable { not exists(AsmStmt s | f = s.getEnclosingFunction()) and // Ignore functions with error expressions as they indicate expressions that the extractor couldn't process not any(ErrorExpr e).getEnclosingFunction() = f - ) + ) and + // exclude uninstantiated template members + not this.isFromUninstantiatedTemplate(_) and + // Do not report compiler generated variables + not this.isCompilerGenerated() + } +} + +/** + * A `LocalVariable` which is a candidate for being unused, and not defined in a macro. + */ +class PotentiallyUnusedLocalVariable extends BasePotentiallyUnusedLocalVariable { + PotentiallyUnusedLocalVariable() { + // Ignore variables declared in macro expansions + not exists(DeclStmt ds | ds.getADeclaration() = this and ds.isInMacroExpansion()) } } @@ -92,19 +107,36 @@ class PotentiallyUnusedMemberVariable extends MemberVariable { // Must be in a fully defined class, otherwise one of the undefined functions may use the variable getDeclaringType() instanceof FullyDefinedClass and // Lambda captures are not "real" member variables - it's an implementation detail that they are represented that way - not this = any(LambdaCapture lc).getField() + not this = any(LambdaCapture lc).getField() and + // exclude uninstantiated template members + not this.isFromUninstantiatedTemplate(_) and + // Do not report compiler generated variables + not this.isCompilerGenerated() } } -/** A `GlobalOrNamespaceVariable` which is potentially unused. */ -class PotentiallyUnusedGlobalOrNamespaceVariable extends GlobalOrNamespaceVariable { - PotentiallyUnusedGlobalOrNamespaceVariable() { +/** A `GlobalOrNamespaceVariable` which is potentially unused and may or may not be defined in a macro */ +class BasePotentiallyUnusedGlobalOrNamespaceVariable extends GlobalOrNamespaceVariable { + BasePotentiallyUnusedGlobalOrNamespaceVariable() { // A non-defined variable may never be used hasDefinition() and - // Not declared in a macro expansion - not isInMacroExpansion() and // No side-effects from declaration - not declarationHasSideEffects(this) + not declarationHasSideEffects(this) and + // exclude uninstantiated template members + not this.isFromUninstantiatedTemplate(_) and + // Do not report compiler generated variables + not this.isCompilerGenerated() + } +} + +/** + * A `GlobalOrNamespaceVariable` which is potentially unused, and is not defined in a macro. + */ +class PotentiallyUnusedGlobalOrNamespaceVariable extends BasePotentiallyUnusedGlobalOrNamespaceVariable +{ + PotentiallyUnusedGlobalOrNamespaceVariable() { + // Not declared in a macro expansion + not isInMacroExpansion() } } @@ -119,3 +151,39 @@ class UserProvidedConstructorFieldInit extends ConstructorFieldInit { not getEnclosingFunction().isCompilerGenerated() } } + +/** + * Holds if `v` may hold a compile time value and is accessible to a template instantiation that + * receives a constant value as an argument equal to the value of `v`. + */ +predicate maybeACompileTimeTemplateArgument(Variable v) { + v.isConstexpr() and + exists(ClassTemplateInstantiation cti, TranslationUnit tu | + cti.getATemplateArgument().(Expr).getValue() = v.getInitializer().getExpr().getValue() and + ( + cti.getFile() = tu and + ( + v.getADeclarationEntry().getFile() = tu or + tu.getATransitivelyIncludedFile() = v.getADeclarationEntry().getFile() + ) + ) + ) +} + +/** Gets the constant value of a constexpr/const variable. */ +string getConstExprValue(Variable v) { + result = v.getInitializer().getExpr().getValue() and + (v.isConst() or v.isConstexpr()) +} + +/** + * Counts uses of `Variable` v in a local array of size `n` + */ +int countUsesInLocalArraySize(Variable v) { + result = + count(ArrayType at, LocalVariable arrayVariable | + arrayVariable.getType().resolveTypedefs() = at and + v.(PotentiallyUnusedLocalVariable).getFunction() = arrayVariable.getFunction() and + at.getArraySize().toString() = getConstExprValue(v) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/deadcode/UselessAssignments.qll b/cpp/common/src/codingstandards/cpp/deadcode/UselessAssignments.qll index e326f814be..031ad2aa7c 100644 --- a/cpp/common/src/codingstandards/cpp/deadcode/UselessAssignments.qll +++ b/cpp/common/src/codingstandards/cpp/deadcode/UselessAssignments.qll @@ -3,6 +3,7 @@ */ import cpp +import codingstandards.cpp.deadcode.UnusedVariables import codingstandards.cpp.enhancements.ControlFlowGraphEnhancements /** If a variable may escape from the local context */ @@ -43,7 +44,13 @@ class InterestingStackVariable extends StackVariable { // A reference parameter can have an affect outside the enclosing function not mayEscape(this) and // Not a loop control variable, explicitly excluded - not this instanceof LoopControlVariable + not this instanceof LoopControlVariable and + // Ignore variables in uninstantiated templates + not this.isFromUninstantiatedTemplate(_) and + // Ignore compiler generated variables, such as those generated for range based for loops + not this.isCompilerGenerated() and + // Explicitly ignore (propagated) constants that may be used to define sizes of local arrays + not countUsesInLocalArraySize(this) > 0 } } diff --git a/cpp/common/src/codingstandards/cpp/deviations/CodeIdentifierDeviation.qll b/cpp/common/src/codingstandards/cpp/deviations/CodeIdentifierDeviation.qll new file mode 100644 index 0000000000..00e02d5712 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/deviations/CodeIdentifierDeviation.qll @@ -0,0 +1,428 @@ +/** + * A module for identifying in code markers in code that trigger deviations. + * + * This module supports two different code identifier markers: + * - A C/C++ attribute based syntax + * - A comment-based format + * + * The C/C++ attribute based syntax uses the following format: + * ``` + * [[codeql::_deviation("code-identifier")]] + * ``` + * The deviation will be applied to the selected program element, and any syntactically nested children of that program element. + * + * For the comment format the marker consists of a `code-identifier` with some optional annotations. A deviation will be applied to + * some range of lines in the file containing the comment based on the annotation. The supported marker annotation + * formats are: + * - `` - the deviation applies to results on the current line. + * - `codeql::_deviation()` - same as above. + * - `codeql::_deviation_next_line()` - this deviation applies to results on the next line. + * - `codeql::_deviation_begin()` - marks the beginning of a range of lines where the deviation applies. + * - `codeql::_deviation_end()` - marks the end of a range of lines where the deviation applies. + * + * The valid `code-identifier`s are specified in deviation records, which also specify the query whose results are + * suppressed by the deviation. + * + * For begin/end, we maintain a stack of begin markers. When we encounter an end marker, we pop the stack to determine + * the range of that begin/end marker. If the stack is empty, the end marker is considered unmatched and invalid. If + * the stack is non-empty at the end of the file, all the begin markers are considered unmatched and invalid. + * + * Begin/end markers are not valid across include boundaries, as the stack is not maintained across files. + */ + +import cpp +import Deviations +import codingstandards.cpp.Locations + +string supportedStandard() { result = ["misra", "autosar", "cert"] } + +/** + * Holds if the given comment contains the code identifier. + */ +bindingset[codeIdentifier] +private predicate commentMatches(Comment comment, string codeIdentifier) { + exists(string text | + comment instanceof CppStyleComment and + // strip the beginning slashes + text = comment.getContents().suffix(2).trim() + or + comment instanceof CStyleComment and + // strip both the beginning /* and the end */ the comment + exists(string text0 | + text0 = comment.getContents().suffix(2) and + text = text0.prefix(text0.length() - 2).trim() + ) and + // The /* */ comment must be a single-line comment + not text.matches("%\n%") + | + // Code identifier appears at the start of the comment (modulo whitespace) + text.prefix(codeIdentifier.length()) = codeIdentifier + or + // Code identifier appears at the end of the comment (modulo whitespace) + text.suffix(text.length() - codeIdentifier.length()) = codeIdentifier + ) +} + +/** + * A deviation marker in the code. + */ +abstract class CommentDeviationMarker extends Comment { + DeviationRecord record; + + /** + * Gets the deviation record associated with this deviation marker. + */ + DeviationRecord getRecord() { result = record } +} + +/** + * A deviation marker in a comment that is not a valid deviation marker. + */ +class InvalidCommentDeviationMarker extends Comment { + InvalidCommentDeviationMarker() { + not this instanceof CommentDeviationMarker and + commentMatches(this, "codeql::" + supportedStandard() + "_deviation") + } +} + +/** + * A deviation marker for a deviation that applies to the current line. + */ +class DeviationEndOfLineMarker extends CommentDeviationMarker { + DeviationEndOfLineMarker() { + commentMatches(this, + "codeql::" + supportedStandard() + "_deviation(" + record.getCodeIdentifier() + ")") + } +} + +/** + * A deviation marker for a deviation that applies to the next line. + */ +class DeviationNextLineMarker extends CommentDeviationMarker { + DeviationNextLineMarker() { + commentMatches(this, + "codeql::" + supportedStandard() + "_deviation_next_line(" + record.getCodeIdentifier() + ")") + } +} + +/** + * A deviation marker for a deviation that applies to a range of lines + */ +abstract class CommentDeviationRangeMarker extends CommentDeviationMarker { } + +/** + * A deviation marker for a deviation that begins on this line. + */ +class DeviationBegin extends CommentDeviationRangeMarker { + DeviationBegin() { + commentMatches(this, + "codeql::" + supportedStandard() + "_deviation_begin(" + record.getCodeIdentifier() + ")") + } +} + +/** + * A deviation marker for a deviation that ends on this line. + */ +class DeviationEnd extends CommentDeviationRangeMarker { + DeviationEnd() { + commentMatches(this, + "codeql::" + supportedStandard() + "_deviation_end(" + record.getCodeIdentifier() + ")") + } +} + +private predicate hasDeviationCommentFileOrdering( + DeviationRecord record, CommentDeviationRangeMarker comment, File file, int index +) { + comment = + rank[index](CommentDeviationRangeMarker c | + c.getRecord() = record and + file = c.getFile() + | + c order by c.getLocation().getStartLine(), c.getLocation().getStartColumn() + ) +} + +/** + * Calculate the stack of deviation begin markers related to the given deviation record, in the given file, + * at the given `markerRecordFileIndex` into the list of deviation markers for that record in that file. + */ +private BeginStack calculateBeginStack(DeviationRecord record, File file, int markerRecordFileIndex) { + // Stack is empty at the start + markerRecordFileIndex = 0 and + result = TEmptyBeginStack() and + // Only initialize when there is at least one such comment marker for this file and record + // pairing + exists(CommentDeviationRangeMarker marker | + marker.getRecord() = record and marker.getLocation().getFile() = file + ) + or + // Next token is begin, so push it to the stack + exists(DeviationBegin begin, BeginStack prev | + record = begin.getRecord() and + hasDeviationCommentFileOrdering(record, begin, file, markerRecordFileIndex) and + prev = calculateBeginStack(record, file, markerRecordFileIndex - 1) and + result = TConsBeginStack(begin, prev) + ) + or + // Next token is end + exists(DeviationEnd end, BeginStack prevStack | + record = end.getRecord() and + hasDeviationCommentFileOrdering(record, end, file, markerRecordFileIndex) and + prevStack = calculateBeginStack(record, file, markerRecordFileIndex - 1) + | + // There is, so pop the most recent begin off the stack + prevStack = TConsBeginStack(_, result) + or + // Error, no begin on the stack, ignore the end and continue + prevStack = TEmptyBeginStack() and + result = TEmptyBeginStack() + ) +} + +newtype TBeginStack = + TConsBeginStack(DeviationBegin begin, TBeginStack prev) { + exists(File file, int index | + hasDeviationCommentFileOrdering(begin.getRecord(), begin, file, index) and + prev = calculateBeginStack(begin.getRecord(), file, index - 1) + ) + } or + TEmptyBeginStack() + +/** + * A stack of begin markers that occur in the same file, referring to the same record. + */ +private class BeginStack extends TBeginStack { + /** Gets the top begin marker on the stack. */ + DeviationBegin peek() { this = TConsBeginStack(result, _) } + + string toString() { + exists(DeviationBegin begin, BeginStack prev | this = TConsBeginStack(begin, prev) | + result = "(" + begin + ", " + prev.toString() + ")" + ) + or + this = TEmptyBeginStack() and + result = "()" + } +} + +predicate isDeviationRangePaired(DeviationRecord record, DeviationBegin begin, DeviationEnd end) { + exists(File file, int index | + record = end.getRecord() and + hasDeviationCommentFileOrdering(record, end, file, index) and + begin = calculateBeginStack(record, file, index - 1).peek() + ) +} + +/** + * A standard attribute that either deviates a result. + */ +class DeviationAttribute extends StdAttribute { + DeviationRecord record; + + DeviationAttribute() { + this.hasQualifiedName("codeql", supportedStandard() + "_deviation") and + // Support multiple argument deviations + "\"" + record.getCodeIdentifier() + "\"" = this.getAnArgument().getValueText() + } + + DeviationRecord getADeviationRecord() { result = record } + + /** Gets the element to which this attribute was applied. */ + Element getPrimarySuppressedElement() { + result.(Type).getAnAttribute() = this + or + result.(Stmt).getAnAttribute() = this + or + result.(Variable).getAnAttribute() = this + or + result.(Function).getAnAttribute() = this + } + + pragma[nomagic] + Element getASuppressedElement() { + result = this.getPrimarySuppressedElement() + or + result.(Expr).getEnclosingStmt() = this.getASuppressedElement() + or + result.(Stmt).getParentStmt() = this.getASuppressedElement() + or + result.(Stmt).getEnclosingFunction() = this.getASuppressedElement() + or + result.(LocalVariable) = this.getASuppressedElement().(DeclStmt).getADeclaration() + or + result.(Function).getDeclaringType() = this.getASuppressedElement() + or + result.(Variable).getDeclaringType() = this.getASuppressedElement() + or + exists(LambdaExpression expr | + expr = this.getASuppressedElement() and + result = expr.getLambdaFunction() + ) + or + exists(Function f | + f = this.getASuppressedElement() and + // A suppression on the function should apply to the noexcept expression + result = f.getADeclarationEntry().getNoExceptExpr() + ) + } +} + +/** + * A deviation attribute that is not associated with any deviation record. + */ +class InvalidDeviationAttribute extends StdAttribute { + string unknownCodeIdentifier; + + InvalidDeviationAttribute() { + this.hasQualifiedName("codeql", supportedStandard() + "_deviation") and + "\"" + unknownCodeIdentifier + "\"" = this.getAnArgument().getValueText() and + not exists(DeviationRecord record | record.getCodeIdentifier() = unknownCodeIdentifier) + } + + string getAnUnknownCodeIdentifier() { result = unknownCodeIdentifier } +} + +newtype TCodeIndentifierDeviation = + TSingleLineDeviation(DeviationRecord record, Comment comment, string filepath, int suppressedLine) { + ( + commentMatches(comment, record.getCodeIdentifier()) or + comment.(DeviationEndOfLineMarker).getRecord() = record + ) and + comment.getLocation().hasLocationInfo(filepath, suppressedLine, _, _, _) + or + comment.(DeviationNextLineMarker).getRecord() = record and + comment.getLocation().hasLocationInfo(filepath, suppressedLine - 1, _, _, _) + } or + TMultiLineDeviation( + DeviationRecord record, DeviationBegin beginComment, DeviationEnd endComment, string filepath, + int suppressedStartLine, int suppressedStartColumn, int suppressedEndLine, + int suppressedEndColumn + ) { + isDeviationRangePaired(record, beginComment, endComment) and + beginComment + .getLocation() + .hasLocationInfo(filepath, suppressedStartLine, suppressedStartColumn, _, _) and + endComment.getLocation().hasLocationInfo(filepath, _, _, suppressedEndLine, suppressedEndColumn) + } or + TCodeIdentifierDeviation(DeviationRecord record, DeviationAttribute attribute) { + attribute.getADeviationRecord() = record + } + +class CodeIdentifierDeviation extends TCodeIndentifierDeviation { + /** The deviation record associated with the deviation comment. */ + DeviationRecord getADeviationRecord() { + this = TSingleLineDeviation(result, _, _, _) + or + this = TMultiLineDeviation(result, _, _, _, _, _, _, _) + or + this = TCodeIdentifierDeviation(result, _) + } + + /** + * Holds if the given element is matched by this code identifier deviation. + */ + bindingset[e] + pragma[inline_late] + predicate isElementMatching(Element e) { + exists(string filepath, int elementLocationStart, int elementLocationColumnStart | + e.getLocation() + .hasLocationInfo(filepath, elementLocationStart, elementLocationColumnStart, _, _) + | + exists(int suppressedLine | + this = TSingleLineDeviation(_, _, filepath, suppressedLine) and + suppressedLine = elementLocationStart + ) + or + exists( + int suppressedStartLine, int suppressedStartColumn, int suppressedEndLine, + int suppressedEndColumn + | + this = + TMultiLineDeviation(_, _, _, filepath, suppressedStartLine, suppressedStartColumn, + suppressedEndLine, suppressedEndColumn) and + ( + // Element starts on a line after the begin marker of the suppression + suppressedStartLine < elementLocationStart + or + // Element exists on the same line as the begin marker, and occurs after it + suppressedStartLine = elementLocationStart and + suppressedStartColumn < elementLocationColumnStart + ) and + ( + // Element starts on a line before the end marker of the suppression + suppressedEndLine > elementLocationStart + or + // Element exists on the same line as the end marker of the suppression, and occurs before it + suppressedEndLine = elementLocationStart and + elementLocationColumnStart < suppressedEndColumn + ) + ) + ) + or + exists(DeviationAttribute attribute | + this = TCodeIdentifierDeviation(_, attribute) and + attribute.getASuppressedElement() = e + ) + } + + /** + * Holds for the region matched by this code identifier deviation. + * + * Note: this is not the location of the marker itself. + */ + predicate hasLocationInfo( + string filepath, int suppressedLine, int suppressedColumn, int endline, int endcolumn + ) { + exists(Comment commentMarker | + this = TSingleLineDeviation(_, commentMarker, filepath, suppressedLine) and + suppressedColumn = 1 and + endline = suppressedLine + | + if commentMarker instanceof DeviationEndOfLineMarker + then endcolumn = commentMarker.(DeviationEndOfLineMarker).getLocation().getEndColumn() + else + // Find the last column for a location on the next line + endcolumn = getLastColumnNumber(filepath, suppressedLine) + ) + or + this = + TMultiLineDeviation(_, _, _, filepath, suppressedLine, suppressedColumn, endline, endcolumn) + or + exists(DeviationAttribute attribute | + this = TCodeIdentifierDeviation(_, attribute) and + attribute + .getPrimarySuppressedElement() + .getLocation() + .hasLocationInfo(filepath, suppressedLine, suppressedColumn, endline, endcolumn) + ) + } + + string toString() { + exists(string filepath | + exists(int suppressedLine | + this = TSingleLineDeviation(_, _, filepath, suppressedLine) and + result = + "Deviation of " + getADeviationRecord().getQuery() + " applied to " + filepath + " Line " + + suppressedLine + ) + or + exists( + int suppressedStartLine, int suppressedStartColumn, int suppressedEndLine, + int suppressedEndColumn + | + this = + TMultiLineDeviation(_, _, _, filepath, suppressedStartLine, suppressedStartColumn, + suppressedEndLine, suppressedEndColumn) and + result = + "Deviation of " + getADeviationRecord().getQuery() + " applied to " + filepath + " Line " + + suppressedStartLine + ":" + suppressedStartColumn + ":" + suppressedEndLine + ":" + + suppressedEndColumn + ) + ) + or + exists(DeviationAttribute attribute | + this = TCodeIdentifierDeviation(_, attribute) and + result = "Deviation of " + getADeviationRecord().getQuery() + " applied to " + attribute + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/deviations/Deviations.qll b/cpp/common/src/codingstandards/cpp/deviations/Deviations.qll index dbc6a240cd..e8c030cdd4 100644 --- a/cpp/common/src/codingstandards/cpp/deviations/Deviations.qll +++ b/cpp/common/src/codingstandards/cpp/deviations/Deviations.qll @@ -7,6 +7,8 @@ import cpp import semmle.code.cpp.XML import codingstandards.cpp.exclusions.RuleMetadata +import codingstandards.cpp.Config +import CodeIdentifierDeviation predicate applyDeviationsAtQueryLevel() { not exists(CodingStandardsReportDeviatedAlerts reportDeviatedResults | @@ -15,28 +17,8 @@ predicate applyDeviationsAtQueryLevel() { ) } -/** A `coding-standards.xml` configuration file (usually generated from an YAML configuration file). */ -class CodingStandardsFile extends XMLFile { - CodingStandardsFile() { - this.getBaseName() = "coding-standards.xml" and - // Must be within the users source code. - exists(this.getRelativePath()) - } -} - -/** A "Coding Standards" configuration file */ -class CodingStandardsConfig extends XMLElement { - CodingStandardsConfig() { - any(CodingStandardsFile csf).getARootElement() = this and - this.getName() = "codingstandards" - } - - /** Gets a deviation record for this configuration. */ - DeviationRecord getADeviationRecord() { result = getAChild().(DeviationRecords).getAChild() } -} - /** An element which tells the analysis whether to report deviated results. */ -class CodingStandardsReportDeviatedAlerts extends XMLElement { +class CodingStandardsReportDeviatedAlerts extends XmlElement { CodingStandardsReportDeviatedAlerts() { getParent() instanceof CodingStandardsConfig and hasName("report-deviated-alerts") @@ -44,23 +26,17 @@ class CodingStandardsReportDeviatedAlerts extends XMLElement { } /** A container of deviation records. */ -class DeviationRecords extends XMLElement { - DeviationRecords() { - getParent() instanceof CodingStandardsConfig and - hasName("deviations") - } +class DeviationRecords extends CodingStandardsConfigSection { + DeviationRecords() { hasName("deviations") } } /** A container for the deviation permits records. */ -class DeviationPermits extends XMLElement { - DeviationPermits() { - getParent() instanceof CodingStandardsConfig and - hasName("deviation-permits") - } +class DeviationPermits extends CodingStandardsConfigSection { + DeviationPermits() { hasName("deviation-permits") } } /** A deviation permit record, that is specified by a permit identifier */ -class DeviationPermit extends XMLElement { +class DeviationPermit extends XmlElement { DeviationPermit() { getParent() instanceof DeviationPermits and hasName("deviation-permits-entry") @@ -143,7 +119,7 @@ class DeviationPermit extends XMLElement { } /** A deviation record, that is a specified rule or query */ -class DeviationRecord extends XMLElement { +class DeviationRecord extends XmlElement { DeviationRecord() { getParent() instanceof DeviationRecords and hasName("deviations-entry") @@ -159,13 +135,13 @@ class DeviationRecord extends XMLElement { private string getRawPermitId() { result = getAChild("permit-id").getTextValue() } - private XMLElement getRawRaisedBy() { result = getAChild("raised-by") } + private XmlElement getRawRaisedBy() { result = getAChild("raised-by") } private string getRawRaisedByName() { result = getRawRaisedBy().getAChild("name").getTextValue() } private string getRawRaisedByDate() { result = getRawRaisedBy().getAChild("date").getTextValue() } - private XMLElement getRawApprovedBy() { result = getAChild("approved-by") } + private XmlElement getRawApprovedBy() { result = getAChild("approved-by") } private string getRawApprovedByName() { result = getRawApprovedBy().getAChild("name").getTextValue() @@ -244,32 +220,8 @@ class DeviationRecord extends XMLElement { else result = getADeviationPermit().getCodeIdentifier() } - /** Gets a comment which starts or ends with the code identifier comment. */ - Comment getACodeIdentifierComment() { - exists(string text | - ( - result instanceof CppStyleComment and - // strip the beginning slashes - text = result.getContents().suffix(2).trim() - or - result instanceof CStyleComment and - // strip both the beginning /* and the end */ the comment - exists(string text0 | - text0 = result.getContents().suffix(2) and - text = text0.prefix(text0.length() - 2).trim() - ) and - // The /* */ comment must be a single-line comment - not text.matches("%\n%") - ) and - ( - // Code identifier appears at the start of the comment (modulo whitespace) - text.prefix(getCodeIdentifier().length()) = getCodeIdentifier() - or - // Code identifier appears at the end of the comment (modulo whitespace) - text.suffix(text.length() - getCodeIdentifier().length()) = getCodeIdentifier() - ) - ) - } + /** Gets a code identifier deviation in code which starts or ends with the code identifier comment. */ + CodeIdentifierDeviation getACodeIdentifierDeviation() { this = result.getADeviationRecord() } /** Gets the `rule-id` specified for this record, if any. */ private string getRawRuleId() { result = getAChild("rule-id").getTextValue() } @@ -357,6 +309,13 @@ class DeviationRecord extends XMLElement { hasPermitId() and not hasADeviationPermit() and result = "There is no deviation permit with id `" + getPermitId() + "`." + or + exists(Query q | q.getQueryId() = getQueryId() | + not q.getEffectiveCategory().permitsDeviation() and + result = + "The deviation is applied to a query with the rule category '" + + q.getEffectiveCategory().toString() + "' that does not permit a deviation." + ) } /** Holds if the deviation record is valid */ @@ -381,25 +340,30 @@ class DeviationRecord extends XMLElement { result.getRelativePath() = getAChild("paths").getAChild("paths-entry").getTextValue() } + private string getADeviationPath0() { + if exists(getPathAContainer()) + then + // Use the path, which will be relative to this file, if specified + result = getPathAContainer().getRelativePath() + else ( + // Otherwise, if no code identifier was supplied, it applies to the parent container of the + // file itself + not exists(getCodeIdentifier()) and + result = this.getFile().getParentContainer().getRelativePath() + ) + } + /** Gets a path to which this deviation applies. */ string getADeviationPath() { - ( - if exists(getPathAContainer()) - then - // Use the path, which will be relative to this file, if specified - result = getPathAContainer().getRelativePath() - else ( - // Otherwise, if no code identifier was supplied, it applies to the parent container of the - // file itself - not exists(getCodeIdentifier()) and - result = this.getFile().getParentContainer().getRelativePath() - ) + exists(string res | + res = getADeviationPath0() and + if res = "" then result = "(root)" else result = res ) } cached predicate isDeviated(Query query, string deviationPath) { query = getQuery() and - deviationPath = getADeviationPath() + deviationPath = getADeviationPath0() } } diff --git a/cpp/common/src/codingstandards/cpp/deviations/DeviationsSuppression.ql b/cpp/common/src/codingstandards/cpp/deviations/DeviationsSuppression.ql index 9035b7d288..94f45c74b3 100644 --- a/cpp/common/src/codingstandards/cpp/deviations/DeviationsSuppression.ql +++ b/cpp/common/src/codingstandards/cpp/deviations/DeviationsSuppression.ql @@ -7,29 +7,7 @@ import cpp import Deviations - -/** Holds if `lineNumber` is an indexed line number in file `f`. */ -private predicate isLineNumber(File f, int lineNumber) { - exists(Location l | l.getFile() = f | - l.getStartLine() = lineNumber - or - l.getEndLine() = lineNumber - ) -} - -/** Gets the last line number in `f`. */ -private int getLastLineNumber(File f) { result = max(int lineNumber | isLineNumber(f, lineNumber)) } - -/** Gets the last column number on the last line of `f`. */ -int getLastColumnNumber(File f) { - result = - max(Location l | - l.getFile() = f and - l.getEndLine() = getLastLineNumber(f) - | - l.getEndColumn() - ) -} +import codingstandards.cpp.Locations newtype TDeviationScope = TDeviationRecordFileScope(DeviationRecord dr, File file) { @@ -38,7 +16,9 @@ newtype TDeviationScope = file.getRelativePath().prefix(deviationPath.length()) = deviationPath ) } or - TDeviationRecordCommentScope(DeviationRecord dr, Comment c) { c = dr.getACodeIdentifierComment() } + TDeviationRecordCodeIdentiferDeviationScope(DeviationRecord dr, CodeIdentifierDeviation c) { + c = dr.getACodeIdentifierDeviation() + } /** A deviation scope. */ class DeviationScope extends TDeviationScope { @@ -69,10 +49,9 @@ class DeviationRecordFileScope extends DeviationScope, TDeviationRecordFileScope string filepath, int startline, int startcolumn, int endline, int endcolumn ) { // In an ideal world, we would produce a URL here that informed the AlertSuppression code that - // the whole file was suppressed. However, experimentation suggestions the alert suppression - // code only works with locations with lines and columns, so we generate a location that covers - // the whole "indexed" file, by finding the location indexed in the database with the latest - // line and column number. + // the whole file was suppressed. However, the alert suppression code only works with locations + // with lines and columns, so we generate a location that covers the whole "indexed" file, by + // finding the location indexed in the database with the latest line and column number. exists(File f | f = getFile() | f.getLocation().hasLocationInfo(filepath, _, _, _, _) and startline = 1 and @@ -91,10 +70,16 @@ class DeviationRecordFileScope extends DeviationScope, TDeviationRecordFileScope * A deviation scope derived from a comment corresponding to a "code-identifier" entry for a * `DeviationRecord`. */ -class DeviationRecordCommentScope extends DeviationScope, TDeviationRecordCommentScope { - private DeviationRecord getDeviationRecord() { this = TDeviationRecordCommentScope(result, _) } +class DeviationRecordCommentScope extends DeviationScope, + TDeviationRecordCodeIdentiferDeviationScope +{ + private DeviationRecord getDeviationRecord() { + this = TDeviationRecordCodeIdentiferDeviationScope(result, _) + } - private Comment getComment() { this = TDeviationRecordCommentScope(_, result) } + private CodeIdentifierDeviation getCodeIdentifierDeviation() { + this = TDeviationRecordCodeIdentiferDeviationScope(_, result) + } override Locatable getDeviationDefinitionLocation() { result = getDeviationRecord() } @@ -103,14 +88,11 @@ class DeviationRecordCommentScope extends DeviationScope, TDeviationRecordCommen override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - getComment().getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and - startcolumn = 1 + getCodeIdentifierDeviation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } - override string toString() { - result = - "Deviation of " + getDeviationRecord().getQuery() + " for comment " + getComment() + "." - } + override string toString() { result = getCodeIdentifierDeviation().toString() } } from DeviationScope deviationScope diff --git a/cpp/common/src/codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.md b/cpp/common/src/codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.md new file mode 100644 index 0000000000..364e1ae915 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.md @@ -0,0 +1,19 @@ +# Invalid deviation code identifier + +## Overview + +Invalid deviation markers in code have no effect on the results but may indicate confusion over which results will be suppressed. + +Deviation code markers are used to suppress CodeQL Coding Standards results, following the process specified in the "MISRA Compliance 2020" document. There are a range of different deviation markers, with specific syntactic requirements. If those syntactic requirements are not met, the marker is invalid and will not be applied, which is likely contrary to developer expectations. + +## Recommendation + +Ensure the following requirements are met: + + * All `codeql::_deviation_begin(..)` markers are paired with a matching `codeql::_deviation_end(..)` marker. + * All instances of `codeql::_deviation` in comments are correctly formatted comment markers, and reference a `code-identifier`s that is specified in a deviation record included in the analysis. + * All deviation attributes reference `code-identifier`s that are specified in a deviation record included in the analysis. + +## References + +* [MISRA Compliance 2020 document - Chapter 4.2 (page 12) - Deviations](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.ql b/cpp/common/src/codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.ql new file mode 100644 index 0000000000..87dafbba13 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.ql @@ -0,0 +1,41 @@ +/** + * @id cpp/coding-standards/invalid-deviation-code-identifiers + * @name Invalid deviation code identifiers + * @description Deviation code identifiers must be valid. + * @kind problem + * @problem.severity error + */ + +import cpp +import CodeIdentifierDeviation + +predicate deviationCodeIdentifierError(Element e, string message) { + exists(DeviationEnd end | + e = end and + not isDeviationRangePaired(_, _, end) and + message = "Deviation end block is unmatched." + ) + or + exists(DeviationBegin begin | + e = begin and + not isDeviationRangePaired(_, begin, _) and + message = "Deviation start block is unmatched." + ) + or + exists(InvalidDeviationAttribute b | + e = b and + message = + "Deviation attribute references unknown code identifier " + b.getAnUnknownCodeIdentifier() + + "." + ) + or + exists(InvalidCommentDeviationMarker m | + e = m and + message = + "Deviation marker does not match an expected format, or references an unknown code identifier." + ) +} + +from Element e, string message +where deviationCodeIdentifierError(e, message) +select e, message diff --git a/cpp/common/src/codingstandards/cpp/dominance/BehavioralSet.qll b/cpp/common/src/codingstandards/cpp/dominance/BehavioralSet.qll new file mode 100644 index 0000000000..8609e3213b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/dominance/BehavioralSet.qll @@ -0,0 +1,40 @@ +import cpp +import semmle.code.cpp.controlflow.ControlFlowGraph + +signature class TargetNode extends ControlFlowNode; + +signature module DominatingSetConfigSig { + predicate isTargetBehavior(ControlFlowNode behavior, Target target); + + default predicate isBlockingBehavior(ControlFlowNode behavior, Target target) { none() } +} + +/** + * A module to find whether there exists a dominator set for a node which performs a relevant + * behavior. + * + * For instance, we may wish to see that all paths leading to an `abort()` statement include a + * logging call. In this case, the `abort()` statement is the `Target` node, and the config module + * predicate `isTargetBehavior` logging statements. + * + * Additionally, the config may specify `isBlockingBehavior` to prevent searching too far for the + * relevant behavior. For instance, if analyzing that all paths to an `fflush()` call are preceded + * by a write, we should ignore paths from write operations that have already been flushed through + * an intermediary `fflush()` call. + */ +module DominatingBehavioralSet Config> { + /** + * Holds if this search step can reach the entry or a blocking node, without passing through a + * target behavior, indicating that the target is has no relevant dominator set. + */ + private predicate searchStep(ControlFlowNode node, Target target) { + Config::isBlockingBehavior(node, target) + or + not Config::isTargetBehavior(node, target) and + exists(ControlFlowNode prev | prev = node.getAPredecessor() | searchStep(prev, target)) + } + + predicate isDominatedByBehavior(Target target) { + forex(ControlFlowNode prev | prev = target.getAPredecessor() | not searchStep(prev, target)) + } +} diff --git a/cpp/common/src/codingstandards/cpp/enhancements/AggregateLiteralEnhancements.qll b/cpp/common/src/codingstandards/cpp/enhancements/AggregateLiteralEnhancements.qll index 4d80fc16a2..eb204c9608 100644 --- a/cpp/common/src/codingstandards/cpp/enhancements/AggregateLiteralEnhancements.qll +++ b/cpp/common/src/codingstandards/cpp/enhancements/AggregateLiteralEnhancements.qll @@ -85,6 +85,9 @@ module ArrayAggregateLiterals { // The aggregate itself not be compiler generated, or in a macro expansion, otherwise our line numbers will be off not cal.isCompilerGenerated() and not cal.isInMacroExpansion() and + // Ignore cases where the compilerGenerated value is a variable access targeting + // a parameter, as these are generated from variadic templates + not compilerGeneratedVal.(VariableAccess).getTarget() instanceof Parameter and exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | compilerGeneratedVal.getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) and previousExpr @@ -114,7 +117,7 @@ module ClassAggregateLiterals { exists(Expr compilerGeneratedVal, int index, Expr previousExpr | // Identify the candidate expression which may be compiler generated compilerGeneratedVal = cal.getChild(index) and - compilerGeneratedVal = cal.getFieldExpr(f) and + compilerGeneratedVal = cal.getAFieldExpr(f) and // Find the previous expression for this aggregate literal previousExpr = getPreviousExpr(cal, index) | @@ -201,7 +204,7 @@ class InferredAggregateLiteral extends AggregateLiteral { predicate isExprValueInitialized(AggregateLiteral al, Expr e) { // This expression is a value initialized field exists(Field f | - e = al.(ClassAggregateLiteral).getFieldExpr(f) and + e = al.(ClassAggregateLiteral).getAFieldExpr(f) and ClassAggregateLiterals::isValueInitialized(al, f) ) or @@ -236,7 +239,7 @@ predicate isLeadingZeroInitialized(AggregateLiteral a) { // Or because it's a class aggregate, and all other fields are value initialized forall(Field f | f = a.getType().(Class).getAField() and - not a.(ClassAggregateLiteral).getFieldExpr(f) = a.getChild(0) + not a.(ClassAggregateLiteral).getAFieldExpr(f) = a.getChild(0) | ClassAggregateLiterals::isValueInitialized(a, f) ) diff --git a/cpp/common/src/codingstandards/cpp/enhancements/ControlFlowGraphEnhancements.qll b/cpp/common/src/codingstandards/cpp/enhancements/ControlFlowGraphEnhancements.qll index 74d7e8e1c1..9dac58377c 100644 --- a/cpp/common/src/codingstandards/cpp/enhancements/ControlFlowGraphEnhancements.qll +++ b/cpp/common/src/codingstandards/cpp/enhancements/ControlFlowGraphEnhancements.qll @@ -10,8 +10,8 @@ import cpp * should be in this relation. */ pragma[noinline] -private predicate isFunction(Element el) { - el instanceof Function +private predicate isFunction(@element el) { + el instanceof @function or el.(Expr).getParent() = el } @@ -22,7 +22,7 @@ private predicate isFunction(Element el) { */ pragma[noopt] private predicate callHasNoTarget(@funbindexpr fc) { - exists(Function f | + exists(@function f | funbind(fc, f) and not isFunction(f) ) diff --git a/cpp/common/src/codingstandards/cpp/enhancements/MacroEnhacements.qll b/cpp/common/src/codingstandards/cpp/enhancements/MacroEnhacements.qll index ba132f5892..be79397929 100644 --- a/cpp/common/src/codingstandards/cpp/enhancements/MacroEnhacements.qll +++ b/cpp/common/src/codingstandards/cpp/enhancements/MacroEnhacements.qll @@ -29,7 +29,7 @@ module MacroEnhancements { /** A use of the NULL macro. */ class NULL extends StandardLibrary::Literal { NULL() { - exists(StandardLibrary::NULLMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) + exists(StandardLibrary::NullMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) } } } diff --git a/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlow.qll b/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlow.qll index df23fa4e95..d62bc8c02a 100644 --- a/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlow.qll +++ b/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlow.qll @@ -4,6 +4,8 @@ import cpp import codingstandards.cpp.standardlibrary.Exceptions +import codingstandards.cpp.exceptions.ExceptionSpecifications +import codingstandards.cpp.exceptions.ExceptionFlowCustomizations import ThirdPartyExceptions /* @@ -270,51 +272,6 @@ ExceptionType getAFunctionThrownType(Function f, ThrowingExpr throwingExpr) { ) } -/** A `ThrowingExpr` which is the origin of a exceptions in the program. */ -abstract class OriginThrowingExpr extends ThrowingExpr { } - -/** An expression which directly throws. */ -class DirectThrowExprThrowingExpr extends DirectThrowExpr, OriginThrowingExpr { - override ExceptionType getAnExceptionType() { result = getExceptionType() } -} - -/** An `typeid` expression which may throw `std::bad_typeid`. */ -class TypeIdThrowingExpr extends TypeidOperator, OriginThrowingExpr { - override ExceptionType getAnExceptionType() { result instanceof StdBadTypeId } -} - -/** An `new[]` expression which may throw `std::bad_array_new_length`. */ -class NewThrowingExpr extends NewArrayExpr, OriginThrowingExpr { - NewThrowingExpr() { - // If the extent is known to be below 0 at runtime - getExtent().getValue().toInt() < 0 - or - // initializer has more elements than the array size - getExtent().getValue().toInt() < getInitializer().(ArrayAggregateLiteral).getArraySize() - } - - override ExceptionType getAnExceptionType() { result instanceof StdBadArrayNewLength } -} - -/** A `ReThrowExpr` which throws a previously caught exception. */ -class ReThrowExprThrowingExpr extends ReThrowExpr, ThrowingExpr { - predicate rethrows(CatchBlock cb, ExceptionType et, ThrowingExpr te) { - // Find the nearest CatchBlock - cb = getNearestCatch(this.getEnclosingStmt()) and - // Find an `ExceptionType` which is caught by this catch block, and `ThrowingExpr` which throws that exception type - catches(cb, te, et) - } - - override ExceptionType getAnExceptionType() { rethrows(_, result, _) } - - CatchBlock getCatchBlock() { rethrows(result, _, _) } -} - -/** An expression which calls a function which may throw an exception. */ -class FunctionCallThrowingExpr extends FunctionCall, ThrowingExpr { - override ExceptionType getAnExceptionType() { result = getAFunctionThrownType(getTarget(), _) } -} - module ExceptionPathGraph { /** * A function for which we want path information. diff --git a/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlowCustomizations.qll b/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlowCustomizations.qll new file mode 100644 index 0000000000..90f67c3075 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlowCustomizations.qll @@ -0,0 +1,113 @@ +/* + * A library customize models that model the flow of exceptions through the program. + */ + +import cpp +private import codingstandards.cpp.exceptions.ExceptionFlow + +/** A `ThrowingExpr` which is the origin of a exceptions in the program. */ +abstract class OriginThrowingExpr extends ThrowingExpr { } + +/** + * A `FunctionCall` to an external function without an exception specification that * + * may throw an exception. + */ +abstract class ExternalUnderspecifiedFunctionCallThrowingExpr extends FunctionCall, ThrowingExpr { } + +/** + * An extensible predicate that describes functions that when called may throw an exception. + */ +extensible predicate throwingFunctionModel( + string functionNamespaceQualifier, string functionTypeQualifier, string functionName, + string exceptionNamespaceQualifier, string exceptionType +); + +/** + * A `FunctionCall` that may throw an exception of type `ExceptionType` as provded by + * the extensible predicate `throwingFunctionModel`. + */ +private class ExternalFunctionCallThrowingExpr extends FunctionCall, ThrowingExpr { + ExceptionType exceptionType; + + ExternalFunctionCallThrowingExpr() { + exists( + string functionNamespaceQualifier, string functionTypeQualifier, string functionName, + string exceptionNamespaceQualifier, string exceptionTypeSpec + | + throwingFunctionModel(functionNamespaceQualifier, functionTypeQualifier, functionName, + exceptionNamespaceQualifier, exceptionTypeSpec) and + this.getTarget() + .hasQualifiedName(functionNamespaceQualifier, functionTypeQualifier, functionName) and + exceptionType.(Class).hasQualifiedName(exceptionNamespaceQualifier, exceptionTypeSpec) + ) + } + + override ExceptionType getAnExceptionType() { result = exceptionType } +} + +/** An expression which directly throws. */ +class DirectThrowExprThrowingExpr extends DirectThrowExpr, OriginThrowingExpr { + override ExceptionType getAnExceptionType() { result = getExceptionType() } +} + +/** A `ReThrowExpr` which throws a previously caught exception. */ +class ReThrowExprThrowingExpr extends ReThrowExpr, ThrowingExpr { + predicate rethrows(CatchBlock cb, ExceptionType et, ThrowingExpr te) { + // Find the nearest CatchBlock + cb = getNearestCatch(this.getEnclosingStmt()) and + // Find an `ExceptionType` which is caught by this catch block, and `ThrowingExpr` which throws that exception type + catches(cb, te, et) + } + + override ExceptionType getAnExceptionType() { rethrows(_, result, _) } + + CatchBlock getCatchBlock() { rethrows(result, _, _) } +} + +/** An expression which calls a function which may throw an exception. */ +class FunctionCallThrowingExpr extends FunctionCall, ThrowingExpr { + override ExceptionType getAnExceptionType() { + exists(Function target | + target = getTarget() and + result = getAFunctionThrownType(target, _) and + // [expect.spec] states that throwing an exception type that is prohibited + // by the specification will result in the program terminating, unless + // a custom `unexpected_handler` is registered that throws an exception type + // which is compatible with the dynamic exception specification, or the + // dynamic exception specification lists `std::bad_exception`, in which case + // a `std::bad_exception` is thrown. + // As dynamic exception specifications and the `unexpected_handler` are both + // deprecated in C++14 and removed in C++17, we assume a default + // `std::unexpected` handler that calls `std::terminate` and therefore + // do not propagate such exceptions to the call sites for the function. + not ( + hasDynamicExceptionSpecification(target) and + not result = getAHandledExceptionType(target.getAThrownType()) + or + isNoExceptTrue(target) + ) + ) + or + result = this.(ExternalUnderspecifiedFunctionCallThrowingExpr).getAnExceptionType() + or + result = this.(ExternalFunctionCallThrowingExpr).getAnExceptionType() + } +} + +/** An `typeid` expression which may throw `std::bad_typeid`. */ +private class TypeIdThrowingExpr extends TypeidOperator, OriginThrowingExpr { + override ExceptionType getAnExceptionType() { result instanceof StdBadTypeId } +} + +/** An `new[]` expression which may throw `std::bad_array_new_length`. */ +private class NewThrowingExpr extends NewArrayExpr, OriginThrowingExpr { + NewThrowingExpr() { + // If the extent is known to be below 0 at runtime + getExtent().getValue().toInt() < 0 + or + // initializer has more elements than the array size + getExtent().getValue().toInt() < getInitializer().(ArrayAggregateLiteral).getArraySize() + } + + override ExceptionType getAnExceptionType() { result instanceof StdBadArrayNewLength } +} diff --git a/cpp/common/src/codingstandards/cpp/exceptions/SpecialFunctionExceptions.qll b/cpp/common/src/codingstandards/cpp/exceptions/SpecialFunctionExceptions.qll index 07a6fa32cb..c24cfee66d 100644 --- a/cpp/common/src/codingstandards/cpp/exceptions/SpecialFunctionExceptions.qll +++ b/cpp/common/src/codingstandards/cpp/exceptions/SpecialFunctionExceptions.qll @@ -36,7 +36,8 @@ class SpecialFunction extends Function { } /** A special function which throws an exception. */ -abstract class SpecialExceptionThrowingFunction extends ExceptionPathGraph::ExceptionThrowingFunction { +abstract class SpecialExceptionThrowingFunction extends ExceptionPathGraph::ExceptionThrowingFunction +{ SpecialExceptionThrowingFunction() { exists(getAFunctionThrownType(this, _)) } /** Gets a description for this exception throwing. */ diff --git a/cpp/common/src/codingstandards/cpp/exclusions/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/RuleMetadata.qll index d6511f61b5..c001b6073a 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/RuleMetadata.qll @@ -1,21 +1,90 @@ import cpp private import cpp.RuleMetadata as CPPRuleMetadata private import c.RuleMetadata as CRuleMetadata +private import codingstandards.cpp.guideline_recategorizations.GuidelineRecategorizations newtype TQuery = TQueryCPP(CPPRuleMetadata::TCPPQuery t) or - TQueryC(CRuleMetadata::TCQuery t) + TQueryC(CRuleMetadata::TCQuery t) or + /* A dummy query for testing purposes */ + TQueryTestDummy() + +private predicate isMisraRuleCategory(string category) { + category = ["disapplied", "advisory", "required", "mandatory"] +} + +newtype TEffectiveCategory = + TInvalid(string reason) { + exists(GuidelineRecategorization gr | reason = gr.getAnInvalidReason()) + } or + TDisapplied() or + TAdvisory() or + TRequired() or + TMandatory() or + TNonMisraRuleCategory(string category) { + exists(Query q | q.getCategory() = category | not isMisraRuleCategory(category)) + } + +class EffectiveCategory extends TEffectiveCategory { + string toString() { + this instanceof TInvalid and result = "invalid" + or + this instanceof TDisapplied and result = "disapplied" + or + this instanceof TAdvisory and result = "advisory" + or + this instanceof TRequired and result = "required" + or + this instanceof TMandatory and result = "mandatory" + or + this = TNonMisraRuleCategory(result) + } + + /** Holds if the effective category permits a deviation */ + predicate permitsDeviation() { not this instanceof TMandatory and not this instanceof TInvalid } + + /** Holds if the effective category is 'Disapplied'. */ + predicate isDisapplied() { this instanceof TDisapplied } +} class Query extends TQuery { string getQueryId() { - CPPRuleMetadata::isQueryMetadata(this, result, _) or - CRuleMetadata::isQueryMetadata(this, result, _) + CPPRuleMetadata::isQueryMetadata(this, result, _, _) + or + CRuleMetadata::isQueryMetadata(this, result, _, _) + or + this = TQueryTestDummy() and result = "cpp/test/dummy" } string getRuleId() { - CPPRuleMetadata::isQueryMetadata(this, _, result) or - CRuleMetadata::isQueryMetadata(this, _, result) + CPPRuleMetadata::isQueryMetadata(this, _, result, _) + or + CRuleMetadata::isQueryMetadata(this, _, result, _) + or + this = TQueryTestDummy() and result = "cpp-test-dummy" + } + + string getCategory() { + CPPRuleMetadata::isQueryMetadata(this, _, _, result) + or + CRuleMetadata::isQueryMetadata(this, _, _, result) + or + this = TQueryTestDummy() and result = "required" + } + + EffectiveCategory getEffectiveCategory() { + if exists(GuidelineRecategorization gr | gr.getQuery() = this) + then + exists(GuidelineRecategorization gr | gr.getQuery() = this | + result = gr.getEffectiveCategory() + ) + else result.toString() = this.getCategory() } string toString() { result = getQueryId() } } + +/** A `Query` used for shared query test cases. */ +class TestQuery extends Query { + TestQuery() { this = TQueryTestDummy() } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Alignment.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Alignment.qll new file mode 100644 index 0000000000..9447abf636 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Alignment.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype AlignmentQuery = + TRedeclarationOfObjectWithoutAlignmentQuery() or + TRedeclarationOfObjectWithUnmatchedAlignmentQuery() or + TAlignmentWithSizeZeroQuery() or + TMoreThanOneAlignmentSpecifierOnDeclarationQuery() + +predicate isAlignmentQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `redeclarationOfObjectWithoutAlignment` query + AlignmentPackage::redeclarationOfObjectWithoutAlignmentQuery() and + queryId = + // `@id` for the `redeclarationOfObjectWithoutAlignment` query + "c/misra/redeclaration-of-object-without-alignment" and + ruleId = "RULE-8-15" and + category = "required" + or + query = + // `Query` instance for the `redeclarationOfObjectWithUnmatchedAlignment` query + AlignmentPackage::redeclarationOfObjectWithUnmatchedAlignmentQuery() and + queryId = + // `@id` for the `redeclarationOfObjectWithUnmatchedAlignment` query + "c/misra/redeclaration-of-object-with-unmatched-alignment" and + ruleId = "RULE-8-15" and + category = "required" + or + query = + // `Query` instance for the `alignmentWithSizeZero` query + AlignmentPackage::alignmentWithSizeZeroQuery() and + queryId = + // `@id` for the `alignmentWithSizeZero` query + "c/misra/alignment-with-size-zero" and + ruleId = "RULE-8-16" and + category = "advisory" + or + query = + // `Query` instance for the `moreThanOneAlignmentSpecifierOnDeclaration` query + AlignmentPackage::moreThanOneAlignmentSpecifierOnDeclarationQuery() and + queryId = + // `@id` for the `moreThanOneAlignmentSpecifierOnDeclaration` query + "c/misra/more-than-one-alignment-specifier-on-declaration" and + ruleId = "RULE-8-17" and + category = "advisory" +} + +module AlignmentPackage { + Query redeclarationOfObjectWithoutAlignmentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `redeclarationOfObjectWithoutAlignment` query + TQueryC(TAlignmentPackageQuery(TRedeclarationOfObjectWithoutAlignmentQuery())) + } + + Query redeclarationOfObjectWithUnmatchedAlignmentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `redeclarationOfObjectWithUnmatchedAlignment` query + TQueryC(TAlignmentPackageQuery(TRedeclarationOfObjectWithUnmatchedAlignmentQuery())) + } + + Query alignmentWithSizeZeroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `alignmentWithSizeZero` query + TQueryC(TAlignmentPackageQuery(TAlignmentWithSizeZeroQuery())) + } + + Query moreThanOneAlignmentSpecifierOnDeclarationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `moreThanOneAlignmentSpecifierOnDeclaration` query + TQueryC(TAlignmentPackageQuery(TMoreThanOneAlignmentSpecifierOnDeclarationQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Banned.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Banned.qll index 7a9af8467d..f8a4e027bb 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Banned.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Banned.qll @@ -24,14 +24,15 @@ newtype BannedQuery = TOctalConstantsUsedQuery() or TRestrictTypeQualifierUsedQuery() -predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { +predicate isBannedQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotCallSystem` query BannedPackage::doNotCallSystemQuery() and queryId = // `@id` for the `doNotCallSystem` query "c/cert/do-not-call-system" and - ruleId = "ENV33-C" + ruleId = "ENV33-C" and + category = "rule" or query = // `Query` instance for the `commaOperatorShouldNotBeUsed` query @@ -39,7 +40,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `commaOperatorShouldNotBeUsed` query "c/misra/comma-operator-should-not-be-used" and - ruleId = "RULE-12-3" + ruleId = "RULE-12-3" and + category = "advisory" or query = // `Query` instance for the `featuresOfStdarghUsed` query @@ -47,7 +49,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `featuresOfStdarghUsed` query "c/misra/features-of-stdargh-used" and - ruleId = "RULE-17-1" + ruleId = "RULE-17-1" and + category = "required" or query = // `Query` instance for the `unionKeywordShouldNotBeUsed` query @@ -55,7 +58,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unionKeywordShouldNotBeUsed` query "c/misra/union-keyword-should-not-be-used" and - ruleId = "RULE-19-2" + ruleId = "RULE-19-2" and + category = "advisory" or query = // `Query` instance for the `standardLibraryTimeAndDateFunctionsUsed` query @@ -63,7 +67,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `standardLibraryTimeAndDateFunctionsUsed` query "c/misra/standard-library-time-and-date-functions-used" and - ruleId = "RULE-21-10" + ruleId = "RULE-21-10" and + category = "required" or query = // `Query` instance for the `standardHeaderFileTgmathhUsed` query @@ -71,7 +76,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `standardHeaderFileTgmathhUsed` query "c/misra/standard-header-file-tgmathh-used" and - ruleId = "RULE-21-11" + ruleId = "RULE-21-11" and + category = "advisory" or query = // `Query` instance for the `exceptionHandlingFeaturesOfFenvhUsed` query @@ -79,7 +85,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `exceptionHandlingFeaturesOfFenvhUsed` query "c/misra/exception-handling-features-of-fenvh-used" and - ruleId = "RULE-21-12" + ruleId = "RULE-21-12" and + category = "advisory" or query = // `Query` instance for the `systemOfStdlibhUsed` query @@ -87,7 +94,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `systemOfStdlibhUsed` query "c/misra/system-of-stdlibh-used" and - ruleId = "RULE-21-21" + ruleId = "RULE-21-21" and + category = "required" or query = // `Query` instance for the `memoryAllocDeallocFunctionsOfStdlibhUsed` query @@ -95,7 +103,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `memoryAllocDeallocFunctionsOfStdlibhUsed` query "c/misra/memory-alloc-dealloc-functions-of-stdlibh-used" and - ruleId = "RULE-21-3" + ruleId = "RULE-21-3" and + category = "required" or query = // `Query` instance for the `standardHeaderFileUsedSetjmph` query @@ -103,7 +112,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `standardHeaderFileUsedSetjmph` query "c/misra/standard-header-file-used-setjmph" and - ruleId = "RULE-21-4" + ruleId = "RULE-21-4" and + category = "required" or query = // `Query` instance for the `standardHeaderFileUsedSignalh` query @@ -111,7 +121,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `standardHeaderFileUsedSignalh` query "c/misra/standard-header-file-used-signalh" and - ruleId = "RULE-21-5" + ruleId = "RULE-21-5" and + category = "required" or query = // `Query` instance for the `standardLibraryInputoutputFunctionsUsed` query @@ -119,7 +130,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `standardLibraryInputoutputFunctionsUsed` query "c/misra/standard-library-inputoutput-functions-used" and - ruleId = "RULE-21-6" + ruleId = "RULE-21-6" and + category = "required" or query = // `Query` instance for the `atofAtoiAtolAndAtollOfStdlibhUsed` query @@ -127,7 +139,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `atofAtoiAtolAndAtollOfStdlibhUsed` query "c/misra/atof-atoi-atol-and-atoll-of-stdlibh-used" and - ruleId = "RULE-21-7" + ruleId = "RULE-21-7" and + category = "required" or query = // `Query` instance for the `terminationFunctionsOfStdlibhUsed` query @@ -135,7 +148,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `terminationFunctionsOfStdlibhUsed` query "c/misra/termination-functions-of-stdlibh-used" and - ruleId = "RULE-21-8" + ruleId = "RULE-21-8" and + category = "required" or query = // `Query` instance for the `terminationMacrosOfStdlibhUsed` query @@ -143,7 +157,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `terminationMacrosOfStdlibhUsed` query "c/misra/termination-macros-of-stdlibh-used" and - ruleId = "RULE-21-8" + ruleId = "RULE-21-8" and + category = "required" or query = // `Query` instance for the `bsearchAndQsortOfStdlibhUsed` query @@ -151,7 +166,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `bsearchAndQsortOfStdlibhUsed` query "c/misra/bsearch-and-qsort-of-stdlibh-used" and - ruleId = "RULE-21-9" + ruleId = "RULE-21-9" and + category = "required" or query = // `Query` instance for the `stdLibDynamicMemoryAllocationUsed` query @@ -159,7 +175,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `stdLibDynamicMemoryAllocationUsed` query "c/misra/std-lib-dynamic-memory-allocation-used" and - ruleId = "RULE-4-12" + ruleId = "DIR-4-12" and + category = "required" or query = // `Query` instance for the `octalConstantsUsed` query @@ -167,7 +184,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `octalConstantsUsed` query "c/misra/octal-constants-used" and - ruleId = "RULE-7-1" + ruleId = "RULE-7-1" and + category = "required" or query = // `Query` instance for the `restrictTypeQualifierUsed` query @@ -175,7 +193,8 @@ predicate isBannedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `restrictTypeQualifierUsed` query "c/misra/restrict-type-qualifier-used" and - ruleId = "RULE-8-14" + ruleId = "RULE-8-14" and + category = "required" } module BannedPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Banned2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Banned2.qll new file mode 100644 index 0000000000..024aa9b76c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Banned2.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Banned2Query = TCallToBannedRandomFunctionQuery() + +predicate isBanned2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `callToBannedRandomFunction` query + Banned2Package::callToBannedRandomFunctionQuery() and + queryId = + // `@id` for the `callToBannedRandomFunction` query + "c/misra/call-to-banned-random-function" and + ruleId = "RULE-21-24" and + category = "required" +} + +module Banned2Package { + Query callToBannedRandomFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `callToBannedRandomFunction` query + TQueryC(TBanned2PackageQuery(TCallToBannedRandomFunctionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/BitfieldTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/BitfieldTypes.qll new file mode 100644 index 0000000000..ef9e94b27a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/BitfieldTypes.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype BitfieldTypesQuery = + TBitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery() or + TSingleBitNamedBitFieldsOfASignedTypeQuery() + +predicate isBitfieldTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `bitFieldsShallOnlyBeDeclaredWithAnAppropriateType` query + BitfieldTypesPackage::bitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery() and + queryId = + // `@id` for the `bitFieldsShallOnlyBeDeclaredWithAnAppropriateType` query + "c/misra/bit-fields-shall-only-be-declared-with-an-appropriate-type" and + ruleId = "RULE-6-1" and + category = "required" + or + query = + // `Query` instance for the `singleBitNamedBitFieldsOfASignedType` query + BitfieldTypesPackage::singleBitNamedBitFieldsOfASignedTypeQuery() and + queryId = + // `@id` for the `singleBitNamedBitFieldsOfASignedType` query + "c/misra/single-bit-named-bit-fields-of-a-signed-type" and + ruleId = "RULE-6-2" and + category = "required" +} + +module BitfieldTypesPackage { + Query bitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `bitFieldsShallOnlyBeDeclaredWithAnAppropriateType` query + TQueryC(TBitfieldTypesPackageQuery(TBitFieldsShallOnlyBeDeclaredWithAnAppropriateTypeQuery())) + } + + Query singleBitNamedBitFieldsOfASignedTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `singleBitNamedBitFieldsOfASignedType` query + TQueryC(TBitfieldTypesPackageQuery(TSingleBitNamedBitFieldsOfASignedTypeQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/BitfieldTypes2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/BitfieldTypes2.qll new file mode 100644 index 0000000000..ca116bb51c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/BitfieldTypes2.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype BitfieldTypes2Query = TBitFieldDeclaredAsMemberOfAUnionQuery() + +predicate isBitfieldTypes2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `bitFieldDeclaredAsMemberOfAUnion` query + BitfieldTypes2Package::bitFieldDeclaredAsMemberOfAUnionQuery() and + queryId = + // `@id` for the `bitFieldDeclaredAsMemberOfAUnion` query + "c/misra/bit-field-declared-as-member-of-a-union" and + ruleId = "RULE-6-3" and + category = "required" +} + +module BitfieldTypes2Package { + Query bitFieldDeclaredAsMemberOfAUnionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `bitFieldDeclaredAsMemberOfAUnion` query + TQueryC(TBitfieldTypes2PackageQuery(TBitFieldDeclaredAsMemberOfAUnionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency1.qll index 7d49962781..ed3a82c28d 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency1.qll @@ -8,14 +8,15 @@ newtype Concurrency1Query = TRaceConditionsWhenUsingLibraryFunctionsQuery() or TDoNotCallSignalInMultithreadedProgramQuery() -predicate isConcurrency1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isConcurrency1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `preventDataRacesWithMultipleThreads` query Concurrency1Package::preventDataRacesWithMultipleThreadsQuery() and queryId = // `@id` for the `preventDataRacesWithMultipleThreads` query "c/cert/prevent-data-races-with-multiple-threads" and - ruleId = "CON32-C" + ruleId = "CON32-C" and + category = "rule" or query = // `Query` instance for the `raceConditionsWhenUsingLibraryFunctions` query @@ -23,7 +24,8 @@ predicate isConcurrency1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `raceConditionsWhenUsingLibraryFunctions` query "c/cert/race-conditions-when-using-library-functions" and - ruleId = "CON33-C" + ruleId = "CON33-C" and + category = "rule" or query = // `Query` instance for the `doNotCallSignalInMultithreadedProgram` query @@ -31,7 +33,8 @@ predicate isConcurrency1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotCallSignalInMultithreadedProgram` query "c/cert/do-not-call-signal-in-multithreaded-program" and - ruleId = "CON37-C" + ruleId = "CON37-C" and + category = "rule" } module Concurrency1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency2.qll index 7e3bbe10a7..38a3eaa513 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency2.qll @@ -7,14 +7,15 @@ newtype Concurrency2Query = TDeadlockByLockingInPredefinedOrderQuery() or TWrapFunctionsThatCanSpuriouslyWakeUpInLoopQuery() -predicate isConcurrency2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isConcurrency2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `deadlockByLockingInPredefinedOrder` query Concurrency2Package::deadlockByLockingInPredefinedOrderQuery() and queryId = // `@id` for the `deadlockByLockingInPredefinedOrder` query "c/cert/deadlock-by-locking-in-predefined-order" and - ruleId = "CON35-C" + ruleId = "CON35-C" and + category = "rule" or query = // `Query` instance for the `wrapFunctionsThatCanSpuriouslyWakeUpInLoop` query @@ -22,7 +23,8 @@ predicate isConcurrency2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `wrapFunctionsThatCanSpuriouslyWakeUpInLoop` query "c/cert/wrap-functions-that-can-spuriously-wake-up-in-loop" and - ruleId = "CON36-C" + ruleId = "CON36-C" and + category = "rule" } module Concurrency2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency3.qll index 029d0e16e4..982ea21543 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency3.qll @@ -9,14 +9,15 @@ newtype Concurrency3Query = TPreserveSafetyWhenUsingConditionVariablesQuery() or TWrapFunctionsThatCanFailSpuriouslyInLoopQuery() -predicate isConcurrency3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isConcurrency3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotAllowAMutexToGoOutOfScopeWhileLocked` query Concurrency3Package::doNotAllowAMutexToGoOutOfScopeWhileLockedQuery() and queryId = // `@id` for the `doNotAllowAMutexToGoOutOfScopeWhileLocked` query "c/cert/do-not-allow-a-mutex-to-go-out-of-scope-while-locked" and - ruleId = "CON31-C" + ruleId = "CON31-C" and + category = "rule" or query = // `Query` instance for the `doNotDestroyAMutexWhileItIsLocked` query @@ -24,7 +25,8 @@ predicate isConcurrency3QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotDestroyAMutexWhileItIsLocked` query "c/cert/do-not-destroy-a-mutex-while-it-is-locked" and - ruleId = "CON31-C" + ruleId = "CON31-C" and + category = "rule" or query = // `Query` instance for the `preserveSafetyWhenUsingConditionVariables` query @@ -32,7 +34,8 @@ predicate isConcurrency3QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `preserveSafetyWhenUsingConditionVariables` query "c/cert/preserve-safety-when-using-condition-variables" and - ruleId = "CON38-C" + ruleId = "CON38-C" and + category = "rule" or query = // `Query` instance for the `wrapFunctionsThatCanFailSpuriouslyInLoop` query @@ -40,7 +43,8 @@ predicate isConcurrency3QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `wrapFunctionsThatCanFailSpuriouslyInLoop` query "c/cert/wrap-functions-that-can-fail-spuriously-in-loop" and - ruleId = "CON41-C" + ruleId = "CON41-C" and + category = "rule" } module Concurrency3Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency4.qll index 43faee8521..dfe9273563 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency4.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency4.qll @@ -8,14 +8,15 @@ newtype Concurrency4Query = TAppropriateThreadObjectStorageDurationsQuery() or TThreadObjectStorageDurationsNotInitializedQuery() -predicate isConcurrency4QueryMetadata(Query query, string queryId, string ruleId) { +predicate isConcurrency4QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `cleanUpThreadSpecificStorage` query Concurrency4Package::cleanUpThreadSpecificStorageQuery() and queryId = // `@id` for the `cleanUpThreadSpecificStorage` query "c/cert/clean-up-thread-specific-storage" and - ruleId = "CON30-C" + ruleId = "CON30-C" and + category = "rule" or query = // `Query` instance for the `appropriateThreadObjectStorageDurations` query @@ -23,7 +24,8 @@ predicate isConcurrency4QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `appropriateThreadObjectStorageDurations` query "c/cert/appropriate-thread-object-storage-durations" and - ruleId = "CON34-C" + ruleId = "CON34-C" and + category = "rule" or query = // `Query` instance for the `threadObjectStorageDurationsNotInitialized` query @@ -31,7 +33,8 @@ predicate isConcurrency4QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `threadObjectStorageDurationsNotInitialized` query "c/cert/thread-object-storage-durations-not-initialized" and - ruleId = "CON34-C" + ruleId = "CON34-C" and + category = "rule" } module Concurrency4Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll index ebbb3dabe5..6ff1bc60a4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll @@ -7,14 +7,15 @@ newtype Concurrency5Query = TThreadWasPreviouslyJoinedOrDetachedQuery() or TAtomicVariableTwiceInExpressionQuery() -predicate isConcurrency5QueryMetadata(Query query, string queryId, string ruleId) { +predicate isConcurrency5QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `threadWasPreviouslyJoinedOrDetached` query Concurrency5Package::threadWasPreviouslyJoinedOrDetachedQuery() and queryId = // `@id` for the `threadWasPreviouslyJoinedOrDetached` query "c/cert/thread-was-previously-joined-or-detached" and - ruleId = "CON39-C" + ruleId = "CON39-C" and + category = "rule" or query = // `Query` instance for the `atomicVariableTwiceInExpression` query @@ -22,7 +23,8 @@ predicate isConcurrency5QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `atomicVariableTwiceInExpression` query "c/cert/atomic-variable-twice-in-expression" and - ruleId = "CON40-C" + ruleId = "CON40-C" and + category = "rule" } module Concurrency5Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency6.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency6.qll new file mode 100644 index 0000000000..62d9f362fe --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency6.qll @@ -0,0 +1,112 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Concurrency6Query = + TNotNoDeadlocksBetweenThreadsQuery() or + TThreadCreatedByThreadQuery() or + TBannedDynamicThreadCreationQuery() or + TAtomicAggregateObjectDirectlyAccessedQuery() or + TInvalidMemoryOrderArgumentQuery() or + TThreadPreviouslyJoinedOrDetachedQuery() + +predicate isConcurrency6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `notNoDeadlocksBetweenThreads` query + Concurrency6Package::notNoDeadlocksBetweenThreadsQuery() and + queryId = + // `@id` for the `notNoDeadlocksBetweenThreads` query + "c/misra/not-no-deadlocks-between-threads" and + ruleId = "DIR-5-2" and + category = "required" + or + query = + // `Query` instance for the `threadCreatedByThread` query + Concurrency6Package::threadCreatedByThreadQuery() and + queryId = + // `@id` for the `threadCreatedByThread` query + "c/misra/thread-created-by-thread" and + ruleId = "DIR-5-3" and + category = "required" + or + query = + // `Query` instance for the `bannedDynamicThreadCreation` query + Concurrency6Package::bannedDynamicThreadCreationQuery() and + queryId = + // `@id` for the `bannedDynamicThreadCreation` query + "c/misra/banned-dynamic-thread-creation" and + ruleId = "DIR-5-3" and + category = "required" + or + query = + // `Query` instance for the `atomicAggregateObjectDirectlyAccessed` query + Concurrency6Package::atomicAggregateObjectDirectlyAccessedQuery() and + queryId = + // `@id` for the `atomicAggregateObjectDirectlyAccessed` query + "c/misra/atomic-aggregate-object-directly-accessed" and + ruleId = "RULE-12-6" and + category = "required" + or + query = + // `Query` instance for the `invalidMemoryOrderArgument` query + Concurrency6Package::invalidMemoryOrderArgumentQuery() and + queryId = + // `@id` for the `invalidMemoryOrderArgument` query + "c/misra/invalid-memory-order-argument" and + ruleId = "RULE-21-25" and + category = "required" + or + query = + // `Query` instance for the `threadPreviouslyJoinedOrDetached` query + Concurrency6Package::threadPreviouslyJoinedOrDetachedQuery() and + queryId = + // `@id` for the `threadPreviouslyJoinedOrDetached` query + "c/misra/thread-previously-joined-or-detached" and + ruleId = "RULE-22-11" and + category = "required" +} + +module Concurrency6Package { + Query notNoDeadlocksBetweenThreadsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `notNoDeadlocksBetweenThreads` query + TQueryC(TConcurrency6PackageQuery(TNotNoDeadlocksBetweenThreadsQuery())) + } + + Query threadCreatedByThreadQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadCreatedByThread` query + TQueryC(TConcurrency6PackageQuery(TThreadCreatedByThreadQuery())) + } + + Query bannedDynamicThreadCreationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `bannedDynamicThreadCreation` query + TQueryC(TConcurrency6PackageQuery(TBannedDynamicThreadCreationQuery())) + } + + Query atomicAggregateObjectDirectlyAccessedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `atomicAggregateObjectDirectlyAccessed` query + TQueryC(TConcurrency6PackageQuery(TAtomicAggregateObjectDirectlyAccessedQuery())) + } + + Query invalidMemoryOrderArgumentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidMemoryOrderArgument` query + TQueryC(TConcurrency6PackageQuery(TInvalidMemoryOrderArgumentQuery())) + } + + Query threadPreviouslyJoinedOrDetachedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadPreviouslyJoinedOrDetached` query + TQueryC(TConcurrency6PackageQuery(TThreadPreviouslyJoinedOrDetachedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency7.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency7.qll new file mode 100644 index 0000000000..ba492b2a6b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency7.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Concurrency7Query = + TUninitializedAtomicObjectQuery() or + TTimedlockOnInappropriateMutexTypeQuery() + +predicate isConcurrency7QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `uninitializedAtomicObject` query + Concurrency7Package::uninitializedAtomicObjectQuery() and + queryId = + // `@id` for the `uninitializedAtomicObject` query + "c/misra/uninitialized-atomic-object" and + ruleId = "RULE-9-7" and + category = "mandatory" + or + query = + // `Query` instance for the `timedlockOnInappropriateMutexType` query + Concurrency7Package::timedlockOnInappropriateMutexTypeQuery() and + queryId = + // `@id` for the `timedlockOnInappropriateMutexType` query + "c/misra/timedlock-on-inappropriate-mutex-type" and + ruleId = "RULE-21-26" and + category = "required" +} + +module Concurrency7Package { + Query uninitializedAtomicObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `uninitializedAtomicObject` query + TQueryC(TConcurrency7PackageQuery(TUninitializedAtomicObjectQuery())) + } + + Query timedlockOnInappropriateMutexTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `timedlockOnInappropriateMutexType` query + TQueryC(TConcurrency7PackageQuery(TTimedlockOnInappropriateMutexTypeQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency8.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency8.qll new file mode 100644 index 0000000000..677b35d12b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency8.qll @@ -0,0 +1,112 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Concurrency8Query = + TNonstandardUseOfThreadingObjectQuery() or + TThreadingObjectWithInvalidStorageDurationQuery() or + TMutexNotInitializedBeforeUseQuery() or + TMutexInitializedInsideThreadQuery() or + TMutexInitWithInvalidMutexTypeQuery() or + TMutexObjectsNotAlwaysUnlockedQuery() + +predicate isConcurrency8QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `nonstandardUseOfThreadingObject` query + Concurrency8Package::nonstandardUseOfThreadingObjectQuery() and + queryId = + // `@id` for the `nonstandardUseOfThreadingObject` query + "c/misra/nonstandard-use-of-threading-object" and + ruleId = "RULE-22-12" and + category = "mandatory" + or + query = + // `Query` instance for the `threadingObjectWithInvalidStorageDuration` query + Concurrency8Package::threadingObjectWithInvalidStorageDurationQuery() and + queryId = + // `@id` for the `threadingObjectWithInvalidStorageDuration` query + "c/misra/threading-object-with-invalid-storage-duration" and + ruleId = "RULE-22-13" and + category = "required" + or + query = + // `Query` instance for the `mutexNotInitializedBeforeUse` query + Concurrency8Package::mutexNotInitializedBeforeUseQuery() and + queryId = + // `@id` for the `mutexNotInitializedBeforeUse` query + "c/misra/mutex-not-initialized-before-use" and + ruleId = "RULE-22-14" and + category = "mandatory" + or + query = + // `Query` instance for the `mutexInitializedInsideThread` query + Concurrency8Package::mutexInitializedInsideThreadQuery() and + queryId = + // `@id` for the `mutexInitializedInsideThread` query + "c/misra/mutex-initialized-inside-thread" and + ruleId = "RULE-22-14" and + category = "mandatory" + or + query = + // `Query` instance for the `mutexInitWithInvalidMutexType` query + Concurrency8Package::mutexInitWithInvalidMutexTypeQuery() and + queryId = + // `@id` for the `mutexInitWithInvalidMutexType` query + "c/misra/mutex-init-with-invalid-mutex-type" and + ruleId = "RULE-22-14" and + category = "mandatory" + or + query = + // `Query` instance for the `mutexObjectsNotAlwaysUnlocked` query + Concurrency8Package::mutexObjectsNotAlwaysUnlockedQuery() and + queryId = + // `@id` for the `mutexObjectsNotAlwaysUnlocked` query + "c/misra/mutex-objects-not-always-unlocked" and + ruleId = "RULE-22-16" and + category = "required" +} + +module Concurrency8Package { + Query nonstandardUseOfThreadingObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonstandardUseOfThreadingObject` query + TQueryC(TConcurrency8PackageQuery(TNonstandardUseOfThreadingObjectQuery())) + } + + Query threadingObjectWithInvalidStorageDurationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadingObjectWithInvalidStorageDuration` query + TQueryC(TConcurrency8PackageQuery(TThreadingObjectWithInvalidStorageDurationQuery())) + } + + Query mutexNotInitializedBeforeUseQuery() { + //autogenerate `Query` type + result = + // `Query` type for `mutexNotInitializedBeforeUse` query + TQueryC(TConcurrency8PackageQuery(TMutexNotInitializedBeforeUseQuery())) + } + + Query mutexInitializedInsideThreadQuery() { + //autogenerate `Query` type + result = + // `Query` type for `mutexInitializedInsideThread` query + TQueryC(TConcurrency8PackageQuery(TMutexInitializedInsideThreadQuery())) + } + + Query mutexInitWithInvalidMutexTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `mutexInitWithInvalidMutexType` query + TQueryC(TConcurrency8PackageQuery(TMutexInitWithInvalidMutexTypeQuery())) + } + + Query mutexObjectsNotAlwaysUnlockedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `mutexObjectsNotAlwaysUnlocked` query + TQueryC(TConcurrency8PackageQuery(TMutexObjectsNotAlwaysUnlockedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency9.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency9.qll new file mode 100644 index 0000000000..b013bbdabb --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency9.qll @@ -0,0 +1,146 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Concurrency9Query = + TPossibleDataRaceBetweenThreadsQuery() or + TThreadResourceDisposedBeforeThreadsJoinedQuery() or + TInvalidOperationOnUnlockedMutexQuery() or + TNonRecursiveMutexRecursivelyLockedQuery() or + TNonRecursiveMutexRecursivelyLockedAuditQuery() or + TConditionVariableUsedWithMultipleMutexesQuery() or + TThreadStorageNotInitializedBeforeUseQuery() or + TThreadStoragePointerInitializedInsideThreadQuery() + +predicate isConcurrency9QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `possibleDataRaceBetweenThreads` query + Concurrency9Package::possibleDataRaceBetweenThreadsQuery() and + queryId = + // `@id` for the `possibleDataRaceBetweenThreads` query + "c/misra/possible-data-race-between-threads" and + ruleId = "DIR-5-1" and + category = "required" + or + query = + // `Query` instance for the `threadResourceDisposedBeforeThreadsJoined` query + Concurrency9Package::threadResourceDisposedBeforeThreadsJoinedQuery() and + queryId = + // `@id` for the `threadResourceDisposedBeforeThreadsJoined` query + "c/misra/thread-resource-disposed-before-threads-joined" and + ruleId = "RULE-22-15" and + category = "required" + or + query = + // `Query` instance for the `invalidOperationOnUnlockedMutex` query + Concurrency9Package::invalidOperationOnUnlockedMutexQuery() and + queryId = + // `@id` for the `invalidOperationOnUnlockedMutex` query + "c/misra/invalid-operation-on-unlocked-mutex" and + ruleId = "RULE-22-17" and + category = "required" + or + query = + // `Query` instance for the `nonRecursiveMutexRecursivelyLocked` query + Concurrency9Package::nonRecursiveMutexRecursivelyLockedQuery() and + queryId = + // `@id` for the `nonRecursiveMutexRecursivelyLocked` query + "c/misra/non-recursive-mutex-recursively-locked" and + ruleId = "RULE-22-18" and + category = "required" + or + query = + // `Query` instance for the `nonRecursiveMutexRecursivelyLockedAudit` query + Concurrency9Package::nonRecursiveMutexRecursivelyLockedAuditQuery() and + queryId = + // `@id` for the `nonRecursiveMutexRecursivelyLockedAudit` query + "c/misra/non-recursive-mutex-recursively-locked-audit" and + ruleId = "RULE-22-18" and + category = "required" + or + query = + // `Query` instance for the `conditionVariableUsedWithMultipleMutexes` query + Concurrency9Package::conditionVariableUsedWithMultipleMutexesQuery() and + queryId = + // `@id` for the `conditionVariableUsedWithMultipleMutexes` query + "c/misra/condition-variable-used-with-multiple-mutexes" and + ruleId = "RULE-22-19" and + category = "required" + or + query = + // `Query` instance for the `threadStorageNotInitializedBeforeUse` query + Concurrency9Package::threadStorageNotInitializedBeforeUseQuery() and + queryId = + // `@id` for the `threadStorageNotInitializedBeforeUse` query + "c/misra/thread-storage-not-initialized-before-use" and + ruleId = "RULE-22-20" and + category = "mandatory" + or + query = + // `Query` instance for the `threadStoragePointerInitializedInsideThread` query + Concurrency9Package::threadStoragePointerInitializedInsideThreadQuery() and + queryId = + // `@id` for the `threadStoragePointerInitializedInsideThread` query + "c/misra/thread-storage-pointer-initialized-inside-thread" and + ruleId = "RULE-22-20" and + category = "mandatory" +} + +module Concurrency9Package { + Query possibleDataRaceBetweenThreadsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `possibleDataRaceBetweenThreads` query + TQueryC(TConcurrency9PackageQuery(TPossibleDataRaceBetweenThreadsQuery())) + } + + Query threadResourceDisposedBeforeThreadsJoinedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadResourceDisposedBeforeThreadsJoined` query + TQueryC(TConcurrency9PackageQuery(TThreadResourceDisposedBeforeThreadsJoinedQuery())) + } + + Query invalidOperationOnUnlockedMutexQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidOperationOnUnlockedMutex` query + TQueryC(TConcurrency9PackageQuery(TInvalidOperationOnUnlockedMutexQuery())) + } + + Query nonRecursiveMutexRecursivelyLockedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonRecursiveMutexRecursivelyLocked` query + TQueryC(TConcurrency9PackageQuery(TNonRecursiveMutexRecursivelyLockedQuery())) + } + + Query nonRecursiveMutexRecursivelyLockedAuditQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonRecursiveMutexRecursivelyLockedAudit` query + TQueryC(TConcurrency9PackageQuery(TNonRecursiveMutexRecursivelyLockedAuditQuery())) + } + + Query conditionVariableUsedWithMultipleMutexesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `conditionVariableUsedWithMultipleMutexes` query + TQueryC(TConcurrency9PackageQuery(TConditionVariableUsedWithMultipleMutexesQuery())) + } + + Query threadStorageNotInitializedBeforeUseQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadStorageNotInitializedBeforeUse` query + TQueryC(TConcurrency9PackageQuery(TThreadStorageNotInitializedBeforeUseQuery())) + } + + Query threadStoragePointerInitializedInsideThreadQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadStoragePointerInitializedInsideThread` query + TQueryC(TConcurrency9PackageQuery(TThreadStoragePointerInitializedInsideThreadQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts.qll new file mode 100644 index 0000000000..174e7769b7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype ContractsQuery = + TDoNotViolateInLineLinkageConstraintsQuery() or + TCheckMathLibraryFunctionParametersQuery() or + TLowPrecisionPeriodicTrigonometricFunctionCallQuery() or + TFunctionErrorInformationUntestedQuery() + +predicate isContractsQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotViolateInLineLinkageConstraints` query + ContractsPackage::doNotViolateInLineLinkageConstraintsQuery() and + queryId = + // `@id` for the `doNotViolateInLineLinkageConstraints` query + "c/cert/do-not-violate-in-line-linkage-constraints" and + ruleId = "MSC40-C" and + category = "rule" + or + query = + // `Query` instance for the `checkMathLibraryFunctionParameters` query + ContractsPackage::checkMathLibraryFunctionParametersQuery() and + queryId = + // `@id` for the `checkMathLibraryFunctionParameters` query + "c/misra/check-math-library-function-parameters" and + ruleId = "DIR-4-11" and + category = "required" + or + query = + // `Query` instance for the `lowPrecisionPeriodicTrigonometricFunctionCall` query + ContractsPackage::lowPrecisionPeriodicTrigonometricFunctionCallQuery() and + queryId = + // `@id` for the `lowPrecisionPeriodicTrigonometricFunctionCall` query + "c/misra/low-precision-periodic-trigonometric-function-call" and + ruleId = "DIR-4-11" and + category = "required" + or + query = + // `Query` instance for the `functionErrorInformationUntested` query + ContractsPackage::functionErrorInformationUntestedQuery() and + queryId = + // `@id` for the `functionErrorInformationUntested` query + "c/misra/function-error-information-untested" and + ruleId = "DIR-4-7" and + category = "required" +} + +module ContractsPackage { + Query doNotViolateInLineLinkageConstraintsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotViolateInLineLinkageConstraints` query + TQueryC(TContractsPackageQuery(TDoNotViolateInLineLinkageConstraintsQuery())) + } + + Query checkMathLibraryFunctionParametersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `checkMathLibraryFunctionParameters` query + TQueryC(TContractsPackageQuery(TCheckMathLibraryFunctionParametersQuery())) + } + + Query lowPrecisionPeriodicTrigonometricFunctionCallQuery() { + //autogenerate `Query` type + result = + // `Query` type for `lowPrecisionPeriodicTrigonometricFunctionCall` query + TQueryC(TContractsPackageQuery(TLowPrecisionPeriodicTrigonometricFunctionCallQuery())) + } + + Query functionErrorInformationUntestedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionErrorInformationUntested` query + TQueryC(TContractsPackageQuery(TFunctionErrorInformationUntestedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll index b7a10673b7..3336438b98 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll @@ -7,14 +7,15 @@ newtype Contracts1Query = TDoNotModifyTheReturnValueOfCertainFunctionsQuery() or TEnvPointerIsInvalidAfterCertainOperationsQuery() -predicate isContracts1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isContracts1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotModifyTheReturnValueOfCertainFunctions` query Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery() and queryId = // `@id` for the `doNotModifyTheReturnValueOfCertainFunctions` query "c/cert/do-not-modify-the-return-value-of-certain-functions" and - ruleId = "ENV30-C" + ruleId = "ENV30-C" and + category = "rule" or query = // `Query` instance for the `envPointerIsInvalidAfterCertainOperations` query @@ -22,7 +23,8 @@ predicate isContracts1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `envPointerIsInvalidAfterCertainOperations` query "c/cert/env-pointer-is-invalid-after-certain-operations" and - ruleId = "ENV31-C" + ruleId = "ENV31-C" and + category = "rule" } module Contracts1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts2.qll index ebf2ba3e26..f5cfe21f1a 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts2.qll @@ -11,14 +11,15 @@ newtype Contracts2Query = TCallToSetlocaleInvalidatesOldPointersQuery() or TCallToSetlocaleInvalidatesOldPointersWarnQuery() -predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `exitHandlersMustReturnNormally` query Contracts2Package::exitHandlersMustReturnNormallyQuery() and queryId = // `@id` for the `exitHandlersMustReturnNormally` query "c/cert/exit-handlers-must-return-normally" and - ruleId = "ENV32-C" + ruleId = "ENV32-C" and + category = "rule" or query = // `Query` instance for the `doNotStorePointersReturnedByEnvFunctions` query @@ -26,7 +27,8 @@ predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotStorePointersReturnedByEnvFunctions` query "c/cert/do-not-store-pointers-returned-by-env-functions" and - ruleId = "ENV34-C" + ruleId = "ENV34-C" and + category = "rule" or query = // `Query` instance for the `doNotStorePointersReturnedByEnvironmentFunWarn` query @@ -34,7 +36,8 @@ predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotStorePointersReturnedByEnvironmentFunWarn` query "c/cert/do-not-store-pointers-returned-by-environment-fun-warn" and - ruleId = "ENV34-C" + ruleId = "ENV34-C" and + category = "rule" or query = // `Query` instance for the `valuesReturnedByLocaleSettingUsedAsPtrToConst` query @@ -42,7 +45,8 @@ predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `valuesReturnedByLocaleSettingUsedAsPtrToConst` query "c/misra/values-returned-by-locale-setting-used-as-ptr-to-const" and - ruleId = "RULE-21-19" + ruleId = "RULE-21-19" and + category = "mandatory" or query = // `Query` instance for the `callToSetlocaleInvalidatesOldPointers` query @@ -50,7 +54,8 @@ predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `callToSetlocaleInvalidatesOldPointers` query "c/misra/call-to-setlocale-invalidates-old-pointers" and - ruleId = "RULE-21-20" + ruleId = "RULE-21-20" and + category = "mandatory" or query = // `Query` instance for the `callToSetlocaleInvalidatesOldPointersWarn` query @@ -58,7 +63,8 @@ predicate isContracts2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `callToSetlocaleInvalidatesOldPointersWarn` query "c/misra/call-to-setlocale-invalidates-old-pointers-warn" and - ruleId = "RULE-21-20" + ruleId = "RULE-21-20" and + category = "mandatory" } module Contracts2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts3.qll index 7b7e1224d0..633907ba8c 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts3.qll @@ -8,14 +8,15 @@ newtype Contracts3Query = TErrnoSetToZeroPriorToCallQuery() or TErrnoSetToZeroAfterCallQuery() -predicate isContracts3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isContracts3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `onlyTestErrnoRightAfterErrnoSettingFunction` query Contracts3Package::onlyTestErrnoRightAfterErrnoSettingFunctionQuery() and queryId = // `@id` for the `onlyTestErrnoRightAfterErrnoSettingFunction` query "c/misra/only-test-errno-right-after-errno-setting-function" and - ruleId = "RULE-22-10" + ruleId = "RULE-22-10" and + category = "required" or query = // `Query` instance for the `errnoSetToZeroPriorToCall` query @@ -23,7 +24,8 @@ predicate isContracts3QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `errnoSetToZeroPriorToCall` query "c/misra/errno-set-to-zero-prior-to-call" and - ruleId = "RULE-22-8" + ruleId = "RULE-22-8" and + category = "required" or query = // `Query` instance for the `errnoSetToZeroAfterCall` query @@ -31,7 +33,8 @@ predicate isContracts3QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `errnoSetToZeroAfterCall` query "c/misra/errno-set-to-zero-after-call" and - ruleId = "RULE-22-9" + ruleId = "RULE-22-9" and + category = "required" } module Contracts3Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts4.qll new file mode 100644 index 0000000000..2603360915 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts4.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Contracts4Query = + TSetlocaleMightSetErrnoQuery() or + TErrnoReadBeforeReturnQuery() or + TFunctionCallBeforeErrnoCheckQuery() or + TErrnoNotSetToZeroQuery() + +predicate isContracts4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `setlocaleMightSetErrno` query + Contracts4Package::setlocaleMightSetErrnoQuery() and + queryId = + // `@id` for the `setlocaleMightSetErrno` query + "c/cert/setlocale-might-set-errno" and + ruleId = "ERR30-C" and + category = "rule" + or + query = + // `Query` instance for the `errnoReadBeforeReturn` query + Contracts4Package::errnoReadBeforeReturnQuery() and + queryId = + // `@id` for the `errnoReadBeforeReturn` query + "c/cert/errno-read-before-return" and + ruleId = "ERR30-C" and + category = "rule" + or + query = + // `Query` instance for the `functionCallBeforeErrnoCheck` query + Contracts4Package::functionCallBeforeErrnoCheckQuery() and + queryId = + // `@id` for the `functionCallBeforeErrnoCheck` query + "c/cert/function-call-before-errno-check" and + ruleId = "ERR30-C" and + category = "rule" + or + query = + // `Query` instance for the `errnoNotSetToZero` query + Contracts4Package::errnoNotSetToZeroQuery() and + queryId = + // `@id` for the `errnoNotSetToZero` query + "c/cert/errno-not-set-to-zero" and + ruleId = "ERR30-C" and + category = "rule" +} + +module Contracts4Package { + Query setlocaleMightSetErrnoQuery() { + //autogenerate `Query` type + result = + // `Query` type for `setlocaleMightSetErrno` query + TQueryC(TContracts4PackageQuery(TSetlocaleMightSetErrnoQuery())) + } + + Query errnoReadBeforeReturnQuery() { + //autogenerate `Query` type + result = + // `Query` type for `errnoReadBeforeReturn` query + TQueryC(TContracts4PackageQuery(TErrnoReadBeforeReturnQuery())) + } + + Query functionCallBeforeErrnoCheckQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionCallBeforeErrnoCheck` query + TQueryC(TContracts4PackageQuery(TFunctionCallBeforeErrnoCheckQuery())) + } + + Query errnoNotSetToZeroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `errnoNotSetToZero` query + TQueryC(TContracts4PackageQuery(TErrnoNotSetToZeroQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts5.qll new file mode 100644 index 0000000000..53daab0105 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts5.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Contracts5Query = + TDoNotRelyOnIndeterminateValuesOfErrnoQuery() or + TDetectAndHandleStandardLibraryErrorsQuery() + +predicate isContracts5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotRelyOnIndeterminateValuesOfErrno` query + Contracts5Package::doNotRelyOnIndeterminateValuesOfErrnoQuery() and + queryId = + // `@id` for the `doNotRelyOnIndeterminateValuesOfErrno` query + "c/cert/do-not-rely-on-indeterminate-values-of-errno" and + ruleId = "ERR32-C" and + category = "rule" + or + query = + // `Query` instance for the `detectAndHandleStandardLibraryErrors` query + Contracts5Package::detectAndHandleStandardLibraryErrorsQuery() and + queryId = + // `@id` for the `detectAndHandleStandardLibraryErrors` query + "c/cert/detect-and-handle-standard-library-errors" and + ruleId = "ERR33-C" and + category = "rule" +} + +module Contracts5Package { + Query doNotRelyOnIndeterminateValuesOfErrnoQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotRelyOnIndeterminateValuesOfErrno` query + TQueryC(TContracts5PackageQuery(TDoNotRelyOnIndeterminateValuesOfErrnoQuery())) + } + + Query detectAndHandleStandardLibraryErrorsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `detectAndHandleStandardLibraryErrors` query + TQueryC(TContracts5PackageQuery(TDetectAndHandleStandardLibraryErrorsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts6.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts6.qll new file mode 100644 index 0000000000..eed78ae507 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts6.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Contracts6Query = + TDoNotModifyConstantObjectsQuery() or + TArrayFunctionArgumentNumberOfElementsQuery() or + TValueReturnedByAFunctionNotUsedQuery() + +predicate isContracts6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotModifyConstantObjects` query + Contracts6Package::doNotModifyConstantObjectsQuery() and + queryId = + // `@id` for the `doNotModifyConstantObjects` query + "c/cert/do-not-modify-constant-objects" and + ruleId = "EXP40-C" and + category = "rule" + or + query = + // `Query` instance for the `arrayFunctionArgumentNumberOfElements` query + Contracts6Package::arrayFunctionArgumentNumberOfElementsQuery() and + queryId = + // `@id` for the `arrayFunctionArgumentNumberOfElements` query + "c/misra/array-function-argument-number-of-elements" and + ruleId = "RULE-17-5" and + category = "required" + or + query = + // `Query` instance for the `valueReturnedByAFunctionNotUsed` query + Contracts6Package::valueReturnedByAFunctionNotUsedQuery() and + queryId = + // `@id` for the `valueReturnedByAFunctionNotUsed` query + "c/misra/value-returned-by-a-function-not-used" and + ruleId = "RULE-17-7" and + category = "required" +} + +module Contracts6Package { + Query doNotModifyConstantObjectsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotModifyConstantObjects` query + TQueryC(TContracts6PackageQuery(TDoNotModifyConstantObjectsQuery())) + } + + Query arrayFunctionArgumentNumberOfElementsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `arrayFunctionArgumentNumberOfElements` query + TQueryC(TContracts6PackageQuery(TArrayFunctionArgumentNumberOfElementsQuery())) + } + + Query valueReturnedByAFunctionNotUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `valueReturnedByAFunctionNotUsed` query + TQueryC(TContracts6PackageQuery(TValueReturnedByAFunctionNotUsedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts7.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts7.qll new file mode 100644 index 0000000000..f6838fe797 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts7.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Contracts7Query = + TDoNotPassInvalidDataToTheAsctimeFunctionQuery() or + TDoNotCallVaArgOnAVaListThatHasAnIndeterminateValueQuery() or + TRightHandOperandOfAShiftRangeQuery() or + TObjectAssignedToAnOverlappingObjectQuery() or + TObjectCopiedToAnOverlappingObjectQuery() + +predicate isContracts7QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotPassInvalidDataToTheAsctimeFunction` query + Contracts7Package::doNotPassInvalidDataToTheAsctimeFunctionQuery() and + queryId = + // `@id` for the `doNotPassInvalidDataToTheAsctimeFunction` query + "c/cert/do-not-pass-invalid-data-to-the-asctime-function" and + ruleId = "MSC33-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotCallVaArgOnAVaListThatHasAnIndeterminateValue` query + Contracts7Package::doNotCallVaArgOnAVaListThatHasAnIndeterminateValueQuery() and + queryId = + // `@id` for the `doNotCallVaArgOnAVaListThatHasAnIndeterminateValue` query + "c/cert/do-not-call-va-arg-on-a-va-list-that-has-an-indeterminate-value" and + ruleId = "MSC39-C" and + category = "rule" + or + query = + // `Query` instance for the `rightHandOperandOfAShiftRange` query + Contracts7Package::rightHandOperandOfAShiftRangeQuery() and + queryId = + // `@id` for the `rightHandOperandOfAShiftRange` query + "c/misra/right-hand-operand-of-a-shift-range" and + ruleId = "RULE-12-2" and + category = "required" + or + query = + // `Query` instance for the `objectAssignedToAnOverlappingObject` query + Contracts7Package::objectAssignedToAnOverlappingObjectQuery() and + queryId = + // `@id` for the `objectAssignedToAnOverlappingObject` query + "c/misra/object-assigned-to-an-overlapping-object" and + ruleId = "RULE-19-1" and + category = "mandatory" + or + query = + // `Query` instance for the `objectCopiedToAnOverlappingObject` query + Contracts7Package::objectCopiedToAnOverlappingObjectQuery() and + queryId = + // `@id` for the `objectCopiedToAnOverlappingObject` query + "c/misra/object-copied-to-an-overlapping-object" and + ruleId = "RULE-19-1" and + category = "mandatory" +} + +module Contracts7Package { + Query doNotPassInvalidDataToTheAsctimeFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotPassInvalidDataToTheAsctimeFunction` query + TQueryC(TContracts7PackageQuery(TDoNotPassInvalidDataToTheAsctimeFunctionQuery())) + } + + Query doNotCallVaArgOnAVaListThatHasAnIndeterminateValueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCallVaArgOnAVaListThatHasAnIndeterminateValue` query + TQueryC(TContracts7PackageQuery(TDoNotCallVaArgOnAVaListThatHasAnIndeterminateValueQuery())) + } + + Query rightHandOperandOfAShiftRangeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `rightHandOperandOfAShiftRange` query + TQueryC(TContracts7PackageQuery(TRightHandOperandOfAShiftRangeQuery())) + } + + Query objectAssignedToAnOverlappingObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectAssignedToAnOverlappingObject` query + TQueryC(TContracts7PackageQuery(TObjectAssignedToAnOverlappingObjectQuery())) + } + + Query objectCopiedToAnOverlappingObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectCopiedToAnOverlappingObject` query + TQueryC(TContracts7PackageQuery(TObjectCopiedToAnOverlappingObjectQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/DeadCode.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/DeadCode.qll new file mode 100644 index 0000000000..9117d62321 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/DeadCode.qll @@ -0,0 +1,129 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype DeadCodeQuery = + TUnreachableCodeQuery() or + TDeadCodeQuery() or + TUnusedTypeDeclarationsQuery() or + TUnusedTagDeclarationQuery() or + TUnusedMacroDeclarationQuery() or + TUnusedLabelDeclarationQuery() or + TUnusedParameterQuery() + +predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `unreachableCode` query + DeadCodePackage::unreachableCodeQuery() and + queryId = + // `@id` for the `unreachableCode` query + "c/misra/unreachable-code" and + ruleId = "RULE-2-1" and + category = "required" + or + query = + // `Query` instance for the `deadCode` query + DeadCodePackage::deadCodeQuery() and + queryId = + // `@id` for the `deadCode` query + "c/misra/dead-code" and + ruleId = "RULE-2-2" and + category = "required" + or + query = + // `Query` instance for the `unusedTypeDeclarations` query + DeadCodePackage::unusedTypeDeclarationsQuery() and + queryId = + // `@id` for the `unusedTypeDeclarations` query + "c/misra/unused-type-declarations" and + ruleId = "RULE-2-3" and + category = "advisory" + or + query = + // `Query` instance for the `unusedTagDeclaration` query + DeadCodePackage::unusedTagDeclarationQuery() and + queryId = + // `@id` for the `unusedTagDeclaration` query + "c/misra/unused-tag-declaration" and + ruleId = "RULE-2-4" and + category = "advisory" + or + query = + // `Query` instance for the `unusedMacroDeclaration` query + DeadCodePackage::unusedMacroDeclarationQuery() and + queryId = + // `@id` for the `unusedMacroDeclaration` query + "c/misra/unused-macro-declaration" and + ruleId = "RULE-2-5" and + category = "advisory" + or + query = + // `Query` instance for the `unusedLabelDeclaration` query + DeadCodePackage::unusedLabelDeclarationQuery() and + queryId = + // `@id` for the `unusedLabelDeclaration` query + "c/misra/unused-label-declaration" and + ruleId = "RULE-2-6" and + category = "advisory" + or + query = + // `Query` instance for the `unusedParameter` query + DeadCodePackage::unusedParameterQuery() and + queryId = + // `@id` for the `unusedParameter` query + "c/misra/unused-parameter" and + ruleId = "RULE-2-7" and + category = "advisory" +} + +module DeadCodePackage { + Query unreachableCodeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unreachableCode` query + TQueryC(TDeadCodePackageQuery(TUnreachableCodeQuery())) + } + + Query deadCodeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `deadCode` query + TQueryC(TDeadCodePackageQuery(TDeadCodeQuery())) + } + + Query unusedTypeDeclarationsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedTypeDeclarations` query + TQueryC(TDeadCodePackageQuery(TUnusedTypeDeclarationsQuery())) + } + + Query unusedTagDeclarationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedTagDeclaration` query + TQueryC(TDeadCodePackageQuery(TUnusedTagDeclarationQuery())) + } + + Query unusedMacroDeclarationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedMacroDeclaration` query + TQueryC(TDeadCodePackageQuery(TUnusedMacroDeclarationQuery())) + } + + Query unusedLabelDeclarationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedLabelDeclaration` query + TQueryC(TDeadCodePackageQuery(TUnusedLabelDeclarationQuery())) + } + + Query unusedParameterQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedParameter` query + TQueryC(TDeadCodePackageQuery(TUnusedParameterQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/DeadCode2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/DeadCode2.qll new file mode 100644 index 0000000000..611062a5ac --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/DeadCode2.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype DeadCode2Query = + TUnusedObjectDefinitionQuery() or + TUnusedObjectDefinitionStrictQuery() + +predicate isDeadCode2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `unusedObjectDefinition` query + DeadCode2Package::unusedObjectDefinitionQuery() and + queryId = + // `@id` for the `unusedObjectDefinition` query + "c/misra/unused-object-definition" and + ruleId = "RULE-2-8" and + category = "advisory" + or + query = + // `Query` instance for the `unusedObjectDefinitionStrict` query + DeadCode2Package::unusedObjectDefinitionStrictQuery() and + queryId = + // `@id` for the `unusedObjectDefinitionStrict` query + "c/misra/unused-object-definition-strict" and + ruleId = "RULE-2-8" and + category = "advisory" +} + +module DeadCode2Package { + Query unusedObjectDefinitionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedObjectDefinition` query + TQueryC(TDeadCode2PackageQuery(TUnusedObjectDefinitionQuery())) + } + + Query unusedObjectDefinitionStrictQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedObjectDefinitionStrict` query + TQueryC(TDeadCode2PackageQuery(TUnusedObjectDefinitionStrictQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll index c52cd567c8..c5ffaa56e3 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll @@ -11,14 +11,15 @@ newtype Declarations1Query = TMacroIdentifiersNotDistinctQuery() or TMacroIdentifierNotDistinctFromParameterQuery() -predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `declareIdentifiersBeforeUsingThem` query Declarations1Package::declareIdentifiersBeforeUsingThemQuery() and queryId = // `@id` for the `declareIdentifiersBeforeUsingThem` query "c/cert/declare-identifiers-before-using-them" and - ruleId = "DCL31-C" + ruleId = "DCL31-C" and + category = "rule" or query = // `Query` instance for the `doNotDeclareOrDefineAReservedIdentifier` query @@ -26,7 +27,8 @@ predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `doNotDeclareOrDefineAReservedIdentifier` query "c/cert/do-not-declare-or-define-a-reserved-identifier" and - ruleId = "DCL37-C" + ruleId = "DCL37-C" and + category = "rule" or query = // `Query` instance for the `doNotDeclareAReservedIdentifier` query @@ -34,7 +36,8 @@ predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `doNotDeclareAReservedIdentifier` query "c/misra/do-not-declare-a-reserved-identifier" and - ruleId = "RULE-21-2" + ruleId = "RULE-21-2" and + category = "required" or query = // `Query` instance for the `externalIdentifiersNotDistinct` query @@ -42,7 +45,8 @@ predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `externalIdentifiersNotDistinct` query "c/misra/external-identifiers-not-distinct" and - ruleId = "RULE-5-1" + ruleId = "RULE-5-1" and + category = "required" or query = // `Query` instance for the `macroIdentifiersNotDistinct` query @@ -50,7 +54,8 @@ predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `macroIdentifiersNotDistinct` query "c/misra/macro-identifiers-not-distinct" and - ruleId = "RULE-5-4" + ruleId = "RULE-5-4" and + category = "required" or query = // `Query` instance for the `macroIdentifierNotDistinctFromParameter` query @@ -58,7 +63,8 @@ predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `macroIdentifierNotDistinctFromParameter` query "c/misra/macro-identifier-not-distinct-from-parameter" and - ruleId = "RULE-5-4" + ruleId = "RULE-5-4" and + category = "required" } module Declarations1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations2.qll index 5314ebc400..a8b7b95d2c 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations2.qll @@ -10,14 +10,15 @@ newtype Declarations2Query = TIncompatibleFunctionDeclarationsQuery() or TVariablesInsideSwitchStatementQuery() -predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `declaringAFlexibleArrayMember` query Declarations2Package::declaringAFlexibleArrayMemberQuery() and queryId = // `@id` for the `declaringAFlexibleArrayMember` query "c/cert/declaring-a-flexible-array-member" and - ruleId = "DCL38-C" + ruleId = "DCL38-C" and + category = "rule" or query = // `Query` instance for the `excessLengthNamesIdentifiersNotDistinct` query @@ -25,7 +26,8 @@ predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `excessLengthNamesIdentifiersNotDistinct` query "c/cert/excess-length-names-identifiers-not-distinct" and - ruleId = "DCL40-C" + ruleId = "DCL40-C" and + category = "rule" or query = // `Query` instance for the `incompatibleObjectDeclarations` query @@ -33,7 +35,8 @@ predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `incompatibleObjectDeclarations` query "c/cert/incompatible-object-declarations" and - ruleId = "DCL40-C" + ruleId = "DCL40-C" and + category = "rule" or query = // `Query` instance for the `incompatibleFunctionDeclarations` query @@ -41,7 +44,8 @@ predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `incompatibleFunctionDeclarations` query "c/cert/incompatible-function-declarations" and - ruleId = "DCL40-C" + ruleId = "DCL40-C" and + category = "rule" or query = // `Query` instance for the `variablesInsideSwitchStatement` query @@ -49,7 +53,8 @@ predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `variablesInsideSwitchStatement` query "c/cert/variables-inside-switch-statement" and - ruleId = "DCL41-C" + ruleId = "DCL41-C" and + category = "rule" } module Declarations2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations3.qll index 0aa63e6dc5..22ed294ee7 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations3.qll @@ -10,14 +10,15 @@ newtype Declarations3Query = TTagNameNotUniqueQuery() or TExplicitlyDeclareTypesQuery() -predicate isDeclarations3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isDeclarations3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `identifierHidingC` query Declarations3Package::identifierHidingCQuery() and queryId = // `@id` for the `identifierHidingC` query "c/misra/identifier-hiding-c" and - ruleId = "RULE-5-3" + ruleId = "RULE-5-3" and + category = "required" or query = // `Query` instance for the `identifiersNotDistinctFromMacroNames` query @@ -25,7 +26,8 @@ predicate isDeclarations3QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `identifiersNotDistinctFromMacroNames` query "c/misra/identifiers-not-distinct-from-macro-names" and - ruleId = "RULE-5-5" + ruleId = "RULE-5-5" and + category = "required" or query = // `Query` instance for the `typedefNameNotUnique` query @@ -33,7 +35,8 @@ predicate isDeclarations3QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `typedefNameNotUnique` query "c/misra/typedef-name-not-unique" and - ruleId = "RULE-5-6" + ruleId = "RULE-5-6" and + category = "required" or query = // `Query` instance for the `tagNameNotUnique` query @@ -41,7 +44,8 @@ predicate isDeclarations3QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `tagNameNotUnique` query "c/misra/tag-name-not-unique" and - ruleId = "RULE-5-7" + ruleId = "RULE-5-7" and + category = "required" or query = // `Query` instance for the `explicitlyDeclareTypes` query @@ -49,7 +53,8 @@ predicate isDeclarations3QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `explicitlyDeclareTypes` query "c/misra/explicitly-declare-types" and - ruleId = "RULE-8-1" + ruleId = "RULE-8-1" and + category = "required" } module Declarations3Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations4.qll new file mode 100644 index 0000000000..348fd97309 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations4.qll @@ -0,0 +1,112 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations4Query = + TFunctionTypesNotInPrototypeFormQuery() or + TDeclarationsOfAnObjectSameNameAndTypeQuery() or + TDeclarationsOfAFunctionSameNameAndTypeQuery() or + TCompatibleDeclarationObjectDefinedQuery() or + TCompatibleDeclarationFunctionDefinedQuery() or + TIdentifierWithExternalLinkageOneDefinitionQuery() + +predicate isDeclarations4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `functionTypesNotInPrototypeForm` query + Declarations4Package::functionTypesNotInPrototypeFormQuery() and + queryId = + // `@id` for the `functionTypesNotInPrototypeForm` query + "c/misra/function-types-not-in-prototype-form" and + ruleId = "RULE-8-2" and + category = "required" + or + query = + // `Query` instance for the `declarationsOfAnObjectSameNameAndType` query + Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery() and + queryId = + // `@id` for the `declarationsOfAnObjectSameNameAndType` query + "c/misra/declarations-of-an-object-same-name-and-type" and + ruleId = "RULE-8-3" and + category = "required" + or + query = + // `Query` instance for the `declarationsOfAFunctionSameNameAndType` query + Declarations4Package::declarationsOfAFunctionSameNameAndTypeQuery() and + queryId = + // `@id` for the `declarationsOfAFunctionSameNameAndType` query + "c/misra/declarations-of-a-function-same-name-and-type" and + ruleId = "RULE-8-3" and + category = "required" + or + query = + // `Query` instance for the `compatibleDeclarationObjectDefined` query + Declarations4Package::compatibleDeclarationObjectDefinedQuery() and + queryId = + // `@id` for the `compatibleDeclarationObjectDefined` query + "c/misra/compatible-declaration-object-defined" and + ruleId = "RULE-8-4" and + category = "required" + or + query = + // `Query` instance for the `compatibleDeclarationFunctionDefined` query + Declarations4Package::compatibleDeclarationFunctionDefinedQuery() and + queryId = + // `@id` for the `compatibleDeclarationFunctionDefined` query + "c/misra/compatible-declaration-function-defined" and + ruleId = "RULE-8-4" and + category = "required" + or + query = + // `Query` instance for the `identifierWithExternalLinkageOneDefinition` query + Declarations4Package::identifierWithExternalLinkageOneDefinitionQuery() and + queryId = + // `@id` for the `identifierWithExternalLinkageOneDefinition` query + "c/misra/identifier-with-external-linkage-one-definition" and + ruleId = "RULE-8-6" and + category = "required" +} + +module Declarations4Package { + Query functionTypesNotInPrototypeFormQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionTypesNotInPrototypeForm` query + TQueryC(TDeclarations4PackageQuery(TFunctionTypesNotInPrototypeFormQuery())) + } + + Query declarationsOfAnObjectSameNameAndTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `declarationsOfAnObjectSameNameAndType` query + TQueryC(TDeclarations4PackageQuery(TDeclarationsOfAnObjectSameNameAndTypeQuery())) + } + + Query declarationsOfAFunctionSameNameAndTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `declarationsOfAFunctionSameNameAndType` query + TQueryC(TDeclarations4PackageQuery(TDeclarationsOfAFunctionSameNameAndTypeQuery())) + } + + Query compatibleDeclarationObjectDefinedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `compatibleDeclarationObjectDefined` query + TQueryC(TDeclarations4PackageQuery(TCompatibleDeclarationObjectDefinedQuery())) + } + + Query compatibleDeclarationFunctionDefinedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `compatibleDeclarationFunctionDefined` query + TQueryC(TDeclarations4PackageQuery(TCompatibleDeclarationFunctionDefinedQuery())) + } + + Query identifierWithExternalLinkageOneDefinitionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `identifierWithExternalLinkageOneDefinition` query + TQueryC(TDeclarations4PackageQuery(TIdentifierWithExternalLinkageOneDefinitionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll new file mode 100644 index 0000000000..b1fe8eacb6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations5Query = + TIdentifiersDeclaredInTheSameScopeNotDistinctQuery() or + TExternalObjectOrFunctionNotDeclaredInOneFileQuery() or + TMissingStaticSpecifierFunctionRedeclarationCQuery() or + TMissingStaticSpecifierObjectRedeclarationCQuery() or + TUnnecessaryExposedIdentifierDeclarationCQuery() + +predicate isDeclarations5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `identifiersDeclaredInTheSameScopeNotDistinct` query + Declarations5Package::identifiersDeclaredInTheSameScopeNotDistinctQuery() and + queryId = + // `@id` for the `identifiersDeclaredInTheSameScopeNotDistinct` query + "c/misra/identifiers-declared-in-the-same-scope-not-distinct" and + ruleId = "RULE-5-2" and + category = "required" + or + query = + // `Query` instance for the `externalObjectOrFunctionNotDeclaredInOneFile` query + Declarations5Package::externalObjectOrFunctionNotDeclaredInOneFileQuery() and + queryId = + // `@id` for the `externalObjectOrFunctionNotDeclaredInOneFile` query + "c/misra/external-object-or-function-not-declared-in-one-file" and + ruleId = "RULE-8-5" and + category = "required" + or + query = + // `Query` instance for the `missingStaticSpecifierFunctionRedeclarationC` query + Declarations5Package::missingStaticSpecifierFunctionRedeclarationCQuery() and + queryId = + // `@id` for the `missingStaticSpecifierFunctionRedeclarationC` query + "c/misra/missing-static-specifier-function-redeclaration-c" and + ruleId = "RULE-8-8" and + category = "required" + or + query = + // `Query` instance for the `missingStaticSpecifierObjectRedeclarationC` query + Declarations5Package::missingStaticSpecifierObjectRedeclarationCQuery() and + queryId = + // `@id` for the `missingStaticSpecifierObjectRedeclarationC` query + "c/misra/missing-static-specifier-object-redeclaration-c" and + ruleId = "RULE-8-8" and + category = "required" + or + query = + // `Query` instance for the `unnecessaryExposedIdentifierDeclarationC` query + Declarations5Package::unnecessaryExposedIdentifierDeclarationCQuery() and + queryId = + // `@id` for the `unnecessaryExposedIdentifierDeclarationC` query + "c/misra/unnecessary-exposed-identifier-declaration-c" and + ruleId = "RULE-8-9" and + category = "advisory" +} + +module Declarations5Package { + Query identifiersDeclaredInTheSameScopeNotDistinctQuery() { + //autogenerate `Query` type + result = + // `Query` type for `identifiersDeclaredInTheSameScopeNotDistinct` query + TQueryC(TDeclarations5PackageQuery(TIdentifiersDeclaredInTheSameScopeNotDistinctQuery())) + } + + Query externalObjectOrFunctionNotDeclaredInOneFileQuery() { + //autogenerate `Query` type + result = + // `Query` type for `externalObjectOrFunctionNotDeclaredInOneFile` query + TQueryC(TDeclarations5PackageQuery(TExternalObjectOrFunctionNotDeclaredInOneFileQuery())) + } + + Query missingStaticSpecifierFunctionRedeclarationCQuery() { + //autogenerate `Query` type + result = + // `Query` type for `missingStaticSpecifierFunctionRedeclarationC` query + TQueryC(TDeclarations5PackageQuery(TMissingStaticSpecifierFunctionRedeclarationCQuery())) + } + + Query missingStaticSpecifierObjectRedeclarationCQuery() { + //autogenerate `Query` type + result = + // `Query` type for `missingStaticSpecifierObjectRedeclarationC` query + TQueryC(TDeclarations5PackageQuery(TMissingStaticSpecifierObjectRedeclarationCQuery())) + } + + Query unnecessaryExposedIdentifierDeclarationCQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unnecessaryExposedIdentifierDeclarationC` query + TQueryC(TDeclarations5PackageQuery(TUnnecessaryExposedIdentifierDeclarationCQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations6.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations6.qll new file mode 100644 index 0000000000..b9db9f986f --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations6.qll @@ -0,0 +1,129 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations6Query = + TFunctionDeclaredImplicitlyQuery() or + TFlexibleArrayMembersDeclaredQuery() or + TIdentifiersWithExternalLinkageNotUniqueQuery() or + TIdentifiersWithInternalLinkageNotUniqueQuery() or + TInlineFunctionNotDeclaredStaticStorageQuery() or + TArrayExternalLinkageSizeExplicitlySpecifiedQuery() or + TShouldNotBeDefinedWithExternalLinkageQuery() + +predicate isDeclarations6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `functionDeclaredImplicitly` query + Declarations6Package::functionDeclaredImplicitlyQuery() and + queryId = + // `@id` for the `functionDeclaredImplicitly` query + "c/misra/function-declared-implicitly" and + ruleId = "RULE-17-3" and + category = "mandatory" + or + query = + // `Query` instance for the `flexibleArrayMembersDeclared` query + Declarations6Package::flexibleArrayMembersDeclaredQuery() and + queryId = + // `@id` for the `flexibleArrayMembersDeclared` query + "c/misra/flexible-array-members-declared" and + ruleId = "RULE-18-7" and + category = "required" + or + query = + // `Query` instance for the `identifiersWithExternalLinkageNotUnique` query + Declarations6Package::identifiersWithExternalLinkageNotUniqueQuery() and + queryId = + // `@id` for the `identifiersWithExternalLinkageNotUnique` query + "c/misra/identifiers-with-external-linkage-not-unique" and + ruleId = "RULE-5-8" and + category = "required" + or + query = + // `Query` instance for the `identifiersWithInternalLinkageNotUnique` query + Declarations6Package::identifiersWithInternalLinkageNotUniqueQuery() and + queryId = + // `@id` for the `identifiersWithInternalLinkageNotUnique` query + "c/misra/identifiers-with-internal-linkage-not-unique" and + ruleId = "RULE-5-9" and + category = "advisory" + or + query = + // `Query` instance for the `inlineFunctionNotDeclaredStaticStorage` query + Declarations6Package::inlineFunctionNotDeclaredStaticStorageQuery() and + queryId = + // `@id` for the `inlineFunctionNotDeclaredStaticStorage` query + "c/misra/inline-function-not-declared-static-storage" and + ruleId = "RULE-8-10" and + category = "required" + or + query = + // `Query` instance for the `arrayExternalLinkageSizeExplicitlySpecified` query + Declarations6Package::arrayExternalLinkageSizeExplicitlySpecifiedQuery() and + queryId = + // `@id` for the `arrayExternalLinkageSizeExplicitlySpecified` query + "c/misra/array-external-linkage-size-explicitly-specified" and + ruleId = "RULE-8-11" and + category = "advisory" + or + query = + // `Query` instance for the `shouldNotBeDefinedWithExternalLinkage` query + Declarations6Package::shouldNotBeDefinedWithExternalLinkageQuery() and + queryId = + // `@id` for the `shouldNotBeDefinedWithExternalLinkage` query + "c/misra/should-not-be-defined-with-external-linkage" and + ruleId = "RULE-8-7" and + category = "advisory" +} + +module Declarations6Package { + Query functionDeclaredImplicitlyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionDeclaredImplicitly` query + TQueryC(TDeclarations6PackageQuery(TFunctionDeclaredImplicitlyQuery())) + } + + Query flexibleArrayMembersDeclaredQuery() { + //autogenerate `Query` type + result = + // `Query` type for `flexibleArrayMembersDeclared` query + TQueryC(TDeclarations6PackageQuery(TFlexibleArrayMembersDeclaredQuery())) + } + + Query identifiersWithExternalLinkageNotUniqueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `identifiersWithExternalLinkageNotUnique` query + TQueryC(TDeclarations6PackageQuery(TIdentifiersWithExternalLinkageNotUniqueQuery())) + } + + Query identifiersWithInternalLinkageNotUniqueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `identifiersWithInternalLinkageNotUnique` query + TQueryC(TDeclarations6PackageQuery(TIdentifiersWithInternalLinkageNotUniqueQuery())) + } + + Query inlineFunctionNotDeclaredStaticStorageQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inlineFunctionNotDeclaredStaticStorage` query + TQueryC(TDeclarations6PackageQuery(TInlineFunctionNotDeclaredStaticStorageQuery())) + } + + Query arrayExternalLinkageSizeExplicitlySpecifiedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `arrayExternalLinkageSizeExplicitlySpecified` query + TQueryC(TDeclarations6PackageQuery(TArrayExternalLinkageSizeExplicitlySpecifiedQuery())) + } + + Query shouldNotBeDefinedWithExternalLinkageQuery() { + //autogenerate `Query` type + result = + // `Query` type for `shouldNotBeDefinedWithExternalLinkage` query + TQueryC(TDeclarations6PackageQuery(TShouldNotBeDefinedWithExternalLinkageQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations7.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations7.qll new file mode 100644 index 0000000000..facb651573 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations7.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations7Query = + TInformationLeakageAcrossTrustBoundariesCQuery() or + TVariableLengthArrayTypesUsedQuery() or + TValueImplicitEnumerationConstantNotUniqueQuery() + +predicate isDeclarations7QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `informationLeakageAcrossTrustBoundariesC` query + Declarations7Package::informationLeakageAcrossTrustBoundariesCQuery() and + queryId = + // `@id` for the `informationLeakageAcrossTrustBoundariesC` query + "c/cert/information-leakage-across-trust-boundaries-c" and + ruleId = "DCL39-C" and + category = "rule" + or + query = + // `Query` instance for the `variableLengthArrayTypesUsed` query + Declarations7Package::variableLengthArrayTypesUsedQuery() and + queryId = + // `@id` for the `variableLengthArrayTypesUsed` query + "c/misra/variable-length-array-types-used" and + ruleId = "RULE-18-8" and + category = "required" + or + query = + // `Query` instance for the `valueImplicitEnumerationConstantNotUnique` query + Declarations7Package::valueImplicitEnumerationConstantNotUniqueQuery() and + queryId = + // `@id` for the `valueImplicitEnumerationConstantNotUnique` query + "c/misra/value-implicit-enumeration-constant-not-unique" and + ruleId = "RULE-8-12" and + category = "required" +} + +module Declarations7Package { + Query informationLeakageAcrossTrustBoundariesCQuery() { + //autogenerate `Query` type + result = + // `Query` type for `informationLeakageAcrossTrustBoundariesC` query + TQueryC(TDeclarations7PackageQuery(TInformationLeakageAcrossTrustBoundariesCQuery())) + } + + Query variableLengthArrayTypesUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `variableLengthArrayTypesUsed` query + TQueryC(TDeclarations7PackageQuery(TVariableLengthArrayTypesUsedQuery())) + } + + Query valueImplicitEnumerationConstantNotUniqueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `valueImplicitEnumerationConstantNotUnique` query + TQueryC(TDeclarations7PackageQuery(TValueImplicitEnumerationConstantNotUniqueQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations8.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations8.qll new file mode 100644 index 0000000000..767373b1c2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations8.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations8Query = + TAppropriateStorageDurationsStackAdressEscapeQuery() or + TAppropriateStorageDurationsFunctionReturnQuery() + +predicate isDeclarations8QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `appropriateStorageDurationsStackAdressEscape` query + Declarations8Package::appropriateStorageDurationsStackAdressEscapeQuery() and + queryId = + // `@id` for the `appropriateStorageDurationsStackAdressEscape` query + "c/cert/appropriate-storage-durations-stack-adress-escape" and + ruleId = "DCL30-C" and + category = "rule" + or + query = + // `Query` instance for the `appropriateStorageDurationsFunctionReturn` query + Declarations8Package::appropriateStorageDurationsFunctionReturnQuery() and + queryId = + // `@id` for the `appropriateStorageDurationsFunctionReturn` query + "c/cert/appropriate-storage-durations-function-return" and + ruleId = "DCL30-C" and + category = "rule" +} + +module Declarations8Package { + Query appropriateStorageDurationsStackAdressEscapeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `appropriateStorageDurationsStackAdressEscape` query + TQueryC(TDeclarations8PackageQuery(TAppropriateStorageDurationsStackAdressEscapeQuery())) + } + + Query appropriateStorageDurationsFunctionReturnQuery() { + //autogenerate `Query` type + result = + // `Query` type for `appropriateStorageDurationsFunctionReturn` query + TQueryC(TDeclarations8PackageQuery(TAppropriateStorageDurationsFunctionReturnQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations9.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations9.qll new file mode 100644 index 0000000000..8a63e50ed4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations9.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations9Query = TAtomicQualifierAppliedToVoidQuery() + +predicate isDeclarations9QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `atomicQualifierAppliedToVoid` query + Declarations9Package::atomicQualifierAppliedToVoidQuery() and + queryId = + // `@id` for the `atomicQualifierAppliedToVoid` query + "c/misra/atomic-qualifier-applied-to-void" and + ruleId = "RULE-11-10" and + category = "required" +} + +module Declarations9Package { + Query atomicQualifierAppliedToVoidQuery() { + //autogenerate `Query` type + result = + // `Query` type for `atomicQualifierAppliedToVoid` query + TQueryC(TDeclarations9PackageQuery(TAtomicQualifierAppliedToVoidQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/EssentialTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/EssentialTypes.qll new file mode 100644 index 0000000000..a29e113146 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/EssentialTypes.qll @@ -0,0 +1,214 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype EssentialTypesQuery = + TOperandsOfAnInappropriateEssentialTypeQuery() or + TPointerTypeOnLogicalOperatorQuery() or + TAdditionSubtractionOnEssentiallyCharTypeQuery() or + TAssignmentOfIncompatibleEssentialTypeQuery() or + TOperandsWithMismatchedEssentialTypeCategoryQuery() or + TInappropriateEssentialTypeCastQuery() or + TAssignmentToWiderEssentialTypeQuery() or + TImplicitConversionOfCompositeExpressionQuery() or + TInappropriateCastOfCompositeExpressionQuery() or + TLoopOverEssentiallyFloatTypeQuery() or + TMemcmpUsedToCompareNullTerminatedStringsQuery() or + TMemcmpOnInappropriateEssentialTypeArgsQuery() + +predicate isEssentialTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `operandsOfAnInappropriateEssentialType` query + EssentialTypesPackage::operandsOfAnInappropriateEssentialTypeQuery() and + queryId = + // `@id` for the `operandsOfAnInappropriateEssentialType` query + "c/misra/operands-of-an-inappropriate-essential-type" and + ruleId = "RULE-10-1" and + category = "required" + or + query = + // `Query` instance for the `pointerTypeOnLogicalOperator` query + EssentialTypesPackage::pointerTypeOnLogicalOperatorQuery() and + queryId = + // `@id` for the `pointerTypeOnLogicalOperator` query + "c/misra/pointer-type-on-logical-operator" and + ruleId = "RULE-10-1" and + category = "required" + or + query = + // `Query` instance for the `additionSubtractionOnEssentiallyCharType` query + EssentialTypesPackage::additionSubtractionOnEssentiallyCharTypeQuery() and + queryId = + // `@id` for the `additionSubtractionOnEssentiallyCharType` query + "c/misra/addition-subtraction-on-essentially-char-type" and + ruleId = "RULE-10-2" and + category = "required" + or + query = + // `Query` instance for the `assignmentOfIncompatibleEssentialType` query + EssentialTypesPackage::assignmentOfIncompatibleEssentialTypeQuery() and + queryId = + // `@id` for the `assignmentOfIncompatibleEssentialType` query + "c/misra/assignment-of-incompatible-essential-type" and + ruleId = "RULE-10-3" and + category = "required" + or + query = + // `Query` instance for the `operandsWithMismatchedEssentialTypeCategory` query + EssentialTypesPackage::operandsWithMismatchedEssentialTypeCategoryQuery() and + queryId = + // `@id` for the `operandsWithMismatchedEssentialTypeCategory` query + "c/misra/operands-with-mismatched-essential-type-category" and + ruleId = "RULE-10-4" and + category = "required" + or + query = + // `Query` instance for the `inappropriateEssentialTypeCast` query + EssentialTypesPackage::inappropriateEssentialTypeCastQuery() and + queryId = + // `@id` for the `inappropriateEssentialTypeCast` query + "c/misra/inappropriate-essential-type-cast" and + ruleId = "RULE-10-5" and + category = "advisory" + or + query = + // `Query` instance for the `assignmentToWiderEssentialType` query + EssentialTypesPackage::assignmentToWiderEssentialTypeQuery() and + queryId = + // `@id` for the `assignmentToWiderEssentialType` query + "c/misra/assignment-to-wider-essential-type" and + ruleId = "RULE-10-6" and + category = "required" + or + query = + // `Query` instance for the `implicitConversionOfCompositeExpression` query + EssentialTypesPackage::implicitConversionOfCompositeExpressionQuery() and + queryId = + // `@id` for the `implicitConversionOfCompositeExpression` query + "c/misra/implicit-conversion-of-composite-expression" and + ruleId = "RULE-10-7" and + category = "required" + or + query = + // `Query` instance for the `inappropriateCastOfCompositeExpression` query + EssentialTypesPackage::inappropriateCastOfCompositeExpressionQuery() and + queryId = + // `@id` for the `inappropriateCastOfCompositeExpression` query + "c/misra/inappropriate-cast-of-composite-expression" and + ruleId = "RULE-10-8" and + category = "required" + or + query = + // `Query` instance for the `loopOverEssentiallyFloatType` query + EssentialTypesPackage::loopOverEssentiallyFloatTypeQuery() and + queryId = + // `@id` for the `loopOverEssentiallyFloatType` query + "c/misra/loop-over-essentially-float-type" and + ruleId = "RULE-14-1" and + category = "required" + or + query = + // `Query` instance for the `memcmpUsedToCompareNullTerminatedStrings` query + EssentialTypesPackage::memcmpUsedToCompareNullTerminatedStringsQuery() and + queryId = + // `@id` for the `memcmpUsedToCompareNullTerminatedStrings` query + "c/misra/memcmp-used-to-compare-null-terminated-strings" and + ruleId = "RULE-21-14" and + category = "required" + or + query = + // `Query` instance for the `memcmpOnInappropriateEssentialTypeArgs` query + EssentialTypesPackage::memcmpOnInappropriateEssentialTypeArgsQuery() and + queryId = + // `@id` for the `memcmpOnInappropriateEssentialTypeArgs` query + "c/misra/memcmp-on-inappropriate-essential-type-args" and + ruleId = "RULE-21-16" and + category = "required" +} + +module EssentialTypesPackage { + Query operandsOfAnInappropriateEssentialTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `operandsOfAnInappropriateEssentialType` query + TQueryC(TEssentialTypesPackageQuery(TOperandsOfAnInappropriateEssentialTypeQuery())) + } + + Query pointerTypeOnLogicalOperatorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointerTypeOnLogicalOperator` query + TQueryC(TEssentialTypesPackageQuery(TPointerTypeOnLogicalOperatorQuery())) + } + + Query additionSubtractionOnEssentiallyCharTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `additionSubtractionOnEssentiallyCharType` query + TQueryC(TEssentialTypesPackageQuery(TAdditionSubtractionOnEssentiallyCharTypeQuery())) + } + + Query assignmentOfIncompatibleEssentialTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `assignmentOfIncompatibleEssentialType` query + TQueryC(TEssentialTypesPackageQuery(TAssignmentOfIncompatibleEssentialTypeQuery())) + } + + Query operandsWithMismatchedEssentialTypeCategoryQuery() { + //autogenerate `Query` type + result = + // `Query` type for `operandsWithMismatchedEssentialTypeCategory` query + TQueryC(TEssentialTypesPackageQuery(TOperandsWithMismatchedEssentialTypeCategoryQuery())) + } + + Query inappropriateEssentialTypeCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inappropriateEssentialTypeCast` query + TQueryC(TEssentialTypesPackageQuery(TInappropriateEssentialTypeCastQuery())) + } + + Query assignmentToWiderEssentialTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `assignmentToWiderEssentialType` query + TQueryC(TEssentialTypesPackageQuery(TAssignmentToWiderEssentialTypeQuery())) + } + + Query implicitConversionOfCompositeExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `implicitConversionOfCompositeExpression` query + TQueryC(TEssentialTypesPackageQuery(TImplicitConversionOfCompositeExpressionQuery())) + } + + Query inappropriateCastOfCompositeExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inappropriateCastOfCompositeExpression` query + TQueryC(TEssentialTypesPackageQuery(TInappropriateCastOfCompositeExpressionQuery())) + } + + Query loopOverEssentiallyFloatTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `loopOverEssentiallyFloatType` query + TQueryC(TEssentialTypesPackageQuery(TLoopOverEssentiallyFloatTypeQuery())) + } + + Query memcmpUsedToCompareNullTerminatedStringsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memcmpUsedToCompareNullTerminatedStrings` query + TQueryC(TEssentialTypesPackageQuery(TMemcmpUsedToCompareNullTerminatedStringsQuery())) + } + + Query memcmpOnInappropriateEssentialTypeArgsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memcmpOnInappropriateEssentialTypeArgs` query + TQueryC(TEssentialTypesPackageQuery(TMemcmpOnInappropriateEssentialTypeArgsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/EssentialTypes2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/EssentialTypes2.qll new file mode 100644 index 0000000000..e1dd8d5636 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/EssentialTypes2.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype EssentialTypes2Query = + TTgMathArgumentWithInvalidEssentialTypeQuery() or + TTgMathArgumentsWithDifferingStandardTypeQuery() + +predicate isEssentialTypes2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `tgMathArgumentWithInvalidEssentialType` query + EssentialTypes2Package::tgMathArgumentWithInvalidEssentialTypeQuery() and + queryId = + // `@id` for the `tgMathArgumentWithInvalidEssentialType` query + "c/misra/tg-math-argument-with-invalid-essential-type" and + ruleId = "RULE-21-22" and + category = "mandatory" + or + query = + // `Query` instance for the `tgMathArgumentsWithDifferingStandardType` query + EssentialTypes2Package::tgMathArgumentsWithDifferingStandardTypeQuery() and + queryId = + // `@id` for the `tgMathArgumentsWithDifferingStandardType` query + "c/misra/tg-math-arguments-with-differing-standard-type" and + ruleId = "RULE-21-23" and + category = "required" +} + +module EssentialTypes2Package { + Query tgMathArgumentWithInvalidEssentialTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `tgMathArgumentWithInvalidEssentialType` query + TQueryC(TEssentialTypes2PackageQuery(TTgMathArgumentWithInvalidEssentialTypeQuery())) + } + + Query tgMathArgumentsWithDifferingStandardTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `tgMathArgumentsWithDifferingStandardType` query + TQueryC(TEssentialTypes2PackageQuery(TTgMathArgumentsWithDifferingStandardTypeQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll index 2e18feca23..fab8e21d01 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll @@ -9,14 +9,15 @@ newtype ExpressionsQuery = TCallPOSIXOpenWithCorrectArgumentCountQuery() or TDoNotUseABitwiseOperatorWithABooleanLikeOperandQuery() -predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotCallFunctionPointerWithIncompatibleType` query ExpressionsPackage::doNotCallFunctionPointerWithIncompatibleTypeQuery() and queryId = // `@id` for the `doNotCallFunctionPointerWithIncompatibleType` query "c/cert/do-not-call-function-pointer-with-incompatible-type" and - ruleId = "EXP37-C" + ruleId = "EXP37-C" and + category = "rule" or query = // `Query` instance for the `doNotCallFunctionsWithIncompatibleArguments` query @@ -24,7 +25,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotCallFunctionsWithIncompatibleArguments` query "c/cert/do-not-call-functions-with-incompatible-arguments" and - ruleId = "EXP37-C" + ruleId = "EXP37-C" and + category = "rule" or query = // `Query` instance for the `callPOSIXOpenWithCorrectArgumentCount` query @@ -32,7 +34,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `callPOSIXOpenWithCorrectArgumentCount` query "c/cert/call-posix-open-with-correct-argument-count" and - ruleId = "EXP37-C" + ruleId = "EXP37-C" and + category = "rule" or query = // `Query` instance for the `doNotUseABitwiseOperatorWithABooleanLikeOperand` query @@ -40,7 +43,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotUseABitwiseOperatorWithABooleanLikeOperand` query "c/cert/do-not-use-a-bitwise-operator-with-a-boolean-like-operand" and - ruleId = "EXP46-C" + ruleId = "EXP46-C" and + category = "rule" } module ExpressionsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll new file mode 100644 index 0000000000..e7dffc30bb --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Expressions2Query = TDoNotCompareFunctionPointersToConstantValuesQuery() + +predicate isExpressions2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotCompareFunctionPointersToConstantValues` query + Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery() and + queryId = + // `@id` for the `doNotCompareFunctionPointersToConstantValues` query + "c/cert/do-not-compare-function-pointers-to-constant-values" and + ruleId = "EXP16-C" and + category = "recommendation" +} + +module Expressions2Package { + Query doNotCompareFunctionPointersToConstantValuesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCompareFunctionPointersToConstantValues` query + TQueryC(TExpressions2PackageQuery(TDoNotCompareFunctionPointersToConstantValuesQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/FloatingTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/FloatingTypes.qll new file mode 100644 index 0000000000..2f39b98177 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/FloatingTypes.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype FloatingTypesQuery = + TUncheckedRangeDomainPoleErrorsQuery() or + TUncheckedFloatingPointConversionQuery() or + TIntToFloatPreservePrecisionQuery() or + TMemcmpUsedToCompareFloatsQuery() + +predicate isFloatingTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `uncheckedRangeDomainPoleErrors` query + FloatingTypesPackage::uncheckedRangeDomainPoleErrorsQuery() and + queryId = + // `@id` for the `uncheckedRangeDomainPoleErrors` query + "c/cert/unchecked-range-domain-pole-errors" and + ruleId = "FLP32-C" and + category = "rule" + or + query = + // `Query` instance for the `uncheckedFloatingPointConversion` query + FloatingTypesPackage::uncheckedFloatingPointConversionQuery() and + queryId = + // `@id` for the `uncheckedFloatingPointConversion` query + "c/cert/unchecked-floating-point-conversion" and + ruleId = "FLP34-C" and + category = "rule" + or + query = + // `Query` instance for the `intToFloatPreservePrecision` query + FloatingTypesPackage::intToFloatPreservePrecisionQuery() and + queryId = + // `@id` for the `intToFloatPreservePrecision` query + "c/cert/int-to-float-preserve-precision" and + ruleId = "FLP36-C" and + category = "rule" + or + query = + // `Query` instance for the `memcmpUsedToCompareFloats` query + FloatingTypesPackage::memcmpUsedToCompareFloatsQuery() and + queryId = + // `@id` for the `memcmpUsedToCompareFloats` query + "c/cert/memcmp-used-to-compare-floats" and + ruleId = "FLP37-C" and + category = "rule" +} + +module FloatingTypesPackage { + Query uncheckedRangeDomainPoleErrorsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `uncheckedRangeDomainPoleErrors` query + TQueryC(TFloatingTypesPackageQuery(TUncheckedRangeDomainPoleErrorsQuery())) + } + + Query uncheckedFloatingPointConversionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `uncheckedFloatingPointConversion` query + TQueryC(TFloatingTypesPackageQuery(TUncheckedFloatingPointConversionQuery())) + } + + Query intToFloatPreservePrecisionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `intToFloatPreservePrecision` query + TQueryC(TFloatingTypesPackageQuery(TIntToFloatPreservePrecisionQuery())) + } + + Query memcmpUsedToCompareFloatsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memcmpUsedToCompareFloats` query + TQueryC(TFloatingTypesPackageQuery(TMemcmpUsedToCompareFloatsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/FloatingTypes2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/FloatingTypes2.qll new file mode 100644 index 0000000000..0dbc6cc22d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/FloatingTypes2.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype FloatingTypes2Query = + TPossibleMisuseOfUndetectedInfinityQuery() or + TPossibleMisuseOfUndetectedNaNQuery() + +predicate isFloatingTypes2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `possibleMisuseOfUndetectedInfinity` query + FloatingTypes2Package::possibleMisuseOfUndetectedInfinityQuery() and + queryId = + // `@id` for the `possibleMisuseOfUndetectedInfinity` query + "c/misra/possible-misuse-of-undetected-infinity" and + ruleId = "DIR-4-15" and + category = "required" + or + query = + // `Query` instance for the `possibleMisuseOfUndetectedNaN` query + FloatingTypes2Package::possibleMisuseOfUndetectedNaNQuery() and + queryId = + // `@id` for the `possibleMisuseOfUndetectedNaN` query + "c/misra/possible-misuse-of-undetected-nan" and + ruleId = "DIR-4-15" and + category = "required" +} + +module FloatingTypes2Package { + Query possibleMisuseOfUndetectedInfinityQuery() { + //autogenerate `Query` type + result = + // `Query` type for `possibleMisuseOfUndetectedInfinity` query + TQueryC(TFloatingTypes2PackageQuery(TPossibleMisuseOfUndetectedInfinityQuery())) + } + + Query possibleMisuseOfUndetectedNaNQuery() { + //autogenerate `Query` type + result = + // `Query` type for `possibleMisuseOfUndetectedNaN` query + TQueryC(TFloatingTypes2PackageQuery(TPossibleMisuseOfUndetectedNaNQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll new file mode 100644 index 0000000000..3d6faadb42 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype FunctionTypesQuery = TFunctionAddressesShouldAddressOperatorQuery() + +predicate isFunctionTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `functionAddressesShouldAddressOperator` query + FunctionTypesPackage::functionAddressesShouldAddressOperatorQuery() and + queryId = + // `@id` for the `functionAddressesShouldAddressOperator` query + "c/misra/function-addresses-should-address-operator" and + ruleId = "RULE-17-12" and + category = "advisory" +} + +module FunctionTypesPackage { + Query functionAddressesShouldAddressOperatorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionAddressesShouldAddressOperator` query + TQueryC(TFunctionTypesPackageQuery(TFunctionAddressesShouldAddressOperatorQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Generics.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Generics.qll new file mode 100644 index 0000000000..05e3402c19 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Generics.qll @@ -0,0 +1,163 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype GenericsQuery = + TGenericSelectionNotExpandedFromAMacroQuery() or + TGenericSelectionDoesntDependOnMacroArgumentQuery() or + TGenericSelectionNotFromMacroWithSideEffectsQuery() or + TGenericWithoutNonDefaultAssociationQuery() or + TGenericAssociationWithUnselectableTypeQuery() or + TDangerousDefaultSelectionForPointerInGenericQuery() or + TGenericExpressionWithIncorrectEssentialTypeQuery() or + TInvalidGenericMacroArgumentEvaluationQuery() or + TDefaultGenericSelectionNotFirstOrLastQuery() + +predicate isGenericsQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `genericSelectionNotExpandedFromAMacro` query + GenericsPackage::genericSelectionNotExpandedFromAMacroQuery() and + queryId = + // `@id` for the `genericSelectionNotExpandedFromAMacro` query + "c/misra/generic-selection-not-expanded-from-a-macro" and + ruleId = "RULE-23-1" and + category = "advisory" + or + query = + // `Query` instance for the `genericSelectionDoesntDependOnMacroArgument` query + GenericsPackage::genericSelectionDoesntDependOnMacroArgumentQuery() and + queryId = + // `@id` for the `genericSelectionDoesntDependOnMacroArgument` query + "c/misra/generic-selection-doesnt-depend-on-macro-argument" and + ruleId = "RULE-23-1" and + category = "advisory" + or + query = + // `Query` instance for the `genericSelectionNotFromMacroWithSideEffects` query + GenericsPackage::genericSelectionNotFromMacroWithSideEffectsQuery() and + queryId = + // `@id` for the `genericSelectionNotFromMacroWithSideEffects` query + "c/misra/generic-selection-not-from-macro-with-side-effects" and + ruleId = "RULE-23-2" and + category = "required" + or + query = + // `Query` instance for the `genericWithoutNonDefaultAssociation` query + GenericsPackage::genericWithoutNonDefaultAssociationQuery() and + queryId = + // `@id` for the `genericWithoutNonDefaultAssociation` query + "c/misra/generic-without-non-default-association" and + ruleId = "RULE-23-3" and + category = "advisory" + or + query = + // `Query` instance for the `genericAssociationWithUnselectableType` query + GenericsPackage::genericAssociationWithUnselectableTypeQuery() and + queryId = + // `@id` for the `genericAssociationWithUnselectableType` query + "c/misra/generic-association-with-unselectable-type" and + ruleId = "RULE-23-4" and + category = "required" + or + query = + // `Query` instance for the `dangerousDefaultSelectionForPointerInGeneric` query + GenericsPackage::dangerousDefaultSelectionForPointerInGenericQuery() and + queryId = + // `@id` for the `dangerousDefaultSelectionForPointerInGeneric` query + "c/misra/dangerous-default-selection-for-pointer-in-generic" and + ruleId = "RULE-23-5" and + category = "advisory" + or + query = + // `Query` instance for the `genericExpressionWithIncorrectEssentialType` query + GenericsPackage::genericExpressionWithIncorrectEssentialTypeQuery() and + queryId = + // `@id` for the `genericExpressionWithIncorrectEssentialType` query + "c/misra/generic-expression-with-incorrect-essential-type" and + ruleId = "RULE-23-6" and + category = "required" + or + query = + // `Query` instance for the `invalidGenericMacroArgumentEvaluation` query + GenericsPackage::invalidGenericMacroArgumentEvaluationQuery() and + queryId = + // `@id` for the `invalidGenericMacroArgumentEvaluation` query + "c/misra/invalid-generic-macro-argument-evaluation" and + ruleId = "RULE-23-7" and + category = "advisory" + or + query = + // `Query` instance for the `defaultGenericSelectionNotFirstOrLast` query + GenericsPackage::defaultGenericSelectionNotFirstOrLastQuery() and + queryId = + // `@id` for the `defaultGenericSelectionNotFirstOrLast` query + "c/misra/default-generic-selection-not-first-or-last" and + ruleId = "RULE-23-8" and + category = "required" +} + +module GenericsPackage { + Query genericSelectionNotExpandedFromAMacroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `genericSelectionNotExpandedFromAMacro` query + TQueryC(TGenericsPackageQuery(TGenericSelectionNotExpandedFromAMacroQuery())) + } + + Query genericSelectionDoesntDependOnMacroArgumentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `genericSelectionDoesntDependOnMacroArgument` query + TQueryC(TGenericsPackageQuery(TGenericSelectionDoesntDependOnMacroArgumentQuery())) + } + + Query genericSelectionNotFromMacroWithSideEffectsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `genericSelectionNotFromMacroWithSideEffects` query + TQueryC(TGenericsPackageQuery(TGenericSelectionNotFromMacroWithSideEffectsQuery())) + } + + Query genericWithoutNonDefaultAssociationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `genericWithoutNonDefaultAssociation` query + TQueryC(TGenericsPackageQuery(TGenericWithoutNonDefaultAssociationQuery())) + } + + Query genericAssociationWithUnselectableTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `genericAssociationWithUnselectableType` query + TQueryC(TGenericsPackageQuery(TGenericAssociationWithUnselectableTypeQuery())) + } + + Query dangerousDefaultSelectionForPointerInGenericQuery() { + //autogenerate `Query` type + result = + // `Query` type for `dangerousDefaultSelectionForPointerInGeneric` query + TQueryC(TGenericsPackageQuery(TDangerousDefaultSelectionForPointerInGenericQuery())) + } + + Query genericExpressionWithIncorrectEssentialTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `genericExpressionWithIncorrectEssentialType` query + TQueryC(TGenericsPackageQuery(TGenericExpressionWithIncorrectEssentialTypeQuery())) + } + + Query invalidGenericMacroArgumentEvaluationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidGenericMacroArgumentEvaluation` query + TQueryC(TGenericsPackageQuery(TInvalidGenericMacroArgumentEvaluationQuery())) + } + + Query defaultGenericSelectionNotFirstOrLastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `defaultGenericSelectionNotFirstOrLast` query + TQueryC(TGenericsPackageQuery(TDefaultGenericSelectionNotFirstOrLastQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO1.qll index f0aa85cd8a..b3c1bdd428 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/IO1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO1.qll @@ -12,14 +12,15 @@ newtype IO1Query = TUndefinedBehaviorAccessingAClosedFileQuery() or TFileUsedAfterClosedQuery() -predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isIO1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `excludeUserInputFromFormatStrings` query IO1Package::excludeUserInputFromFormatStringsQuery() and queryId = // `@id` for the `excludeUserInputFromFormatStrings` query "c/cert/exclude-user-input-from-format-strings" and - ruleId = "FIO30-C" + ruleId = "FIO30-C" and + category = "rule" or query = // `Query` instance for the `distinguishBetweenCharReadFromAFileAndEofOrWeof` query @@ -27,7 +28,8 @@ predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `distinguishBetweenCharReadFromAFileAndEofOrWeof` query "c/cert/distinguish-between-char-read-from-a-file-and-eof-or-weof" and - ruleId = "FIO34-C" + ruleId = "FIO34-C" and + category = "rule" or query = // `Query` instance for the `endOfFileCheckPortability` query @@ -35,7 +37,8 @@ predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `endOfFileCheckPortability` query "c/cert/end-of-file-check-portability" and - ruleId = "FIO34-C" + ruleId = "FIO34-C" and + category = "rule" or query = // `Query` instance for the `doNotAlternatelyIOFromAStreamWithoutPositioning` query @@ -43,7 +46,8 @@ predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotAlternatelyIOFromAStreamWithoutPositioning` query "c/cert/do-not-alternately-io-from-a-stream-without-positioning" and - ruleId = "FIO39-C" + ruleId = "FIO39-C" and + category = "rule" or query = // `Query` instance for the `closeFilesWhenTheyAreNoLongerNeeded` query @@ -51,7 +55,8 @@ predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `closeFilesWhenTheyAreNoLongerNeeded` query "c/cert/close-files-when-they-are-no-longer-needed" and - ruleId = "FIO42-C" + ruleId = "FIO42-C" and + category = "rule" or query = // `Query` instance for the `undefinedBehaviorAccessingAClosedFile` query @@ -59,7 +64,8 @@ predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `undefinedBehaviorAccessingAClosedFile` query "c/cert/undefined-behavior-accessing-a-closed-file" and - ruleId = "FIO46-C" + ruleId = "FIO46-C" and + category = "rule" or query = // `Query` instance for the `fileUsedAfterClosed` query @@ -67,7 +73,8 @@ predicate isIO1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `fileUsedAfterClosed` query "c/misra/file-used-after-closed" and - ruleId = "RULE-22-6" + ruleId = "RULE-22-6" and + category = "mandatory" } module IO1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO2.qll index 6eca59ea3c..6208470e24 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/IO2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO2.qll @@ -9,14 +9,15 @@ newtype IO2Query = TDoNotCallGetcAndPutcWithSideEffectsQuery() or TOnlyUseValuesForFsetposThatAreReturnedFromFgetposQuery() -predicate isIO2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isIO2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotCopyAFileObject` query IO2Package::doNotCopyAFileObjectQuery() and queryId = // `@id` for the `doNotCopyAFileObject` query "c/cert/do-not-copy-a-file-object" and - ruleId = "FIO38-C" + ruleId = "FIO38-C" and + category = "rule" or query = // `Query` instance for the `resetStringsOnFgetsOrFgetwsFailure` query @@ -24,7 +25,8 @@ predicate isIO2QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `resetStringsOnFgetsOrFgetwsFailure` query "c/cert/reset-strings-on-fgets-or-fgetws-failure" and - ruleId = "FIO40-C" + ruleId = "FIO40-C" and + category = "rule" or query = // `Query` instance for the `doNotCallGetcAndPutcWithSideEffects` query @@ -32,7 +34,8 @@ predicate isIO2QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotCallGetcAndPutcWithSideEffects` query "c/cert/do-not-call-getc-and-putc-with-side-effects" and - ruleId = "FIO41-C" + ruleId = "FIO41-C" and + category = "rule" or query = // `Query` instance for the `onlyUseValuesForFsetposThatAreReturnedFromFgetpos` query @@ -40,7 +43,8 @@ predicate isIO2QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `onlyUseValuesForFsetposThatAreReturnedFromFgetpos` query "c/cert/only-use-values-for-fsetpos-that-are-returned-from-fgetpos" and - ruleId = "FIO44-C" + ruleId = "FIO44-C" and + category = "rule" } module IO2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO3.qll index b04ecbf420..9aac753c0b 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/IO3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO3.qll @@ -11,14 +11,15 @@ newtype IO3Query = TPointerToAFileObjectDereferencedQuery() or TEofShallBeComparedWithUnmodifiedReturnValuesQuery() -predicate isIO3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isIO3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotPerformFileOperationsOnDevices` query IO3Package::doNotPerformFileOperationsOnDevicesQuery() and queryId = // `@id` for the `doNotPerformFileOperationsOnDevices` query "c/cert/do-not-perform-file-operations-on-devices" and - ruleId = "FIO32-C" + ruleId = "FIO32-C" and + category = "rule" or query = // `Query` instance for the `successfulFgetsOrFgetwsMayReturnAnEmptyString` query @@ -26,7 +27,8 @@ predicate isIO3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `successfulFgetsOrFgetwsMayReturnAnEmptyString` query "c/cert/successful-fgets-or-fgetws-may-return-an-empty-string" and - ruleId = "FIO37-C" + ruleId = "FIO37-C" and + category = "rule" or query = // `Query` instance for the `fileOpenForReadAndWriteOnDifferentStreams` query @@ -34,7 +36,8 @@ predicate isIO3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `fileOpenForReadAndWriteOnDifferentStreams` query "c/misra/file-open-for-read-and-write-on-different-streams" and - ruleId = "RULE-22-3" + ruleId = "RULE-22-3" and + category = "required" or query = // `Query` instance for the `attemptToWriteToAReadOnlyStream` query @@ -42,7 +45,8 @@ predicate isIO3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `attemptToWriteToAReadOnlyStream` query "c/misra/attempt-to-write-to-a-read-only-stream" and - ruleId = "RULE-22-4" + ruleId = "RULE-22-4" and + category = "mandatory" or query = // `Query` instance for the `pointerToAFileObjectDereferenced` query @@ -50,7 +54,8 @@ predicate isIO3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerToAFileObjectDereferenced` query "c/misra/pointer-to-a-file-object-dereferenced" and - ruleId = "RULE-22-5" + ruleId = "RULE-22-5" and + category = "mandatory" or query = // `Query` instance for the `eofShallBeComparedWithUnmodifiedReturnValues` query @@ -58,7 +63,8 @@ predicate isIO3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `eofShallBeComparedWithUnmodifiedReturnValues` query "c/misra/eof-shall-be-compared-with-unmodified-return-values" and - ruleId = "RULE-22-7" + ruleId = "RULE-22-7" and + category = "required" } module IO3Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll index a1c3978fc4..872ee2cbcd 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll @@ -9,14 +9,15 @@ newtype IO4Query = TWrongNumberOfFormatArgumentsQuery() or TWrongTypeFormatArgumentsQuery() -predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { +predicate isIO4QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `toctouRaceConditionsWhileAccessingFiles` query IO4Package::toctouRaceConditionsWhileAccessingFilesQuery() and queryId = // `@id` for the `toctouRaceConditionsWhileAccessingFiles` query "c/cert/toctou-race-conditions-while-accessing-files" and - ruleId = "FIO45-C" + ruleId = "FIO45-C" and + category = "rule" or query = // `Query` instance for the `useValidSpecifiers` query @@ -24,7 +25,8 @@ predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useValidSpecifiers` query "c/cert/use-valid-specifiers" and - ruleId = "FIO47-C" + ruleId = "FIO47-C" and + category = "rule" or query = // `Query` instance for the `wrongNumberOfFormatArguments` query @@ -32,7 +34,8 @@ predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `wrongNumberOfFormatArguments` query "c/cert/wrong-number-of-format-arguments" and - ruleId = "FIO47-C" + ruleId = "FIO47-C" and + category = "rule" or query = // `Query` instance for the `wrongTypeFormatArguments` query @@ -40,7 +43,8 @@ predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `wrongTypeFormatArguments` query "c/cert/wrong-type-format-arguments" and - ruleId = "FIO47-C" + ruleId = "FIO47-C" and + category = "rule" } module IO4Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IntegerOverflow.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IntegerOverflow.qll new file mode 100644 index 0000000000..9a74132a55 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IntegerOverflow.qll @@ -0,0 +1,112 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype IntegerOverflowQuery = + TUnsignedIntegerOperationsWrapAroundQuery() or + TIntegerConversionCausesDataLossQuery() or + TSignedIntegerOverflowQuery() or + TDivOrRemByZeroQuery() or + TUseCorrectIntegerPrecisionsQuery() or + TConstantUnsignedIntegerExpressionsWrapAroundQuery() + +predicate isIntegerOverflowQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `unsignedIntegerOperationsWrapAround` query + IntegerOverflowPackage::unsignedIntegerOperationsWrapAroundQuery() and + queryId = + // `@id` for the `unsignedIntegerOperationsWrapAround` query + "c/cert/unsigned-integer-operations-wrap-around" and + ruleId = "INT30-C" and + category = "rule" + or + query = + // `Query` instance for the `integerConversionCausesDataLoss` query + IntegerOverflowPackage::integerConversionCausesDataLossQuery() and + queryId = + // `@id` for the `integerConversionCausesDataLoss` query + "c/cert/integer-conversion-causes-data-loss" and + ruleId = "INT31-C" and + category = "rule" + or + query = + // `Query` instance for the `signedIntegerOverflow` query + IntegerOverflowPackage::signedIntegerOverflowQuery() and + queryId = + // `@id` for the `signedIntegerOverflow` query + "c/cert/signed-integer-overflow" and + ruleId = "INT32-C" and + category = "rule" + or + query = + // `Query` instance for the `divOrRemByZero` query + IntegerOverflowPackage::divOrRemByZeroQuery() and + queryId = + // `@id` for the `divOrRemByZero` query + "c/cert/div-or-rem-by-zero" and + ruleId = "INT33-C" and + category = "rule" + or + query = + // `Query` instance for the `useCorrectIntegerPrecisions` query + IntegerOverflowPackage::useCorrectIntegerPrecisionsQuery() and + queryId = + // `@id` for the `useCorrectIntegerPrecisions` query + "c/cert/use-correct-integer-precisions" and + ruleId = "INT35-C" and + category = "rule" + or + query = + // `Query` instance for the `constantUnsignedIntegerExpressionsWrapAround` query + IntegerOverflowPackage::constantUnsignedIntegerExpressionsWrapAroundQuery() and + queryId = + // `@id` for the `constantUnsignedIntegerExpressionsWrapAround` query + "c/misra/constant-unsigned-integer-expressions-wrap-around" and + ruleId = "RULE-12-4" and + category = "advisory" +} + +module IntegerOverflowPackage { + Query unsignedIntegerOperationsWrapAroundQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unsignedIntegerOperationsWrapAround` query + TQueryC(TIntegerOverflowPackageQuery(TUnsignedIntegerOperationsWrapAroundQuery())) + } + + Query integerConversionCausesDataLossQuery() { + //autogenerate `Query` type + result = + // `Query` type for `integerConversionCausesDataLoss` query + TQueryC(TIntegerOverflowPackageQuery(TIntegerConversionCausesDataLossQuery())) + } + + Query signedIntegerOverflowQuery() { + //autogenerate `Query` type + result = + // `Query` type for `signedIntegerOverflow` query + TQueryC(TIntegerOverflowPackageQuery(TSignedIntegerOverflowQuery())) + } + + Query divOrRemByZeroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `divOrRemByZero` query + TQueryC(TIntegerOverflowPackageQuery(TDivOrRemByZeroQuery())) + } + + Query useCorrectIntegerPrecisionsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useCorrectIntegerPrecisions` query + TQueryC(TIntegerOverflowPackageQuery(TUseCorrectIntegerPrecisionsQuery())) + } + + Query constantUnsignedIntegerExpressionsWrapAroundQuery() { + //autogenerate `Query` type + result = + // `Query` type for `constantUnsignedIntegerExpressionsWrapAround` query + TQueryC(TIntegerOverflowPackageQuery(TConstantUnsignedIntegerExpressionsWrapAroundQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory1.qll new file mode 100644 index 0000000000..e0151592ef --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory1.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype InvalidMemory1Query = + TDoNotReadUninitializedMemoryQuery() or + TDoNotDereferenceNullPointersQuery() or + TDoNotAccessFreedMemoryQuery() or + TObjectWithAutoStorageDurationReadBeforeInitQuery() + +predicate isInvalidMemory1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotReadUninitializedMemory` query + InvalidMemory1Package::doNotReadUninitializedMemoryQuery() and + queryId = + // `@id` for the `doNotReadUninitializedMemory` query + "c/cert/do-not-read-uninitialized-memory" and + ruleId = "EXP33-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotDereferenceNullPointers` query + InvalidMemory1Package::doNotDereferenceNullPointersQuery() and + queryId = + // `@id` for the `doNotDereferenceNullPointers` query + "c/cert/do-not-dereference-null-pointers" and + ruleId = "EXP34-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotAccessFreedMemory` query + InvalidMemory1Package::doNotAccessFreedMemoryQuery() and + queryId = + // `@id` for the `doNotAccessFreedMemory` query + "c/cert/do-not-access-freed-memory" and + ruleId = "MEM30-C" and + category = "rule" + or + query = + // `Query` instance for the `objectWithAutoStorageDurationReadBeforeInit` query + InvalidMemory1Package::objectWithAutoStorageDurationReadBeforeInitQuery() and + queryId = + // `@id` for the `objectWithAutoStorageDurationReadBeforeInit` query + "c/misra/object-with-auto-storage-duration-read-before-init" and + ruleId = "RULE-9-1" and + category = "mandatory" +} + +module InvalidMemory1Package { + Query doNotReadUninitializedMemoryQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotReadUninitializedMemory` query + TQueryC(TInvalidMemory1PackageQuery(TDoNotReadUninitializedMemoryQuery())) + } + + Query doNotDereferenceNullPointersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotDereferenceNullPointers` query + TQueryC(TInvalidMemory1PackageQuery(TDoNotDereferenceNullPointersQuery())) + } + + Query doNotAccessFreedMemoryQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotAccessFreedMemory` query + TQueryC(TInvalidMemory1PackageQuery(TDoNotAccessFreedMemoryQuery())) + } + + Query objectWithAutoStorageDurationReadBeforeInitQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectWithAutoStorageDurationReadBeforeInit` query + TQueryC(TInvalidMemory1PackageQuery(TObjectWithAutoStorageDurationReadBeforeInitQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory2.qll new file mode 100644 index 0000000000..f1a37e4bcc --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory2.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype InvalidMemory2Query = + TVariableLengthArraySizeNotInValidRangeQuery() or + TDoNotUsePointerArithmeticOnNonArrayObjectPointersQuery() or + TDoNotModifyObjectsWithTemporaryLifetimeQuery() + +predicate isInvalidMemory2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `variableLengthArraySizeNotInValidRange` query + InvalidMemory2Package::variableLengthArraySizeNotInValidRangeQuery() and + queryId = + // `@id` for the `variableLengthArraySizeNotInValidRange` query + "c/cert/variable-length-array-size-not-in-valid-range" and + ruleId = "ARR32-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotUsePointerArithmeticOnNonArrayObjectPointers` query + InvalidMemory2Package::doNotUsePointerArithmeticOnNonArrayObjectPointersQuery() and + queryId = + // `@id` for the `doNotUsePointerArithmeticOnNonArrayObjectPointers` query + "c/cert/do-not-use-pointer-arithmetic-on-non-array-object-pointers" and + ruleId = "ARR37-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotModifyObjectsWithTemporaryLifetime` query + InvalidMemory2Package::doNotModifyObjectsWithTemporaryLifetimeQuery() and + queryId = + // `@id` for the `doNotModifyObjectsWithTemporaryLifetime` query + "c/cert/do-not-modify-objects-with-temporary-lifetime" and + ruleId = "EXP35-C" and + category = "rule" +} + +module InvalidMemory2Package { + Query variableLengthArraySizeNotInValidRangeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `variableLengthArraySizeNotInValidRange` query + TQueryC(TInvalidMemory2PackageQuery(TVariableLengthArraySizeNotInValidRangeQuery())) + } + + Query doNotUsePointerArithmeticOnNonArrayObjectPointersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotUsePointerArithmeticOnNonArrayObjectPointers` query + TQueryC(TInvalidMemory2PackageQuery(TDoNotUsePointerArithmeticOnNonArrayObjectPointersQuery())) + } + + Query doNotModifyObjectsWithTemporaryLifetimeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotModifyObjectsWithTemporaryLifetime` query + TQueryC(TInvalidMemory2PackageQuery(TDoNotModifyObjectsWithTemporaryLifetimeQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory3.qll new file mode 100644 index 0000000000..c4e39882ec --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/InvalidMemory3.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype InvalidMemory3Query = + TPointersToVariablyModifiedArrayTypesUsedQuery() or + TArrayToPointerConversionOfTemporaryObjectQuery() or + TModifiableLValueSubscriptedWithTemporaryLifetimeQuery() + +predicate isInvalidMemory3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `pointersToVariablyModifiedArrayTypesUsed` query + InvalidMemory3Package::pointersToVariablyModifiedArrayTypesUsedQuery() and + queryId = + // `@id` for the `pointersToVariablyModifiedArrayTypesUsed` query + "c/misra/pointers-to-variably-modified-array-types-used" and + ruleId = "RULE-18-10" and + category = "mandatory" + or + query = + // `Query` instance for the `arrayToPointerConversionOfTemporaryObject` query + InvalidMemory3Package::arrayToPointerConversionOfTemporaryObjectQuery() and + queryId = + // `@id` for the `arrayToPointerConversionOfTemporaryObject` query + "c/misra/array-to-pointer-conversion-of-temporary-object" and + ruleId = "RULE-18-9" and + category = "required" + or + query = + // `Query` instance for the `modifiableLValueSubscriptedWithTemporaryLifetime` query + InvalidMemory3Package::modifiableLValueSubscriptedWithTemporaryLifetimeQuery() and + queryId = + // `@id` for the `modifiableLValueSubscriptedWithTemporaryLifetime` query + "c/misra/modifiable-l-value-subscripted-with-temporary-lifetime" and + ruleId = "RULE-18-9" and + category = "required" +} + +module InvalidMemory3Package { + Query pointersToVariablyModifiedArrayTypesUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointersToVariablyModifiedArrayTypesUsed` query + TQueryC(TInvalidMemory3PackageQuery(TPointersToVariablyModifiedArrayTypesUsedQuery())) + } + + Query arrayToPointerConversionOfTemporaryObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `arrayToPointerConversionOfTemporaryObject` query + TQueryC(TInvalidMemory3PackageQuery(TArrayToPointerConversionOfTemporaryObjectQuery())) + } + + Query modifiableLValueSubscriptedWithTemporaryLifetimeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `modifiableLValueSubscriptedWithTemporaryLifetime` query + TQueryC(TInvalidMemory3PackageQuery(TModifiableLValueSubscriptedWithTemporaryLifetimeQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Language1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Language1.qll index a2787698cc..0fb03bf6d5 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Language1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Language1.qll @@ -5,14 +5,15 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype Language1Query = TLanguageNotEncapsulatedAndIsolatedQuery() -predicate isLanguage1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isLanguage1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `languageNotEncapsulatedAndIsolated` query Language1Package::languageNotEncapsulatedAndIsolatedQuery() and queryId = // `@id` for the `languageNotEncapsulatedAndIsolated` query "c/misra/language-not-encapsulated-and-isolated" and - ruleId = "DIR-4-3" + ruleId = "DIR-4-3" and + category = "required" } module Language1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Language2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Language2.qll new file mode 100644 index 0000000000..2a85696cc2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Language2.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Language2Query = + TUsageOfAssemblyLanguageShouldBeDocumentedQuery() or + TEmergentLanguageFeaturesUsedQuery() + +predicate isLanguage2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `usageOfAssemblyLanguageShouldBeDocumented` query + Language2Package::usageOfAssemblyLanguageShouldBeDocumentedQuery() and + queryId = + // `@id` for the `usageOfAssemblyLanguageShouldBeDocumented` query + "c/misra/usage-of-assembly-language-should-be-documented" and + ruleId = "DIR-4-2" and + category = "advisory" + or + query = + // `Query` instance for the `emergentLanguageFeaturesUsed` query + Language2Package::emergentLanguageFeaturesUsedQuery() and + queryId = + // `@id` for the `emergentLanguageFeaturesUsed` query + "c/misra/emergent-language-features-used" and + ruleId = "RULE-1-4" and + category = "required" +} + +module Language2Package { + Query usageOfAssemblyLanguageShouldBeDocumentedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `usageOfAssemblyLanguageShouldBeDocumented` query + TQueryC(TLanguage2PackageQuery(TUsageOfAssemblyLanguageShouldBeDocumentedQuery())) + } + + Query emergentLanguageFeaturesUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `emergentLanguageFeaturesUsed` query + TQueryC(TLanguage2PackageQuery(TEmergentLanguageFeaturesUsedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll new file mode 100644 index 0000000000..836f8f7010 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Language3Query = + TLanguageExtensionsShouldNotBeUsedQuery() or + TOccurrenceOfUndefinedBehaviorQuery() + +predicate isLanguage3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `languageExtensionsShouldNotBeUsed` query + Language3Package::languageExtensionsShouldNotBeUsedQuery() and + queryId = + // `@id` for the `languageExtensionsShouldNotBeUsed` query + "c/misra/language-extensions-should-not-be-used" and + ruleId = "RULE-1-2" and + category = "advisory" + or + query = + // `Query` instance for the `occurrenceOfUndefinedBehavior` query + Language3Package::occurrenceOfUndefinedBehaviorQuery() and + queryId = + // `@id` for the `occurrenceOfUndefinedBehavior` query + "c/misra/occurrence-of-undefined-behavior" and + ruleId = "RULE-1-3" and + category = "required" +} + +module Language3Package { + Query languageExtensionsShouldNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `languageExtensionsShouldNotBeUsed` query + TQueryC(TLanguage3PackageQuery(TLanguageExtensionsShouldNotBeUsedQuery())) + } + + Query occurrenceOfUndefinedBehaviorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `occurrenceOfUndefinedBehavior` query + TQueryC(TLanguage3PackageQuery(TOccurrenceOfUndefinedBehaviorQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Language4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Language4.qll new file mode 100644 index 0000000000..b4391ff5c2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Language4.qll @@ -0,0 +1,163 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Language4Query = + TMissingStaticSpecifierFuncRedeclarationObsoleteQuery() or + TMissingStaticSpecifierObjectRedeclarationObsoleteQuery() or + TFunctionTypesNotInPrototypeFormObsoleteQuery() or + TUseOfObsoleteMacroAtomicVarInitQuery() or + TInvalidDefineOrUndefOfStdBoolMacroQuery() or + TCallToObsolescentFunctionGetsQuery() or + TUngetcCallOnStreamPositionZeroQuery() or + TSizeInReallocCallMayBeZeroQuery() or + TSizeInReallocCallIsZeroQuery() + +predicate isLanguage4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `missingStaticSpecifierFuncRedeclarationObsolete` query + Language4Package::missingStaticSpecifierFuncRedeclarationObsoleteQuery() and + queryId = + // `@id` for the `missingStaticSpecifierFuncRedeclarationObsolete` query + "c/misra/missing-static-specifier-func-redeclaration-obsolete" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `missingStaticSpecifierObjectRedeclarationObsolete` query + Language4Package::missingStaticSpecifierObjectRedeclarationObsoleteQuery() and + queryId = + // `@id` for the `missingStaticSpecifierObjectRedeclarationObsolete` query + "c/misra/missing-static-specifier-object-redeclaration-obsolete" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `functionTypesNotInPrototypeFormObsolete` query + Language4Package::functionTypesNotInPrototypeFormObsoleteQuery() and + queryId = + // `@id` for the `functionTypesNotInPrototypeFormObsolete` query + "c/misra/function-types-not-in-prototype-form-obsolete" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `useOfObsoleteMacroAtomicVarInit` query + Language4Package::useOfObsoleteMacroAtomicVarInitQuery() and + queryId = + // `@id` for the `useOfObsoleteMacroAtomicVarInit` query + "c/misra/use-of-obsolete-macro-atomic-var-init" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `invalidDefineOrUndefOfStdBoolMacro` query + Language4Package::invalidDefineOrUndefOfStdBoolMacroQuery() and + queryId = + // `@id` for the `invalidDefineOrUndefOfStdBoolMacro` query + "c/misra/invalid-define-or-undef-of-std-bool-macro" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `callToObsolescentFunctionGets` query + Language4Package::callToObsolescentFunctionGetsQuery() and + queryId = + // `@id` for the `callToObsolescentFunctionGets` query + "c/misra/call-to-obsolescent-function-gets" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `ungetcCallOnStreamPositionZero` query + Language4Package::ungetcCallOnStreamPositionZeroQuery() and + queryId = + // `@id` for the `ungetcCallOnStreamPositionZero` query + "c/misra/ungetc-call-on-stream-position-zero" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `sizeInReallocCallMayBeZero` query + Language4Package::sizeInReallocCallMayBeZeroQuery() and + queryId = + // `@id` for the `sizeInReallocCallMayBeZero` query + "c/misra/size-in-realloc-call-may-be-zero" and + ruleId = "RULE-1-5" and + category = "required" + or + query = + // `Query` instance for the `sizeInReallocCallIsZero` query + Language4Package::sizeInReallocCallIsZeroQuery() and + queryId = + // `@id` for the `sizeInReallocCallIsZero` query + "c/misra/size-in-realloc-call-is-zero" and + ruleId = "RULE-1-5" and + category = "required" +} + +module Language4Package { + Query missingStaticSpecifierFuncRedeclarationObsoleteQuery() { + //autogenerate `Query` type + result = + // `Query` type for `missingStaticSpecifierFuncRedeclarationObsolete` query + TQueryC(TLanguage4PackageQuery(TMissingStaticSpecifierFuncRedeclarationObsoleteQuery())) + } + + Query missingStaticSpecifierObjectRedeclarationObsoleteQuery() { + //autogenerate `Query` type + result = + // `Query` type for `missingStaticSpecifierObjectRedeclarationObsolete` query + TQueryC(TLanguage4PackageQuery(TMissingStaticSpecifierObjectRedeclarationObsoleteQuery())) + } + + Query functionTypesNotInPrototypeFormObsoleteQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionTypesNotInPrototypeFormObsolete` query + TQueryC(TLanguage4PackageQuery(TFunctionTypesNotInPrototypeFormObsoleteQuery())) + } + + Query useOfObsoleteMacroAtomicVarInitQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useOfObsoleteMacroAtomicVarInit` query + TQueryC(TLanguage4PackageQuery(TUseOfObsoleteMacroAtomicVarInitQuery())) + } + + Query invalidDefineOrUndefOfStdBoolMacroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidDefineOrUndefOfStdBoolMacro` query + TQueryC(TLanguage4PackageQuery(TInvalidDefineOrUndefOfStdBoolMacroQuery())) + } + + Query callToObsolescentFunctionGetsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `callToObsolescentFunctionGets` query + TQueryC(TLanguage4PackageQuery(TCallToObsolescentFunctionGetsQuery())) + } + + Query ungetcCallOnStreamPositionZeroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `ungetcCallOnStreamPositionZero` query + TQueryC(TLanguage4PackageQuery(TUngetcCallOnStreamPositionZeroQuery())) + } + + Query sizeInReallocCallMayBeZeroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `sizeInReallocCallMayBeZero` query + TQueryC(TLanguage4PackageQuery(TSizeInReallocCallMayBeZeroQuery())) + } + + Query sizeInReallocCallIsZeroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `sizeInReallocCallIsZero` query + TQueryC(TLanguage4PackageQuery(TSizeInReallocCallIsZeroQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Memory1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Memory1.qll new file mode 100644 index 0000000000..061215310c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Memory1.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Memory1Query = + TInitializerForAggregateOrUnionNotEnclosedInBracesQuery() or + TPartiallyInitializedArrayWithExplicitInitializersQuery() or + TRepeatedInitializationOfAggregateObjectElementQuery() + +predicate isMemory1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `initializerForAggregateOrUnionNotEnclosedInBraces` query + Memory1Package::initializerForAggregateOrUnionNotEnclosedInBracesQuery() and + queryId = + // `@id` for the `initializerForAggregateOrUnionNotEnclosedInBraces` query + "c/misra/initializer-for-aggregate-or-union-not-enclosed-in-braces" and + ruleId = "RULE-9-2" and + category = "required" + or + query = + // `Query` instance for the `partiallyInitializedArrayWithExplicitInitializers` query + Memory1Package::partiallyInitializedArrayWithExplicitInitializersQuery() and + queryId = + // `@id` for the `partiallyInitializedArrayWithExplicitInitializers` query + "c/misra/partially-initialized-array-with-explicit-initializers" and + ruleId = "RULE-9-3" and + category = "required" + or + query = + // `Query` instance for the `repeatedInitializationOfAggregateObjectElement` query + Memory1Package::repeatedInitializationOfAggregateObjectElementQuery() and + queryId = + // `@id` for the `repeatedInitializationOfAggregateObjectElement` query + "c/misra/repeated-initialization-of-aggregate-object-element" and + ruleId = "RULE-9-4" and + category = "required" +} + +module Memory1Package { + Query initializerForAggregateOrUnionNotEnclosedInBracesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `initializerForAggregateOrUnionNotEnclosedInBraces` query + TQueryC(TMemory1PackageQuery(TInitializerForAggregateOrUnionNotEnclosedInBracesQuery())) + } + + Query partiallyInitializedArrayWithExplicitInitializersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `partiallyInitializedArrayWithExplicitInitializers` query + TQueryC(TMemory1PackageQuery(TPartiallyInitializedArrayWithExplicitInitializersQuery())) + } + + Query repeatedInitializationOfAggregateObjectElementQuery() { + //autogenerate `Query` type + result = + // `Query` type for `repeatedInitializationOfAggregateObjectElement` query + TQueryC(TMemory1PackageQuery(TRepeatedInitializationOfAggregateObjectElementQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Memory2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Memory2.qll new file mode 100644 index 0000000000..4f537020fa --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Memory2.qll @@ -0,0 +1,197 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Memory2Query = + TDoNotSubtractPointersThatDoNotReferToTheSameArrayQuery() or + TDoNotRelatePointersThatDoNotReferToTheSameArrayQuery() or + TDoNotComparePaddingDataQuery() or + TFreeMemoryWhenNoLongerNeededCertQuery() or + TAllocStructsWithAFlexibleArrayMemberDynamicallyQuery() or + TCopyStructsWithAFlexibleArrayMemberDynamicallyQuery() or + TOnlyFreeMemoryAllocatedDynamicallyCertQuery() or + TDoNotModifyAlignmentOfMemoryWithReallocQuery() or + TFreeMemoryWhenNoLongerNeededMisraQuery() or + TCloseFileHandleWhenNoLongerNeededMisraQuery() or + TOnlyFreeMemoryAllocatedDynamicallyMisraQuery() + +predicate isMemory2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotSubtractPointersThatDoNotReferToTheSameArray` query + Memory2Package::doNotSubtractPointersThatDoNotReferToTheSameArrayQuery() and + queryId = + // `@id` for the `doNotSubtractPointersThatDoNotReferToTheSameArray` query + "c/cert/do-not-subtract-pointers-that-do-not-refer-to-the-same-array" and + ruleId = "ARR36-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotRelatePointersThatDoNotReferToTheSameArray` query + Memory2Package::doNotRelatePointersThatDoNotReferToTheSameArrayQuery() and + queryId = + // `@id` for the `doNotRelatePointersThatDoNotReferToTheSameArray` query + "c/cert/do-not-relate-pointers-that-do-not-refer-to-the-same-array" and + ruleId = "ARR36-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotComparePaddingData` query + Memory2Package::doNotComparePaddingDataQuery() and + queryId = + // `@id` for the `doNotComparePaddingData` query + "c/cert/do-not-compare-padding-data" and + ruleId = "EXP42-C" and + category = "rule" + or + query = + // `Query` instance for the `freeMemoryWhenNoLongerNeededCert` query + Memory2Package::freeMemoryWhenNoLongerNeededCertQuery() and + queryId = + // `@id` for the `freeMemoryWhenNoLongerNeededCert` query + "c/cert/free-memory-when-no-longer-needed-cert" and + ruleId = "MEM31-C" and + category = "rule" + or + query = + // `Query` instance for the `allocStructsWithAFlexibleArrayMemberDynamically` query + Memory2Package::allocStructsWithAFlexibleArrayMemberDynamicallyQuery() and + queryId = + // `@id` for the `allocStructsWithAFlexibleArrayMemberDynamically` query + "c/cert/alloc-structs-with-a-flexible-array-member-dynamically" and + ruleId = "MEM33-C" and + category = "rule" + or + query = + // `Query` instance for the `copyStructsWithAFlexibleArrayMemberDynamically` query + Memory2Package::copyStructsWithAFlexibleArrayMemberDynamicallyQuery() and + queryId = + // `@id` for the `copyStructsWithAFlexibleArrayMemberDynamically` query + "c/cert/copy-structs-with-a-flexible-array-member-dynamically" and + ruleId = "MEM33-C" and + category = "rule" + or + query = + // `Query` instance for the `onlyFreeMemoryAllocatedDynamicallyCert` query + Memory2Package::onlyFreeMemoryAllocatedDynamicallyCertQuery() and + queryId = + // `@id` for the `onlyFreeMemoryAllocatedDynamicallyCert` query + "c/cert/only-free-memory-allocated-dynamically-cert" and + ruleId = "MEM34-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotModifyAlignmentOfMemoryWithRealloc` query + Memory2Package::doNotModifyAlignmentOfMemoryWithReallocQuery() and + queryId = + // `@id` for the `doNotModifyAlignmentOfMemoryWithRealloc` query + "c/cert/do-not-modify-alignment-of-memory-with-realloc" and + ruleId = "MEM36-C" and + category = "rule" + or + query = + // `Query` instance for the `freeMemoryWhenNoLongerNeededMisra` query + Memory2Package::freeMemoryWhenNoLongerNeededMisraQuery() and + queryId = + // `@id` for the `freeMemoryWhenNoLongerNeededMisra` query + "c/misra/free-memory-when-no-longer-needed-misra" and + ruleId = "RULE-22-1" and + category = "required" + or + query = + // `Query` instance for the `closeFileHandleWhenNoLongerNeededMisra` query + Memory2Package::closeFileHandleWhenNoLongerNeededMisraQuery() and + queryId = + // `@id` for the `closeFileHandleWhenNoLongerNeededMisra` query + "c/misra/close-file-handle-when-no-longer-needed-misra" and + ruleId = "RULE-22-1" and + category = "required" + or + query = + // `Query` instance for the `onlyFreeMemoryAllocatedDynamicallyMisra` query + Memory2Package::onlyFreeMemoryAllocatedDynamicallyMisraQuery() and + queryId = + // `@id` for the `onlyFreeMemoryAllocatedDynamicallyMisra` query + "c/misra/only-free-memory-allocated-dynamically-misra" and + ruleId = "RULE-22-2" and + category = "mandatory" +} + +module Memory2Package { + Query doNotSubtractPointersThatDoNotReferToTheSameArrayQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotSubtractPointersThatDoNotReferToTheSameArray` query + TQueryC(TMemory2PackageQuery(TDoNotSubtractPointersThatDoNotReferToTheSameArrayQuery())) + } + + Query doNotRelatePointersThatDoNotReferToTheSameArrayQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotRelatePointersThatDoNotReferToTheSameArray` query + TQueryC(TMemory2PackageQuery(TDoNotRelatePointersThatDoNotReferToTheSameArrayQuery())) + } + + Query doNotComparePaddingDataQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotComparePaddingData` query + TQueryC(TMemory2PackageQuery(TDoNotComparePaddingDataQuery())) + } + + Query freeMemoryWhenNoLongerNeededCertQuery() { + //autogenerate `Query` type + result = + // `Query` type for `freeMemoryWhenNoLongerNeededCert` query + TQueryC(TMemory2PackageQuery(TFreeMemoryWhenNoLongerNeededCertQuery())) + } + + Query allocStructsWithAFlexibleArrayMemberDynamicallyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `allocStructsWithAFlexibleArrayMemberDynamically` query + TQueryC(TMemory2PackageQuery(TAllocStructsWithAFlexibleArrayMemberDynamicallyQuery())) + } + + Query copyStructsWithAFlexibleArrayMemberDynamicallyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `copyStructsWithAFlexibleArrayMemberDynamically` query + TQueryC(TMemory2PackageQuery(TCopyStructsWithAFlexibleArrayMemberDynamicallyQuery())) + } + + Query onlyFreeMemoryAllocatedDynamicallyCertQuery() { + //autogenerate `Query` type + result = + // `Query` type for `onlyFreeMemoryAllocatedDynamicallyCert` query + TQueryC(TMemory2PackageQuery(TOnlyFreeMemoryAllocatedDynamicallyCertQuery())) + } + + Query doNotModifyAlignmentOfMemoryWithReallocQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotModifyAlignmentOfMemoryWithRealloc` query + TQueryC(TMemory2PackageQuery(TDoNotModifyAlignmentOfMemoryWithReallocQuery())) + } + + Query freeMemoryWhenNoLongerNeededMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `freeMemoryWhenNoLongerNeededMisra` query + TQueryC(TMemory2PackageQuery(TFreeMemoryWhenNoLongerNeededMisraQuery())) + } + + Query closeFileHandleWhenNoLongerNeededMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `closeFileHandleWhenNoLongerNeededMisra` query + TQueryC(TMemory2PackageQuery(TCloseFileHandleWhenNoLongerNeededMisraQuery())) + } + + Query onlyFreeMemoryAllocatedDynamicallyMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `onlyFreeMemoryAllocatedDynamicallyMisra` query + TQueryC(TMemory2PackageQuery(TOnlyFreeMemoryAllocatedDynamicallyMisraQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Memory3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Memory3.qll new file mode 100644 index 0000000000..c59ff5eda8 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Memory3.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Memory3Query = TInsufficientMemoryAllocatedForObjectQuery() + +predicate isMemory3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `insufficientMemoryAllocatedForObject` query + Memory3Package::insufficientMemoryAllocatedForObjectQuery() and + queryId = + // `@id` for the `insufficientMemoryAllocatedForObject` query + "c/cert/insufficient-memory-allocated-for-object" and + ruleId = "MEM35-C" and + category = "rule" +} + +module Memory3Package { + Query insufficientMemoryAllocatedForObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `insufficientMemoryAllocatedForObject` query + TQueryC(TMemory3PackageQuery(TInsufficientMemoryAllocatedForObjectQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Misc.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Misc.qll index c63fe7c7d2..9535c8a6d1 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Misc.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Misc.qll @@ -8,14 +8,15 @@ newtype MiscQuery = TProperlySeedPseudorandomNumberGeneratorsQuery() or TControlFlowReachesTheEndOfANonVoidFunctionQuery() -predicate isMiscQueryMetadata(Query query, string queryId, string ruleId) { +predicate isMiscQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `randUsedForGeneratingPseudorandomNumbers` query MiscPackage::randUsedForGeneratingPseudorandomNumbersQuery() and queryId = // `@id` for the `randUsedForGeneratingPseudorandomNumbers` query "c/cert/rand-used-for-generating-pseudorandom-numbers" and - ruleId = "MSC30-C" + ruleId = "MSC30-C" and + category = "rule" or query = // `Query` instance for the `properlySeedPseudorandomNumberGenerators` query @@ -23,7 +24,8 @@ predicate isMiscQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `properlySeedPseudorandomNumberGenerators` query "c/cert/properly-seed-pseudorandom-number-generators" and - ruleId = "MSC32-C" + ruleId = "MSC32-C" and + category = "rule" or query = // `Query` instance for the `controlFlowReachesTheEndOfANonVoidFunction` query @@ -31,7 +33,8 @@ predicate isMiscQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `controlFlowReachesTheEndOfANonVoidFunction` query "c/cert/control-flow-reaches-the-end-of-a-non-void-function" and - ruleId = "MSC37-C" + ruleId = "MSC37-C" and + category = "rule" } module MiscPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/NoReturn.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/NoReturn.qll new file mode 100644 index 0000000000..07b9360213 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/NoReturn.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype NoReturnQuery = + TNonVoidReturnTypeOfNoreturnFunctionQuery() or + TFunctionWithNoReturningBranchShouldBeNoreturnQuery() or + TReturnStatementInNoreturnFunctionQuery() + +predicate isNoReturnQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `nonVoidReturnTypeOfNoreturnFunction` query + NoReturnPackage::nonVoidReturnTypeOfNoreturnFunctionQuery() and + queryId = + // `@id` for the `nonVoidReturnTypeOfNoreturnFunction` query + "c/misra/non-void-return-type-of-noreturn-function" and + ruleId = "RULE-17-10" and + category = "required" + or + query = + // `Query` instance for the `functionWithNoReturningBranchShouldBeNoreturn` query + NoReturnPackage::functionWithNoReturningBranchShouldBeNoreturnQuery() and + queryId = + // `@id` for the `functionWithNoReturningBranchShouldBeNoreturn` query + "c/misra/function-with-no-returning-branch-should-be-noreturn" and + ruleId = "RULE-17-11" and + category = "advisory" + or + query = + // `Query` instance for the `returnStatementInNoreturnFunction` query + NoReturnPackage::returnStatementInNoreturnFunctionQuery() and + queryId = + // `@id` for the `returnStatementInNoreturnFunction` query + "c/misra/return-statement-in-noreturn-function" and + ruleId = "RULE-17-9" and + category = "mandatory" +} + +module NoReturnPackage { + Query nonVoidReturnTypeOfNoreturnFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonVoidReturnTypeOfNoreturnFunction` query + TQueryC(TNoReturnPackageQuery(TNonVoidReturnTypeOfNoreturnFunctionQuery())) + } + + Query functionWithNoReturningBranchShouldBeNoreturnQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionWithNoReturningBranchShouldBeNoreturn` query + TQueryC(TNoReturnPackageQuery(TFunctionWithNoReturningBranchShouldBeNoreturnQuery())) + } + + Query returnStatementInNoreturnFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `returnStatementInNoreturnFunction` query + TQueryC(TNoReturnPackageQuery(TReturnStatementInNoreturnFunctionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/OutOfBounds.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/OutOfBounds.qll new file mode 100644 index 0000000000..1f606288fb --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/OutOfBounds.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype OutOfBoundsQuery = + TDoNotFormOutOfBoundsPointersOrArraySubscriptsQuery() or + TLibraryFunctionArgumentOutOfBoundsQuery() or + TStringFunctionPointerArgumentOutOfBoundsQuery() or + TStringLibrarySizeArgumentOutOfBoundsQuery() + +predicate isOutOfBoundsQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotFormOutOfBoundsPointersOrArraySubscripts` query + OutOfBoundsPackage::doNotFormOutOfBoundsPointersOrArraySubscriptsQuery() and + queryId = + // `@id` for the `doNotFormOutOfBoundsPointersOrArraySubscripts` query + "c/cert/do-not-form-out-of-bounds-pointers-or-array-subscripts" and + ruleId = "ARR30-C" and + category = "rule" + or + query = + // `Query` instance for the `libraryFunctionArgumentOutOfBounds` query + OutOfBoundsPackage::libraryFunctionArgumentOutOfBoundsQuery() and + queryId = + // `@id` for the `libraryFunctionArgumentOutOfBounds` query + "c/cert/library-function-argument-out-of-bounds" and + ruleId = "ARR38-C" and + category = "rule" + or + query = + // `Query` instance for the `stringFunctionPointerArgumentOutOfBounds` query + OutOfBoundsPackage::stringFunctionPointerArgumentOutOfBoundsQuery() and + queryId = + // `@id` for the `stringFunctionPointerArgumentOutOfBounds` query + "c/misra/string-function-pointer-argument-out-of-bounds" and + ruleId = "RULE-21-17" and + category = "mandatory" + or + query = + // `Query` instance for the `stringLibrarySizeArgumentOutOfBounds` query + OutOfBoundsPackage::stringLibrarySizeArgumentOutOfBoundsQuery() and + queryId = + // `@id` for the `stringLibrarySizeArgumentOutOfBounds` query + "c/misra/string-library-size-argument-out-of-bounds" and + ruleId = "RULE-21-18" and + category = "mandatory" +} + +module OutOfBoundsPackage { + Query doNotFormOutOfBoundsPointersOrArraySubscriptsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotFormOutOfBoundsPointersOrArraySubscripts` query + TQueryC(TOutOfBoundsPackageQuery(TDoNotFormOutOfBoundsPointersOrArraySubscriptsQuery())) + } + + Query libraryFunctionArgumentOutOfBoundsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `libraryFunctionArgumentOutOfBounds` query + TQueryC(TOutOfBoundsPackageQuery(TLibraryFunctionArgumentOutOfBoundsQuery())) + } + + Query stringFunctionPointerArgumentOutOfBoundsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `stringFunctionPointerArgumentOutOfBounds` query + TQueryC(TOutOfBoundsPackageQuery(TStringFunctionPointerArgumentOutOfBoundsQuery())) + } + + Query stringLibrarySizeArgumentOutOfBoundsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `stringLibrarySizeArgumentOutOfBounds` query + TQueryC(TOutOfBoundsPackageQuery(TStringLibrarySizeArgumentOutOfBoundsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers1.qll index 506e3a5fba..725fe46904 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers1.qll @@ -19,17 +19,19 @@ newtype Pointers1Query = TDoNotUseAdditionOrSubtractionOperatorsOnPointersQuery() or TNoMoreThanTwoLevelsOfPointerNestingInDeclarationsQuery() or TAutomaticStorageObjectAddressCopiedToOtherObjectQuery() or + TThreadLocalObjectAddressCopiedToGlobalObjectQuery() or TObjectWithNoPointerDereferenceShouldBeOpaqueQuery() or TPointerShouldPointToConstTypeWhenPossibleQuery() -predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `conversionBetweenFunctionPointerAndOtherType` query Pointers1Package::conversionBetweenFunctionPointerAndOtherTypeQuery() and queryId = // `@id` for the `conversionBetweenFunctionPointerAndOtherType` query "c/misra/conversion-between-function-pointer-and-other-type" and - ruleId = "RULE-11-1" + ruleId = "RULE-11-1" and + category = "required" or query = // `Query` instance for the `conversionBetweenIncompleteTypePointerAndOtherType` query @@ -37,7 +39,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `conversionBetweenIncompleteTypePointerAndOtherType` query "c/misra/conversion-between-incomplete-type-pointer-and-other-type" and - ruleId = "RULE-11-2" + ruleId = "RULE-11-2" and + category = "required" or query = // `Query` instance for the `castBetweenObjectPointerAndDifferentObjectType` query @@ -45,7 +48,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castBetweenObjectPointerAndDifferentObjectType` query "c/misra/cast-between-object-pointer-and-different-object-type" and - ruleId = "RULE-11-3" + ruleId = "RULE-11-3" and + category = "required" or query = // `Query` instance for the `conversionBetweenPointerToObjectAndIntegerType` query @@ -53,7 +57,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `conversionBetweenPointerToObjectAndIntegerType` query "c/misra/conversion-between-pointer-to-object-and-integer-type" and - ruleId = "RULE-11-4" + ruleId = "RULE-11-4" and + category = "advisory" or query = // `Query` instance for the `conversionFromPointerToVoidIntoPointerToObject` query @@ -61,7 +66,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `conversionFromPointerToVoidIntoPointerToObject` query "c/misra/conversion-from-pointer-to-void-into-pointer-to-object" and - ruleId = "RULE-11-5" + ruleId = "RULE-11-5" and + category = "advisory" or query = // `Query` instance for the `castBetweenPointerToVoidAndArithmeticType` query @@ -69,7 +75,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castBetweenPointerToVoidAndArithmeticType` query "c/misra/cast-between-pointer-to-void-and-arithmetic-type" and - ruleId = "RULE-11-6" + ruleId = "RULE-11-6" and + category = "required" or query = // `Query` instance for the `castBetweenPointerToObjectAndNonIntArithmeticType` query @@ -77,7 +84,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castBetweenPointerToObjectAndNonIntArithmeticType` query "c/misra/cast-between-pointer-to-object-and-non-int-arithmetic-type" and - ruleId = "RULE-11-7" + ruleId = "RULE-11-7" and + category = "required" or query = // `Query` instance for the `castRemovesConstOrVolatileQualification` query @@ -85,7 +93,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castRemovesConstOrVolatileQualification` query "c/misra/cast-removes-const-or-volatile-qualification" and - ruleId = "RULE-11-8" + ruleId = "RULE-11-8" and + category = "required" or query = // `Query` instance for the `macroNullNotUsedAsIntegerNullPointerConstant` query @@ -93,7 +102,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `macroNullNotUsedAsIntegerNullPointerConstant` query "c/misra/macro-null-not-used-as-integer-null-pointer-constant" and - ruleId = "RULE-11-9" + ruleId = "RULE-11-9" and + category = "required" or query = // `Query` instance for the `pointerAndDerivedPointerMustAddressSameArray` query @@ -101,7 +111,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerAndDerivedPointerMustAddressSameArray` query "c/misra/pointer-and-derived-pointer-must-address-same-array" and - ruleId = "RULE-18-1" + ruleId = "RULE-18-1" and + category = "required" or query = // `Query` instance for the `subtractionBetweenPointersMustAddressSameArray` query @@ -109,7 +120,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `subtractionBetweenPointersMustAddressSameArray` query "c/misra/subtraction-between-pointers-must-address-same-array" and - ruleId = "RULE-18-2" + ruleId = "RULE-18-2" and + category = "required" or query = // `Query` instance for the `relationalOperatorComparesPointerToDifferentArray` query @@ -117,7 +129,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `relationalOperatorComparesPointerToDifferentArray` query "c/misra/relational-operator-compares-pointer-to-different-array" and - ruleId = "RULE-18-3" + ruleId = "RULE-18-3" and + category = "required" or query = // `Query` instance for the `doNotUseAdditionOrSubtractionOperatorsOnPointers` query @@ -125,7 +138,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotUseAdditionOrSubtractionOperatorsOnPointers` query "c/misra/do-not-use-addition-or-subtraction-operators-on-pointers" and - ruleId = "RULE-18-4" + ruleId = "RULE-18-4" and + category = "advisory" or query = // `Query` instance for the `noMoreThanTwoLevelsOfPointerNestingInDeclarations` query @@ -133,7 +147,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `noMoreThanTwoLevelsOfPointerNestingInDeclarations` query "c/misra/no-more-than-two-levels-of-pointer-nesting-in-declarations" and - ruleId = "RULE-18-5" + ruleId = "RULE-18-5" and + category = "advisory" or query = // `Query` instance for the `automaticStorageObjectAddressCopiedToOtherObject` query @@ -141,7 +156,17 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `automaticStorageObjectAddressCopiedToOtherObject` query "c/misra/automatic-storage-object-address-copied-to-other-object" and - ruleId = "RULE-18-6" + ruleId = "RULE-18-6" and + category = "required" + or + query = + // `Query` instance for the `threadLocalObjectAddressCopiedToGlobalObject` query + Pointers1Package::threadLocalObjectAddressCopiedToGlobalObjectQuery() and + queryId = + // `@id` for the `threadLocalObjectAddressCopiedToGlobalObject` query + "c/misra/thread-local-object-address-copied-to-global-object" and + ruleId = "RULE-18-6" and + category = "required" or query = // `Query` instance for the `objectWithNoPointerDereferenceShouldBeOpaque` query @@ -149,7 +174,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `objectWithNoPointerDereferenceShouldBeOpaque` query "c/misra/object-with-no-pointer-dereference-should-be-opaque" and - ruleId = "RULE-4-8" + ruleId = "DIR-4-8" and + category = "advisory" or query = // `Query` instance for the `pointerShouldPointToConstTypeWhenPossible` query @@ -157,7 +183,8 @@ predicate isPointers1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerShouldPointToConstTypeWhenPossible` query "c/misra/pointer-should-point-to-const-type-when-possible" and - ruleId = "RULE-8-13" + ruleId = "RULE-8-13" and + category = "advisory" } module Pointers1Package { @@ -266,6 +293,13 @@ module Pointers1Package { TQueryC(TPointers1PackageQuery(TAutomaticStorageObjectAddressCopiedToOtherObjectQuery())) } + Query threadLocalObjectAddressCopiedToGlobalObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadLocalObjectAddressCopiedToGlobalObject` query + TQueryC(TPointers1PackageQuery(TThreadLocalObjectAddressCopiedToGlobalObjectQuery())) + } + Query objectWithNoPointerDereferenceShouldBeOpaqueQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers2.qll index d5ddafd888..476388612e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers2.qll @@ -5,14 +5,15 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype Pointers2Query = TDoNotAddOrSubtractAScaledIntegerToAPointerQuery() -predicate isPointers2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPointers2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotAddOrSubtractAScaledIntegerToAPointer` query Pointers2Package::doNotAddOrSubtractAScaledIntegerToAPointerQuery() and queryId = // `@id` for the `doNotAddOrSubtractAScaledIntegerToAPointer` query "c/cert/do-not-add-or-subtract-a-scaled-integer-to-a-pointer" and - ruleId = "ARR39-C" + ruleId = "ARR39-C" and + category = "rule" } module Pointers2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll new file mode 100644 index 0000000000..26a8c43446 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Pointers3Query = + TDoNotAccessVolatileObjectWithNonVolatileReferenceQuery() or + TDoNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() or + TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery() or + TDoNotPassAliasedPointerToRestrictQualifiedParamQuery() or + TRestrictPointerReferencesOverlappingObjectQuery() + +predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotAccessVolatileObjectWithNonVolatileReference` query + Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery() and + queryId = + // `@id` for the `doNotAccessVolatileObjectWithNonVolatileReference` query + "c/cert/do-not-access-volatile-object-with-non-volatile-reference" and + ruleId = "EXP32-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotCastPointerToMoreStrictlyAlignedPointerType` query + Pointers3Package::doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() and + queryId = + // `@id` for the `doNotCastPointerToMoreStrictlyAlignedPointerType` query + "c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type" and + ruleId = "EXP36-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotAccessVariableViaPointerOfIncompatibleType` query + Pointers3Package::doNotAccessVariableViaPointerOfIncompatibleTypeQuery() and + queryId = + // `@id` for the `doNotAccessVariableViaPointerOfIncompatibleType` query + "c/cert/do-not-access-variable-via-pointer-of-incompatible-type" and + ruleId = "EXP39-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotPassAliasedPointerToRestrictQualifiedParam` query + Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery() and + queryId = + // `@id` for the `doNotPassAliasedPointerToRestrictQualifiedParam` query + "c/cert/do-not-pass-aliased-pointer-to-restrict-qualified-param" and + ruleId = "EXP43-C" and + category = "rule" + or + query = + // `Query` instance for the `restrictPointerReferencesOverlappingObject` query + Pointers3Package::restrictPointerReferencesOverlappingObjectQuery() and + queryId = + // `@id` for the `restrictPointerReferencesOverlappingObject` query + "c/cert/restrict-pointer-references-overlapping-object" and + ruleId = "EXP43-C" and + category = "rule" +} + +module Pointers3Package { + Query doNotAccessVolatileObjectWithNonVolatileReferenceQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotAccessVolatileObjectWithNonVolatileReference` query + TQueryC(TPointers3PackageQuery(TDoNotAccessVolatileObjectWithNonVolatileReferenceQuery())) + } + + Query doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCastPointerToMoreStrictlyAlignedPointerType` query + TQueryC(TPointers3PackageQuery(TDoNotCastPointerToMoreStrictlyAlignedPointerTypeQuery())) + } + + Query doNotAccessVariableViaPointerOfIncompatibleTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotAccessVariableViaPointerOfIncompatibleType` query + TQueryC(TPointers3PackageQuery(TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery())) + } + + Query doNotPassAliasedPointerToRestrictQualifiedParamQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotPassAliasedPointerToRestrictQualifiedParam` query + TQueryC(TPointers3PackageQuery(TDoNotPassAliasedPointerToRestrictQualifiedParamQuery())) + } + + Query restrictPointerReferencesOverlappingObjectQuery() { + //autogenerate `Query` type + result = + // `Query` type for `restrictPointerReferencesOverlappingObject` query + TQueryC(TPointers3PackageQuery(TRestrictPointerReferencesOverlappingObjectQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor1.qll index 7037c9f195..1f3c9bba38 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor1.qll @@ -9,14 +9,15 @@ newtype Preprocessor1Query = TForbiddenCharactersInHeaderFileNameQuery() or TIdentifiersUsedInPreprocessorExpressionQuery() -predicate isPreprocessor1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPreprocessor1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `includeDirectivesPrecededByDirectivesOrComments` query Preprocessor1Package::includeDirectivesPrecededByDirectivesOrCommentsQuery() and queryId = // `@id` for the `includeDirectivesPrecededByDirectivesOrComments` query "c/misra/include-directives-preceded-by-directives-or-comments" and - ruleId = "RULE-20-1" + ruleId = "RULE-20-1" and + category = "advisory" or query = // `Query` instance for the `preprocessorHashOperatorsShouldNotBeUsed` query @@ -24,7 +25,8 @@ predicate isPreprocessor1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `preprocessorHashOperatorsShouldNotBeUsed` query "c/misra/preprocessor-hash-operators-should-not-be-used" and - ruleId = "RULE-20-10" + ruleId = "RULE-20-10" and + category = "advisory" or query = // `Query` instance for the `forbiddenCharactersInHeaderFileName` query @@ -32,7 +34,8 @@ predicate isPreprocessor1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `forbiddenCharactersInHeaderFileName` query "c/misra/forbidden-characters-in-header-file-name" and - ruleId = "RULE-20-2" + ruleId = "RULE-20-2" and + category = "required" or query = // `Query` instance for the `identifiersUsedInPreprocessorExpression` query @@ -40,7 +43,8 @@ predicate isPreprocessor1QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `identifiersUsedInPreprocessorExpression` query "c/misra/identifiers-used-in-preprocessor-expression" and - ruleId = "RULE-20-9" + ruleId = "RULE-20-9" and + category = "required" } module Preprocessor1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor2.qll index 942f633f45..bc6fc91da6 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor2.qll @@ -9,14 +9,15 @@ newtype Preprocessor2Query = TUndefShouldNotBeUsedQuery() or TPrecautionIncludeGuardsNotProvidedQuery() -predicate isPreprocessor2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPreprocessor2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `moreThanOneHashOperatorInMacroDefinition` query Preprocessor2Package::moreThanOneHashOperatorInMacroDefinitionQuery() and queryId = // `@id` for the `moreThanOneHashOperatorInMacroDefinition` query "c/misra/more-than-one-hash-operator-in-macro-definition" and - ruleId = "RULE-20-11" + ruleId = "RULE-20-11" and + category = "required" or query = // `Query` instance for the `macroParameterUsedAsHashOperand` query @@ -24,7 +25,8 @@ predicate isPreprocessor2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `macroParameterUsedAsHashOperand` query "c/misra/macro-parameter-used-as-hash-operand" and - ruleId = "RULE-20-12" + ruleId = "RULE-20-12" and + category = "required" or query = // `Query` instance for the `undefShouldNotBeUsed` query @@ -32,7 +34,8 @@ predicate isPreprocessor2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `undefShouldNotBeUsed` query "c/misra/undef-should-not-be-used" and - ruleId = "RULE-20-5" + ruleId = "RULE-20-5" and + category = "advisory" or query = // `Query` instance for the `precautionIncludeGuardsNotProvided` query @@ -40,7 +43,8 @@ predicate isPreprocessor2QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `precautionIncludeGuardsNotProvided` query "c/misra/precaution-include-guards-not-provided" and - ruleId = "RULE-4-10" + ruleId = "DIR-4-10" and + category = "required" } module Preprocessor2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor3.qll index 5894975f2d..1b9ed9374e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor3.qll @@ -5,14 +5,15 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype Preprocessor3Query = TControllingExpressionIfDirectiveQuery() -predicate isPreprocessor3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPreprocessor3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `controllingExpressionIfDirective` query Preprocessor3Package::controllingExpressionIfDirectiveQuery() and queryId = // `@id` for the `controllingExpressionIfDirective` query "c/misra/controlling-expression-if-directive" and - ruleId = "RULE-20-8" + ruleId = "RULE-20-8" and + category = "required" } module Preprocessor3Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll index 94ffc1dc8d..8b30501183 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll @@ -8,14 +8,15 @@ newtype Preprocessor4Query = TFunctionLikeMacroArgsContainHashTokenCQueryQuery() or TDefineAndUndefUsedOnReservedIdentifierOrMacroNameQuery() -predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `macroDefinedWithTheSameNameAsKeyword` query Preprocessor4Package::macroDefinedWithTheSameNameAsKeywordQuery() and queryId = // `@id` for the `macroDefinedWithTheSameNameAsKeyword` query "c/misra/macro-defined-with-the-same-name-as-keyword" and - ruleId = "RULE-20-4" + ruleId = "RULE-20-4" and + category = "required" or query = // `Query` instance for the `functionLikeMacroArgsContainHashTokenCQuery` query @@ -23,7 +24,8 @@ predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `functionLikeMacroArgsContainHashTokenCQuery` query "c/misra/function-like-macro-args-contain-hash-token-c-query" and - ruleId = "RULE-20-6" + ruleId = "RULE-20-6" and + category = "required" or query = // `Query` instance for the `defineAndUndefUsedOnReservedIdentifierOrMacroName` query @@ -31,7 +33,8 @@ predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `defineAndUndefUsedOnReservedIdentifierOrMacroName` query "c/misra/define-and-undef-used-on-reserved-identifier-or-macro-name" and - ruleId = "RULE-21-1" + ruleId = "RULE-21-1" and + category = "required" } module Preprocessor4Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor5.qll index c9498fa549..0d35690408 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor5.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor5.qll @@ -8,14 +8,15 @@ newtype Preprocessor5Query = TMacroOrFunctionArgsContainHashTokenQuery() or TMacroParameterNotEnclosedInParenthesesCQueryQuery() -predicate isPreprocessor5QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPreprocessor5QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotTreatAPredefinedIdentifierAsObject` query Preprocessor5Package::doNotTreatAPredefinedIdentifierAsObjectQuery() and queryId = // `@id` for the `doNotTreatAPredefinedIdentifierAsObject` query "c/cert/do-not-treat-a-predefined-identifier-as-object" and - ruleId = "MSC38-C" + ruleId = "MSC38-C" and + category = "rule" or query = // `Query` instance for the `macroOrFunctionArgsContainHashToken` query @@ -23,7 +24,8 @@ predicate isPreprocessor5QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `macroOrFunctionArgsContainHashToken` query "c/cert/macro-or-function-args-contain-hash-token" and - ruleId = "PRE32-C" + ruleId = "PRE32-C" and + category = "rule" or query = // `Query` instance for the `macroParameterNotEnclosedInParenthesesCQuery` query @@ -31,7 +33,8 @@ predicate isPreprocessor5QueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `macroParameterNotEnclosedInParenthesesCQuery` query "c/misra/macro-parameter-not-enclosed-in-parentheses-c-query" and - ruleId = "RULE-20-7" + ruleId = "RULE-20-7" and + category = "required" } module Preprocessor5Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor6.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor6.qll new file mode 100644 index 0000000000..1f68e30376 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor6.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Preprocessor6Query = TFunctionOverFunctionLikeMacroQuery() + +predicate isPreprocessor6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `functionOverFunctionLikeMacro` query + Preprocessor6Package::functionOverFunctionLikeMacroQuery() and + queryId = + // `@id` for the `functionOverFunctionLikeMacro` query + "c/misra/function-over-function-like-macro" and + ruleId = "DIR-4-9" and + category = "advisory" +} + +module Preprocessor6Package { + Query functionOverFunctionLikeMacroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionOverFunctionLikeMacro` query + TQueryC(TPreprocessor6PackageQuery(TFunctionOverFunctionLikeMacroQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index e3b6352ed2..b574f7551c 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -2,106 +2,271 @@ import cpp import codingstandards.cpp.exclusions.RuleMetadata //** Import packages for this language **/ +import Alignment import Banned +import Banned2 +import BitfieldTypes +import BitfieldTypes2 import Concurrency1 import Concurrency2 import Concurrency3 import Concurrency4 import Concurrency5 +import Concurrency6 +import Concurrency7 +import Concurrency8 +import Concurrency9 +import Contracts import Contracts1 import Contracts2 import Contracts3 +import Contracts4 +import Contracts5 +import Contracts6 +import Contracts7 +import DeadCode +import DeadCode2 import Declarations1 import Declarations2 import Declarations3 +import Declarations4 +import Declarations5 +import Declarations6 +import Declarations7 +import Declarations8 +import Declarations9 +import EssentialTypes +import EssentialTypes2 import Expressions +import Expressions2 +import FloatingTypes +import FloatingTypes2 +import FunctionTypes +import Generics import IO1 import IO2 import IO3 import IO4 +import IntegerOverflow +import InvalidMemory1 +import InvalidMemory2 +import InvalidMemory3 import Language1 +import Language2 +import Language3 +import Language4 +import Memory1 +import Memory2 +import Memory3 import Misc +import NoReturn +import OutOfBounds import Pointers1 import Pointers2 +import Pointers3 import Preprocessor1 import Preprocessor2 import Preprocessor3 import Preprocessor4 import Preprocessor5 +import Preprocessor6 import SideEffects1 import SideEffects2 +import SideEffects3 +import SideEffects4 +import SignalHandlers +import StandardLibraryFunctionTypes +import Statements1 +import Statements2 +import Statements3 +import Statements4 +import Statements5 +import Statements6 +import Static import Strings1 import Strings2 import Strings3 import Syntax +import Types1 +import Types2 /** The TQuery type representing this language * */ newtype TCQuery = + TAlignmentPackageQuery(AlignmentQuery q) or TBannedPackageQuery(BannedQuery q) or + TBanned2PackageQuery(Banned2Query q) or + TBitfieldTypesPackageQuery(BitfieldTypesQuery q) or + TBitfieldTypes2PackageQuery(BitfieldTypes2Query q) or TConcurrency1PackageQuery(Concurrency1Query q) or TConcurrency2PackageQuery(Concurrency2Query q) or TConcurrency3PackageQuery(Concurrency3Query q) or TConcurrency4PackageQuery(Concurrency4Query q) or TConcurrency5PackageQuery(Concurrency5Query q) or + TConcurrency6PackageQuery(Concurrency6Query q) or + TConcurrency7PackageQuery(Concurrency7Query q) or + TConcurrency8PackageQuery(Concurrency8Query q) or + TConcurrency9PackageQuery(Concurrency9Query q) or + TContractsPackageQuery(ContractsQuery q) or TContracts1PackageQuery(Contracts1Query q) or TContracts2PackageQuery(Contracts2Query q) or TContracts3PackageQuery(Contracts3Query q) or + TContracts4PackageQuery(Contracts4Query q) or + TContracts5PackageQuery(Contracts5Query q) or + TContracts6PackageQuery(Contracts6Query q) or + TContracts7PackageQuery(Contracts7Query q) or + TDeadCodePackageQuery(DeadCodeQuery q) or + TDeadCode2PackageQuery(DeadCode2Query q) or TDeclarations1PackageQuery(Declarations1Query q) or TDeclarations2PackageQuery(Declarations2Query q) or TDeclarations3PackageQuery(Declarations3Query q) or + TDeclarations4PackageQuery(Declarations4Query q) or + TDeclarations5PackageQuery(Declarations5Query q) or + TDeclarations6PackageQuery(Declarations6Query q) or + TDeclarations7PackageQuery(Declarations7Query q) or + TDeclarations8PackageQuery(Declarations8Query q) or + TDeclarations9PackageQuery(Declarations9Query q) or + TEssentialTypesPackageQuery(EssentialTypesQuery q) or + TEssentialTypes2PackageQuery(EssentialTypes2Query q) or TExpressionsPackageQuery(ExpressionsQuery q) or + TExpressions2PackageQuery(Expressions2Query q) or + TFloatingTypesPackageQuery(FloatingTypesQuery q) or + TFloatingTypes2PackageQuery(FloatingTypes2Query q) or + TFunctionTypesPackageQuery(FunctionTypesQuery q) or + TGenericsPackageQuery(GenericsQuery q) or TIO1PackageQuery(IO1Query q) or TIO2PackageQuery(IO2Query q) or TIO3PackageQuery(IO3Query q) or TIO4PackageQuery(IO4Query q) or + TIntegerOverflowPackageQuery(IntegerOverflowQuery q) or + TInvalidMemory1PackageQuery(InvalidMemory1Query q) or + TInvalidMemory2PackageQuery(InvalidMemory2Query q) or + TInvalidMemory3PackageQuery(InvalidMemory3Query q) or TLanguage1PackageQuery(Language1Query q) or + TLanguage2PackageQuery(Language2Query q) or + TLanguage3PackageQuery(Language3Query q) or + TLanguage4PackageQuery(Language4Query q) or + TMemory1PackageQuery(Memory1Query q) or + TMemory2PackageQuery(Memory2Query q) or + TMemory3PackageQuery(Memory3Query q) or TMiscPackageQuery(MiscQuery q) or + TNoReturnPackageQuery(NoReturnQuery q) or + TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or TPointers1PackageQuery(Pointers1Query q) or TPointers2PackageQuery(Pointers2Query q) or + TPointers3PackageQuery(Pointers3Query q) or TPreprocessor1PackageQuery(Preprocessor1Query q) or TPreprocessor2PackageQuery(Preprocessor2Query q) or TPreprocessor3PackageQuery(Preprocessor3Query q) or TPreprocessor4PackageQuery(Preprocessor4Query q) or TPreprocessor5PackageQuery(Preprocessor5Query q) or + TPreprocessor6PackageQuery(Preprocessor6Query q) or TSideEffects1PackageQuery(SideEffects1Query q) or TSideEffects2PackageQuery(SideEffects2Query q) or + TSideEffects3PackageQuery(SideEffects3Query q) or + TSideEffects4PackageQuery(SideEffects4Query q) or + TSignalHandlersPackageQuery(SignalHandlersQuery q) or + TStandardLibraryFunctionTypesPackageQuery(StandardLibraryFunctionTypesQuery q) or + TStatements1PackageQuery(Statements1Query q) or + TStatements2PackageQuery(Statements2Query q) or + TStatements3PackageQuery(Statements3Query q) or + TStatements4PackageQuery(Statements4Query q) or + TStatements5PackageQuery(Statements5Query q) or + TStatements6PackageQuery(Statements6Query q) or + TStaticPackageQuery(StaticQuery q) or TStrings1PackageQuery(Strings1Query q) or TStrings2PackageQuery(Strings2Query q) or TStrings3PackageQuery(Strings3Query q) or - TSyntaxPackageQuery(SyntaxQuery q) + TSyntaxPackageQuery(SyntaxQuery q) or + TTypes1PackageQuery(Types1Query q) or + TTypes2PackageQuery(Types2Query q) /** The metadata predicate * */ -predicate isQueryMetadata(Query query, string queryId, string ruleId) { - isBannedQueryMetadata(query, queryId, ruleId) or - isConcurrency1QueryMetadata(query, queryId, ruleId) or - isConcurrency2QueryMetadata(query, queryId, ruleId) or - isConcurrency3QueryMetadata(query, queryId, ruleId) or - isConcurrency4QueryMetadata(query, queryId, ruleId) or - isConcurrency5QueryMetadata(query, queryId, ruleId) or - isContracts1QueryMetadata(query, queryId, ruleId) or - isContracts2QueryMetadata(query, queryId, ruleId) or - isContracts3QueryMetadata(query, queryId, ruleId) or - isDeclarations1QueryMetadata(query, queryId, ruleId) or - isDeclarations2QueryMetadata(query, queryId, ruleId) or - isDeclarations3QueryMetadata(query, queryId, ruleId) or - isExpressionsQueryMetadata(query, queryId, ruleId) or - isIO1QueryMetadata(query, queryId, ruleId) or - isIO2QueryMetadata(query, queryId, ruleId) or - isIO3QueryMetadata(query, queryId, ruleId) or - isIO4QueryMetadata(query, queryId, ruleId) or - isLanguage1QueryMetadata(query, queryId, ruleId) or - isMiscQueryMetadata(query, queryId, ruleId) or - isPointers1QueryMetadata(query, queryId, ruleId) or - isPointers2QueryMetadata(query, queryId, ruleId) or - isPreprocessor1QueryMetadata(query, queryId, ruleId) or - isPreprocessor2QueryMetadata(query, queryId, ruleId) or - isPreprocessor3QueryMetadata(query, queryId, ruleId) or - isPreprocessor4QueryMetadata(query, queryId, ruleId) or - isPreprocessor5QueryMetadata(query, queryId, ruleId) or - isSideEffects1QueryMetadata(query, queryId, ruleId) or - isSideEffects2QueryMetadata(query, queryId, ruleId) or - isStrings1QueryMetadata(query, queryId, ruleId) or - isStrings2QueryMetadata(query, queryId, ruleId) or - isStrings3QueryMetadata(query, queryId, ruleId) or - isSyntaxQueryMetadata(query, queryId, ruleId) +predicate isQueryMetadata(Query query, string queryId, string ruleId, string category) { + isAlignmentQueryMetadata(query, queryId, ruleId, category) or + isBannedQueryMetadata(query, queryId, ruleId, category) or + isBanned2QueryMetadata(query, queryId, ruleId, category) or + isBitfieldTypesQueryMetadata(query, queryId, ruleId, category) or + isBitfieldTypes2QueryMetadata(query, queryId, ruleId, category) or + isConcurrency1QueryMetadata(query, queryId, ruleId, category) or + isConcurrency2QueryMetadata(query, queryId, ruleId, category) or + isConcurrency3QueryMetadata(query, queryId, ruleId, category) or + isConcurrency4QueryMetadata(query, queryId, ruleId, category) or + isConcurrency5QueryMetadata(query, queryId, ruleId, category) or + isConcurrency6QueryMetadata(query, queryId, ruleId, category) or + isConcurrency7QueryMetadata(query, queryId, ruleId, category) or + isConcurrency8QueryMetadata(query, queryId, ruleId, category) or + isConcurrency9QueryMetadata(query, queryId, ruleId, category) or + isContractsQueryMetadata(query, queryId, ruleId, category) or + isContracts1QueryMetadata(query, queryId, ruleId, category) or + isContracts2QueryMetadata(query, queryId, ruleId, category) or + isContracts3QueryMetadata(query, queryId, ruleId, category) or + isContracts4QueryMetadata(query, queryId, ruleId, category) or + isContracts5QueryMetadata(query, queryId, ruleId, category) or + isContracts6QueryMetadata(query, queryId, ruleId, category) or + isContracts7QueryMetadata(query, queryId, ruleId, category) or + isDeadCodeQueryMetadata(query, queryId, ruleId, category) or + isDeadCode2QueryMetadata(query, queryId, ruleId, category) or + isDeclarations1QueryMetadata(query, queryId, ruleId, category) or + isDeclarations2QueryMetadata(query, queryId, ruleId, category) or + isDeclarations3QueryMetadata(query, queryId, ruleId, category) or + isDeclarations4QueryMetadata(query, queryId, ruleId, category) or + isDeclarations5QueryMetadata(query, queryId, ruleId, category) or + isDeclarations6QueryMetadata(query, queryId, ruleId, category) or + isDeclarations7QueryMetadata(query, queryId, ruleId, category) or + isDeclarations8QueryMetadata(query, queryId, ruleId, category) or + isDeclarations9QueryMetadata(query, queryId, ruleId, category) or + isEssentialTypesQueryMetadata(query, queryId, ruleId, category) or + isEssentialTypes2QueryMetadata(query, queryId, ruleId, category) or + isExpressionsQueryMetadata(query, queryId, ruleId, category) or + isExpressions2QueryMetadata(query, queryId, ruleId, category) or + isFloatingTypesQueryMetadata(query, queryId, ruleId, category) or + isFloatingTypes2QueryMetadata(query, queryId, ruleId, category) or + isFunctionTypesQueryMetadata(query, queryId, ruleId, category) or + isGenericsQueryMetadata(query, queryId, ruleId, category) or + isIO1QueryMetadata(query, queryId, ruleId, category) or + isIO2QueryMetadata(query, queryId, ruleId, category) or + isIO3QueryMetadata(query, queryId, ruleId, category) or + isIO4QueryMetadata(query, queryId, ruleId, category) or + isIntegerOverflowQueryMetadata(query, queryId, ruleId, category) or + isInvalidMemory1QueryMetadata(query, queryId, ruleId, category) or + isInvalidMemory2QueryMetadata(query, queryId, ruleId, category) or + isInvalidMemory3QueryMetadata(query, queryId, ruleId, category) or + isLanguage1QueryMetadata(query, queryId, ruleId, category) or + isLanguage2QueryMetadata(query, queryId, ruleId, category) or + isLanguage3QueryMetadata(query, queryId, ruleId, category) or + isLanguage4QueryMetadata(query, queryId, ruleId, category) or + isMemory1QueryMetadata(query, queryId, ruleId, category) or + isMemory2QueryMetadata(query, queryId, ruleId, category) or + isMemory3QueryMetadata(query, queryId, ruleId, category) or + isMiscQueryMetadata(query, queryId, ruleId, category) or + isNoReturnQueryMetadata(query, queryId, ruleId, category) or + isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or + isPointers1QueryMetadata(query, queryId, ruleId, category) or + isPointers2QueryMetadata(query, queryId, ruleId, category) or + isPointers3QueryMetadata(query, queryId, ruleId, category) or + isPreprocessor1QueryMetadata(query, queryId, ruleId, category) or + isPreprocessor2QueryMetadata(query, queryId, ruleId, category) or + isPreprocessor3QueryMetadata(query, queryId, ruleId, category) or + isPreprocessor4QueryMetadata(query, queryId, ruleId, category) or + isPreprocessor5QueryMetadata(query, queryId, ruleId, category) or + isPreprocessor6QueryMetadata(query, queryId, ruleId, category) or + isSideEffects1QueryMetadata(query, queryId, ruleId, category) or + isSideEffects2QueryMetadata(query, queryId, ruleId, category) or + isSideEffects3QueryMetadata(query, queryId, ruleId, category) or + isSideEffects4QueryMetadata(query, queryId, ruleId, category) or + isSignalHandlersQueryMetadata(query, queryId, ruleId, category) or + isStandardLibraryFunctionTypesQueryMetadata(query, queryId, ruleId, category) or + isStatements1QueryMetadata(query, queryId, ruleId, category) or + isStatements2QueryMetadata(query, queryId, ruleId, category) or + isStatements3QueryMetadata(query, queryId, ruleId, category) or + isStatements4QueryMetadata(query, queryId, ruleId, category) or + isStatements5QueryMetadata(query, queryId, ruleId, category) or + isStatements6QueryMetadata(query, queryId, ruleId, category) or + isStaticQueryMetadata(query, queryId, ruleId, category) or + isStrings1QueryMetadata(query, queryId, ruleId, category) or + isStrings2QueryMetadata(query, queryId, ruleId, category) or + isStrings3QueryMetadata(query, queryId, ruleId, category) or + isSyntaxQueryMetadata(query, queryId, ruleId, category) or + isTypes1QueryMetadata(query, queryId, ruleId, category) or + isTypes2QueryMetadata(query, queryId, ruleId, category) } diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects1.qll index b3bcac75eb..ec8ab3eae8 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects1.qll @@ -15,14 +15,15 @@ newtype SideEffects1Query = TPossibleSuppressedSideEffectInLogicOperatorOperandQuery() or TSizeofOperandWithSideEffectQuery() -predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `dependenceOnOrderOfScalarEvaluationForSideEffects` query SideEffects1Package::dependenceOnOrderOfScalarEvaluationForSideEffectsQuery() and queryId = // `@id` for the `dependenceOnOrderOfScalarEvaluationForSideEffects` query "c/cert/dependence-on-order-of-scalar-evaluation-for-side-effects" and - ruleId = "EXP30-C" + ruleId = "EXP30-C" and + category = "rule" or query = // `Query` instance for the `dependenceOnOrderOfFunctionArgumentsForSideEffects` query @@ -30,7 +31,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `dependenceOnOrderOfFunctionArgumentsForSideEffects` query "c/cert/dependence-on-order-of-function-arguments-for-side-effects" and - ruleId = "EXP30-C" + ruleId = "EXP30-C" and + category = "rule" or query = // `Query` instance for the `unevaluatedOperandWithSideEffect` query @@ -38,7 +40,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `unevaluatedOperandWithSideEffect` query "c/cert/unevaluated-operand-with-side-effect" and - ruleId = "EXP44-C" + ruleId = "EXP44-C" and + category = "rule" or query = // `Query` instance for the `assignmentsInSelectionStatements` query @@ -46,7 +49,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `assignmentsInSelectionStatements` query "c/cert/assignments-in-selection-statements" and - ruleId = "EXP45-C" + ruleId = "EXP45-C" and + category = "rule" or query = // `Query` instance for the `unenclosedSizeofOperand` query @@ -54,7 +58,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `unenclosedSizeofOperand` query "c/misra/unenclosed-sizeof-operand" and - ruleId = "RULE-12-1" + ruleId = "RULE-12-1" and + category = "advisory" or query = // `Query` instance for the `implicitPrecedenceOfOperatorsInExpression` query @@ -62,7 +67,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `implicitPrecedenceOfOperatorsInExpression` query "c/misra/implicit-precedence-of-operators-in-expression" and - ruleId = "RULE-12-1" + ruleId = "RULE-12-1" and + category = "advisory" or query = // `Query` instance for the `initializerListsContainPersistentSideEffects` query @@ -70,7 +76,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `initializerListsContainPersistentSideEffects` query "c/misra/initializer-lists-contain-persistent-side-effects" and - ruleId = "RULE-13-1" + ruleId = "RULE-13-1" and + category = "required" or query = // `Query` instance for the `resultOfAnAssignmentOperatorShouldNotBeUsed` query @@ -78,7 +85,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `resultOfAnAssignmentOperatorShouldNotBeUsed` query "c/misra/result-of-an-assignment-operator-should-not-be-used" and - ruleId = "RULE-13-4" + ruleId = "RULE-13-4" and + category = "advisory" or query = // `Query` instance for the `possibleSuppressedSideEffectInLogicOperatorOperand` query @@ -86,7 +94,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `possibleSuppressedSideEffectInLogicOperatorOperand` query "c/misra/possible-suppressed-side-effect-in-logic-operator-operand" and - ruleId = "RULE-13-5" + ruleId = "RULE-13-5" and + category = "required" or query = // `Query` instance for the `sizeofOperandWithSideEffect` query @@ -94,7 +103,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `sizeofOperandWithSideEffect` query "c/misra/sizeof-operand-with-side-effect" and - ruleId = "RULE-13-6" + ruleId = "RULE-13-6" and + category = "required" } module SideEffects1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects2.qll index c14446ee92..82e5c0c5d2 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects2.qll @@ -7,14 +7,15 @@ newtype SideEffects2Query = TSideEffectAndCrementInFullExpressionQuery() or TModificationOfFunctionParameterQuery() -predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `sideEffectAndCrementInFullExpression` query SideEffects2Package::sideEffectAndCrementInFullExpressionQuery() and queryId = // `@id` for the `sideEffectAndCrementInFullExpression` query "c/misra/side-effect-and-crement-in-full-expression" and - ruleId = "RULE-13-3" + ruleId = "RULE-13-3" and + category = "advisory" or query = // `Query` instance for the `modificationOfFunctionParameter` query @@ -22,7 +23,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `modificationOfFunctionParameter` query "c/misra/modification-of-function-parameter" and - ruleId = "RULE-17-8" + ruleId = "RULE-17-8" and + category = "advisory" } module SideEffects2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects3.qll new file mode 100644 index 0000000000..7b01c18099 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects3.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype SideEffects3Query = + TUnsequencedSideEffectsQuery() or + TUnsequencedAtomicReadsQuery() + +predicate isSideEffects3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `unsequencedSideEffects` query + SideEffects3Package::unsequencedSideEffectsQuery() and + queryId = + // `@id` for the `unsequencedSideEffects` query + "c/misra/unsequenced-side-effects" and + ruleId = "RULE-13-2" and + category = "required" + or + query = + // `Query` instance for the `unsequencedAtomicReads` query + SideEffects3Package::unsequencedAtomicReadsQuery() and + queryId = + // `@id` for the `unsequencedAtomicReads` query + "c/misra/unsequenced-atomic-reads" and + ruleId = "RULE-13-2" and + category = "required" +} + +module SideEffects3Package { + Query unsequencedSideEffectsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unsequencedSideEffects` query + TQueryC(TSideEffects3PackageQuery(TUnsequencedSideEffectsQuery())) + } + + Query unsequencedAtomicReadsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unsequencedAtomicReads` query + TQueryC(TSideEffects3PackageQuery(TUnsequencedAtomicReadsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects4.qll new file mode 100644 index 0000000000..d48b4a562d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/SideEffects4.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype SideEffects4Query = TSideEffectsInArgumentsToUnsafeMacrosQuery() + +predicate isSideEffects4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `sideEffectsInArgumentsToUnsafeMacros` query + SideEffects4Package::sideEffectsInArgumentsToUnsafeMacrosQuery() and + queryId = + // `@id` for the `sideEffectsInArgumentsToUnsafeMacros` query + "c/cert/side-effects-in-arguments-to-unsafe-macros" and + ruleId = "PRE31-C" and + category = "rule" +} + +module SideEffects4Package { + Query sideEffectsInArgumentsToUnsafeMacrosQuery() { + //autogenerate `Query` type + result = + // `Query` type for `sideEffectsInArgumentsToUnsafeMacros` query + TQueryC(TSideEffects4PackageQuery(TSideEffectsInArgumentsToUnsafeMacrosQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/SignalHandlers.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/SignalHandlers.qll new file mode 100644 index 0000000000..f35a3f16b2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/SignalHandlers.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype SignalHandlersQuery = + TCallOnlyAsyncSafeFunctionsWithinSignalHandlersQuery() or + TDoNotAccessSharedObjectsInSignalHandlersQuery() or + TDoNotCallSignalFromInterruptibleSignalHandlersQuery() or + TDoNotReturnFromAComputationalExceptionHandlerQuery() + +predicate isSignalHandlersQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `callOnlyAsyncSafeFunctionsWithinSignalHandlers` query + SignalHandlersPackage::callOnlyAsyncSafeFunctionsWithinSignalHandlersQuery() and + queryId = + // `@id` for the `callOnlyAsyncSafeFunctionsWithinSignalHandlers` query + "c/cert/call-only-async-safe-functions-within-signal-handlers" and + ruleId = "SIG30-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotAccessSharedObjectsInSignalHandlers` query + SignalHandlersPackage::doNotAccessSharedObjectsInSignalHandlersQuery() and + queryId = + // `@id` for the `doNotAccessSharedObjectsInSignalHandlers` query + "c/cert/do-not-access-shared-objects-in-signal-handlers" and + ruleId = "SIG31-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotCallSignalFromInterruptibleSignalHandlers` query + SignalHandlersPackage::doNotCallSignalFromInterruptibleSignalHandlersQuery() and + queryId = + // `@id` for the `doNotCallSignalFromInterruptibleSignalHandlers` query + "c/cert/do-not-call-signal-from-interruptible-signal-handlers" and + ruleId = "SIG34-C" and + category = "rule" + or + query = + // `Query` instance for the `doNotReturnFromAComputationalExceptionHandler` query + SignalHandlersPackage::doNotReturnFromAComputationalExceptionHandlerQuery() and + queryId = + // `@id` for the `doNotReturnFromAComputationalExceptionHandler` query + "c/cert/do-not-return-from-a-computational-exception-handler" and + ruleId = "SIG35-C" and + category = "rule" +} + +module SignalHandlersPackage { + Query callOnlyAsyncSafeFunctionsWithinSignalHandlersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `callOnlyAsyncSafeFunctionsWithinSignalHandlers` query + TQueryC(TSignalHandlersPackageQuery(TCallOnlyAsyncSafeFunctionsWithinSignalHandlersQuery())) + } + + Query doNotAccessSharedObjectsInSignalHandlersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotAccessSharedObjectsInSignalHandlers` query + TQueryC(TSignalHandlersPackageQuery(TDoNotAccessSharedObjectsInSignalHandlersQuery())) + } + + Query doNotCallSignalFromInterruptibleSignalHandlersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCallSignalFromInterruptibleSignalHandlers` query + TQueryC(TSignalHandlersPackageQuery(TDoNotCallSignalFromInterruptibleSignalHandlersQuery())) + } + + Query doNotReturnFromAComputationalExceptionHandlerQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotReturnFromAComputationalExceptionHandler` query + TQueryC(TSignalHandlersPackageQuery(TDoNotReturnFromAComputationalExceptionHandlerQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/StandardLibraryFunctionTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/StandardLibraryFunctionTypes.qll new file mode 100644 index 0000000000..0d86bd9014 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/StandardLibraryFunctionTypes.qll @@ -0,0 +1,46 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype StandardLibraryFunctionTypesQuery = + TCtypeFunctionArgNotUnsignedCharOrEofQuery() or + TMemcpyMemmoveMemcmpArgNotPointersToCompatibleTypesQuery() + +predicate isStandardLibraryFunctionTypesQueryMetadata( + Query query, string queryId, string ruleId, string category +) { + query = + // `Query` instance for the `ctypeFunctionArgNotUnsignedCharOrEof` query + StandardLibraryFunctionTypesPackage::ctypeFunctionArgNotUnsignedCharOrEofQuery() and + queryId = + // `@id` for the `ctypeFunctionArgNotUnsignedCharOrEof` query + "c/misra/ctype-function-arg-not-unsigned-char-or-eof" and + ruleId = "RULE-21-13" and + category = "mandatory" + or + query = + // `Query` instance for the `memcpyMemmoveMemcmpArgNotPointersToCompatibleTypes` query + StandardLibraryFunctionTypesPackage::memcpyMemmoveMemcmpArgNotPointersToCompatibleTypesQuery() and + queryId = + // `@id` for the `memcpyMemmoveMemcmpArgNotPointersToCompatibleTypes` query + "c/misra/memcpy-memmove-memcmp-arg-not-pointers-to-compatible-types" and + ruleId = "RULE-21-15" and + category = "required" +} + +module StandardLibraryFunctionTypesPackage { + Query ctypeFunctionArgNotUnsignedCharOrEofQuery() { + //autogenerate `Query` type + result = + // `Query` type for `ctypeFunctionArgNotUnsignedCharOrEof` query + TQueryC(TStandardLibraryFunctionTypesPackageQuery(TCtypeFunctionArgNotUnsignedCharOrEofQuery())) + } + + Query memcpyMemmoveMemcmpArgNotPointersToCompatibleTypesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memcpyMemmoveMemcmpArgNotPointersToCompatibleTypes` query + TQueryC(TStandardLibraryFunctionTypesPackageQuery(TMemcpyMemmoveMemcmpArgNotPointersToCompatibleTypesQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements1.qll new file mode 100644 index 0000000000..88ea77c7d4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements1.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements1Query = + TNestSwitchLabelInSwitchStatementQuery() or + TBreakShallTerminateSwitchClauseQuery() or + TEverySwitchShallHaveDefaultLabelQuery() or + TDefaultNotFirstOrLastOfSwitchQuery() + +predicate isStatements1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `nestSwitchLabelInSwitchStatement` query + Statements1Package::nestSwitchLabelInSwitchStatementQuery() and + queryId = + // `@id` for the `nestSwitchLabelInSwitchStatement` query + "c/misra/nest-switch-label-in-switch-statement" and + ruleId = "RULE-16-2" and + category = "required" + or + query = + // `Query` instance for the `breakShallTerminateSwitchClause` query + Statements1Package::breakShallTerminateSwitchClauseQuery() and + queryId = + // `@id` for the `breakShallTerminateSwitchClause` query + "c/misra/break-shall-terminate-switch-clause" and + ruleId = "RULE-16-3" and + category = "required" + or + query = + // `Query` instance for the `everySwitchShallHaveDefaultLabel` query + Statements1Package::everySwitchShallHaveDefaultLabelQuery() and + queryId = + // `@id` for the `everySwitchShallHaveDefaultLabel` query + "c/misra/every-switch-shall-have-default-label" and + ruleId = "RULE-16-4" and + category = "required" + or + query = + // `Query` instance for the `defaultNotFirstOrLastOfSwitch` query + Statements1Package::defaultNotFirstOrLastOfSwitchQuery() and + queryId = + // `@id` for the `defaultNotFirstOrLastOfSwitch` query + "c/misra/default-not-first-or-last-of-switch" and + ruleId = "RULE-16-5" and + category = "required" +} + +module Statements1Package { + Query nestSwitchLabelInSwitchStatementQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nestSwitchLabelInSwitchStatement` query + TQueryC(TStatements1PackageQuery(TNestSwitchLabelInSwitchStatementQuery())) + } + + Query breakShallTerminateSwitchClauseQuery() { + //autogenerate `Query` type + result = + // `Query` type for `breakShallTerminateSwitchClause` query + TQueryC(TStatements1PackageQuery(TBreakShallTerminateSwitchClauseQuery())) + } + + Query everySwitchShallHaveDefaultLabelQuery() { + //autogenerate `Query` type + result = + // `Query` type for `everySwitchShallHaveDefaultLabel` query + TQueryC(TStatements1PackageQuery(TEverySwitchShallHaveDefaultLabelQuery())) + } + + Query defaultNotFirstOrLastOfSwitchQuery() { + //autogenerate `Query` type + result = + // `Query` type for `defaultNotFirstOrLastOfSwitch` query + TQueryC(TStatements1PackageQuery(TDefaultNotFirstOrLastOfSwitchQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements2.qll new file mode 100644 index 0000000000..49dd38c316 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements2.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements2Query = + TGotoLabelLocationConditionQuery() or + TGotoLabelBlockConditionQuery() or + TLoopIterationConditionQuery() or + TSwitchClauseNumberConditionQuery() or + TSwitchExpressionBoolConditionQuery() + +predicate isStatements2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `gotoLabelLocationCondition` query + Statements2Package::gotoLabelLocationConditionQuery() and + queryId = + // `@id` for the `gotoLabelLocationCondition` query + "c/misra/goto-label-location-condition" and + ruleId = "RULE-15-2" and + category = "required" + or + query = + // `Query` instance for the `gotoLabelBlockCondition` query + Statements2Package::gotoLabelBlockConditionQuery() and + queryId = + // `@id` for the `gotoLabelBlockCondition` query + "c/misra/goto-label-block-condition" and + ruleId = "RULE-15-3" and + category = "required" + or + query = + // `Query` instance for the `loopIterationCondition` query + Statements2Package::loopIterationConditionQuery() and + queryId = + // `@id` for the `loopIterationCondition` query + "c/misra/loop-iteration-condition" and + ruleId = "RULE-15-4" and + category = "advisory" + or + query = + // `Query` instance for the `switchClauseNumberCondition` query + Statements2Package::switchClauseNumberConditionQuery() and + queryId = + // `@id` for the `switchClauseNumberCondition` query + "c/misra/switch-clause-number-condition" and + ruleId = "RULE-16-6" and + category = "required" + or + query = + // `Query` instance for the `switchExpressionBoolCondition` query + Statements2Package::switchExpressionBoolConditionQuery() and + queryId = + // `@id` for the `switchExpressionBoolCondition` query + "c/misra/switch-expression-bool-condition" and + ruleId = "RULE-16-7" and + category = "required" +} + +module Statements2Package { + Query gotoLabelLocationConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoLabelLocationCondition` query + TQueryC(TStatements2PackageQuery(TGotoLabelLocationConditionQuery())) + } + + Query gotoLabelBlockConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoLabelBlockCondition` query + TQueryC(TStatements2PackageQuery(TGotoLabelBlockConditionQuery())) + } + + Query loopIterationConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `loopIterationCondition` query + TQueryC(TStatements2PackageQuery(TLoopIterationConditionQuery())) + } + + Query switchClauseNumberConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchClauseNumberCondition` query + TQueryC(TStatements2PackageQuery(TSwitchClauseNumberConditionQuery())) + } + + Query switchExpressionBoolConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchExpressionBoolCondition` query + TQueryC(TStatements2PackageQuery(TSwitchExpressionBoolConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements3.qll new file mode 100644 index 0000000000..25c1a82ea2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements3.qll @@ -0,0 +1,129 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements3Query = + TSwitchCompoundConditionQuery() or + TLoopCompoundConditionQuery() or + TSelectionCompoundConditionQuery() or + TIfElseEndConditionQuery() or + TSwitchCaseStartConditionQuery() or + TSwitchStmtNotWellFormedQuery() or + TRecursiveFunctionConditionQuery() + +predicate isStatements3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `switchCompoundCondition` query + Statements3Package::switchCompoundConditionQuery() and + queryId = + // `@id` for the `switchCompoundCondition` query + "c/misra/switch-compound-condition" and + ruleId = "RULE-15-6" and + category = "required" + or + query = + // `Query` instance for the `loopCompoundCondition` query + Statements3Package::loopCompoundConditionQuery() and + queryId = + // `@id` for the `loopCompoundCondition` query + "c/misra/loop-compound-condition" and + ruleId = "RULE-15-6" and + category = "required" + or + query = + // `Query` instance for the `selectionCompoundCondition` query + Statements3Package::selectionCompoundConditionQuery() and + queryId = + // `@id` for the `selectionCompoundCondition` query + "c/misra/selection-compound-condition" and + ruleId = "RULE-15-6" and + category = "required" + or + query = + // `Query` instance for the `ifElseEndCondition` query + Statements3Package::ifElseEndConditionQuery() and + queryId = + // `@id` for the `ifElseEndCondition` query + "c/misra/if-else-end-condition" and + ruleId = "RULE-15-7" and + category = "required" + or + query = + // `Query` instance for the `switchCaseStartCondition` query + Statements3Package::switchCaseStartConditionQuery() and + queryId = + // `@id` for the `switchCaseStartCondition` query + "c/misra/switch-case-start-condition" and + ruleId = "RULE-16-1" and + category = "required" + or + query = + // `Query` instance for the `switchStmtNotWellFormed` query + Statements3Package::switchStmtNotWellFormedQuery() and + queryId = + // `@id` for the `switchStmtNotWellFormed` query + "c/misra/switch-stmt-not-well-formed" and + ruleId = "RULE-16-1" and + category = "required" + or + query = + // `Query` instance for the `recursiveFunctionCondition` query + Statements3Package::recursiveFunctionConditionQuery() and + queryId = + // `@id` for the `recursiveFunctionCondition` query + "c/misra/recursive-function-condition" and + ruleId = "RULE-17-2" and + category = "required" +} + +module Statements3Package { + Query switchCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchCompoundCondition` query + TQueryC(TStatements3PackageQuery(TSwitchCompoundConditionQuery())) + } + + Query loopCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `loopCompoundCondition` query + TQueryC(TStatements3PackageQuery(TLoopCompoundConditionQuery())) + } + + Query selectionCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `selectionCompoundCondition` query + TQueryC(TStatements3PackageQuery(TSelectionCompoundConditionQuery())) + } + + Query ifElseEndConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `ifElseEndCondition` query + TQueryC(TStatements3PackageQuery(TIfElseEndConditionQuery())) + } + + Query switchCaseStartConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchCaseStartCondition` query + TQueryC(TStatements3PackageQuery(TSwitchCaseStartConditionQuery())) + } + + Query switchStmtNotWellFormedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchStmtNotWellFormed` query + TQueryC(TStatements3PackageQuery(TSwitchStmtNotWellFormedQuery())) + } + + Query recursiveFunctionConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `recursiveFunctionCondition` query + TQueryC(TStatements3PackageQuery(TRecursiveFunctionConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements4.qll new file mode 100644 index 0000000000..b46cd2207b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements4.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements4Query = + TFloatingPointLoopCountersQuery() or + TForLoopNotWellFormedQuery() or + TNonBooleanIfConditionQuery() or + TNonBooleanIterationConditionQuery() + +predicate isStatements4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `floatingPointLoopCounters` query + Statements4Package::floatingPointLoopCountersQuery() and + queryId = + // `@id` for the `floatingPointLoopCounters` query + "c/cert/floating-point-loop-counters" and + ruleId = "FLP30-C" and + category = "rule" + or + query = + // `Query` instance for the `forLoopNotWellFormed` query + Statements4Package::forLoopNotWellFormedQuery() and + queryId = + // `@id` for the `forLoopNotWellFormed` query + "c/misra/for-loop-not-well-formed" and + ruleId = "RULE-14-2" and + category = "required" + or + query = + // `Query` instance for the `nonBooleanIfCondition` query + Statements4Package::nonBooleanIfConditionQuery() and + queryId = + // `@id` for the `nonBooleanIfCondition` query + "c/misra/non-boolean-if-condition" and + ruleId = "RULE-14-4" and + category = "required" + or + query = + // `Query` instance for the `nonBooleanIterationCondition` query + Statements4Package::nonBooleanIterationConditionQuery() and + queryId = + // `@id` for the `nonBooleanIterationCondition` query + "c/misra/non-boolean-iteration-condition" and + ruleId = "RULE-14-4" and + category = "required" +} + +module Statements4Package { + Query floatingPointLoopCountersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `floatingPointLoopCounters` query + TQueryC(TStatements4PackageQuery(TFloatingPointLoopCountersQuery())) + } + + Query forLoopNotWellFormedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `forLoopNotWellFormed` query + TQueryC(TStatements4PackageQuery(TForLoopNotWellFormedQuery())) + } + + Query nonBooleanIfConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonBooleanIfCondition` query + TQueryC(TStatements4PackageQuery(TNonBooleanIfConditionQuery())) + } + + Query nonBooleanIterationConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonBooleanIterationCondition` query + TQueryC(TStatements4PackageQuery(TNonBooleanIterationConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll new file mode 100644 index 0000000000..d8312d11d7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements5Query = + TControllingExprInvariantQuery() or + TFunctionReturnConditionQuery() or + TNonVoidFunctionReturnConditionQuery() + +predicate isStatements5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `controllingExprInvariant` query + Statements5Package::controllingExprInvariantQuery() and + queryId = + // `@id` for the `controllingExprInvariant` query + "c/misra/controlling-expr-invariant" and + ruleId = "RULE-14-3" and + category = "required" + or + query = + // `Query` instance for the `functionReturnCondition` query + Statements5Package::functionReturnConditionQuery() and + queryId = + // `@id` for the `functionReturnCondition` query + "c/misra/function-return-condition" and + ruleId = "RULE-15-5" and + category = "advisory" + or + query = + // `Query` instance for the `nonVoidFunctionReturnCondition` query + Statements5Package::nonVoidFunctionReturnConditionQuery() and + queryId = + // `@id` for the `nonVoidFunctionReturnCondition` query + "c/misra/non-void-function-return-condition" and + ruleId = "RULE-17-4" and + category = "mandatory" +} + +module Statements5Package { + Query controllingExprInvariantQuery() { + //autogenerate `Query` type + result = + // `Query` type for `controllingExprInvariant` query + TQueryC(TStatements5PackageQuery(TControllingExprInvariantQuery())) + } + + Query functionReturnConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionReturnCondition` query + TQueryC(TStatements5PackageQuery(TFunctionReturnConditionQuery())) + } + + Query nonVoidFunctionReturnConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonVoidFunctionReturnCondition` query + TQueryC(TStatements5PackageQuery(TNonVoidFunctionReturnConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements6.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements6.qll new file mode 100644 index 0000000000..7261d0980a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements6.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements6Query = TGotoStatementUsedQuery() + +predicate isStatements6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `gotoStatementUsed` query + Statements6Package::gotoStatementUsedQuery() and + queryId = + // `@id` for the `gotoStatementUsed` query + "c/misra/goto-statement-used" and + ruleId = "RULE-15-1" and + category = "advisory" +} + +module Statements6Package { + Query gotoStatementUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoStatementUsed` query + TQueryC(TStatements6PackageQuery(TGotoStatementUsedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Static.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Static.qll new file mode 100644 index 0000000000..92b07dd448 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Static.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype StaticQuery = TUseOfArrayStaticQuery() + +predicate isStaticQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `useOfArrayStatic` query + StaticPackage::useOfArrayStaticQuery() and + queryId = + // `@id` for the `useOfArrayStatic` query + "c/misra/use-of-array-static" and + ruleId = "RULE-17-6" and + category = "mandatory" +} + +module StaticPackage { + Query useOfArrayStaticQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useOfArrayStatic` query + TQueryC(TStaticPackageQuery(TUseOfArrayStaticQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Strings1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Strings1.qll index 7a401ea5b3..e306df55bf 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Strings1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Strings1.qll @@ -8,14 +8,15 @@ newtype Strings1Query = TStringsHasSufficientSpaceForTheNullTerminatorQuery() or TNonNullTerminatedToFunctionThatExpectsAStringQuery() -predicate isStrings1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isStrings1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotAttemptToModifyStringLiterals` query Strings1Package::doNotAttemptToModifyStringLiteralsQuery() and queryId = // `@id` for the `doNotAttemptToModifyStringLiterals` query "c/cert/do-not-attempt-to-modify-string-literals" and - ruleId = "STR30-C" + ruleId = "STR30-C" and + category = "rule" or query = // `Query` instance for the `stringsHasSufficientSpaceForTheNullTerminator` query @@ -23,7 +24,8 @@ predicate isStrings1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `stringsHasSufficientSpaceForTheNullTerminator` query "c/cert/strings-has-sufficient-space-for-the-null-terminator" and - ruleId = "STR31-C" + ruleId = "STR31-C" and + category = "rule" or query = // `Query` instance for the `nonNullTerminatedToFunctionThatExpectsAString` query @@ -31,7 +33,8 @@ predicate isStrings1QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonNullTerminatedToFunctionThatExpectsAString` query "c/cert/non-null-terminated-to-function-that-expects-a-string" and - ruleId = "STR32-C" + ruleId = "STR32-C" and + category = "rule" } module Strings1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Strings2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Strings2.qll index 4c5b6b8816..99dd98d68e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Strings2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Strings2.qll @@ -5,14 +5,15 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype Strings2Query = TToCharacterHandlingFunctionsRepresentableAsUCharQuery() -predicate isStrings2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isStrings2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `toCharacterHandlingFunctionsRepresentableAsUChar` query Strings2Package::toCharacterHandlingFunctionsRepresentableAsUCharQuery() and queryId = // `@id` for the `toCharacterHandlingFunctionsRepresentableAsUChar` query "c/cert/to-character-handling-functions-representable-as-u-char" and - ruleId = "STR37-C" + ruleId = "STR37-C" and + category = "rule" } module Strings2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Strings3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Strings3.qll index ab837108c9..760f54b9fa 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Strings3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Strings3.qll @@ -7,14 +7,15 @@ newtype Strings3Query = TCastCharBeforeConvertingToLargerSizesQuery() or TDoNotConfuseNarrowAndWideFunctionsQuery() -predicate isStrings3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isStrings3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `castCharBeforeConvertingToLargerSizes` query Strings3Package::castCharBeforeConvertingToLargerSizesQuery() and queryId = // `@id` for the `castCharBeforeConvertingToLargerSizes` query "c/cert/cast-char-before-converting-to-larger-sizes" and - ruleId = "STR34-C" + ruleId = "STR34-C" and + category = "rule" or query = // `Query` instance for the `doNotConfuseNarrowAndWideFunctions` query @@ -22,7 +23,8 @@ predicate isStrings3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotConfuseNarrowAndWideFunctions` query "c/cert/do-not-confuse-narrow-and-wide-functions" and - ruleId = "STR38-C" + ruleId = "STR38-C" and + category = "rule" } module Strings3Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Syntax.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Syntax.qll index 8109741e20..3a53cf05c3 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Syntax.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Syntax.qll @@ -12,14 +12,15 @@ newtype SyntaxQuery = TUOrUSuffixRepresentedInUnsignedTypeQuery() or TLowercaseCharacterLUsedInLiteralSuffixQuery() -predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { +predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `characterSequencesAndUsedWithinAComment` query SyntaxPackage::characterSequencesAndUsedWithinACommentQuery() and queryId = // `@id` for the `characterSequencesAndUsedWithinAComment` query "c/misra/character-sequences-and-used-within-a-comment" and - ruleId = "RULE-3-1" + ruleId = "RULE-3-1" and + category = "required" or query = // `Query` instance for the `lineSplicingUsedInComments` query @@ -27,7 +28,8 @@ predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lineSplicingUsedInComments` query "c/misra/line-splicing-used-in-comments" and - ruleId = "RULE-3-2" + ruleId = "RULE-3-2" and + category = "required" or query = // `Query` instance for the `octalAndHexadecimalEscapeSequencesNotTerminated` query @@ -35,7 +37,8 @@ predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `octalAndHexadecimalEscapeSequencesNotTerminated` query "c/misra/octal-and-hexadecimal-escape-sequences-not-terminated" and - ruleId = "RULE-4-1" + ruleId = "RULE-4-1" and + category = "required" or query = // `Query` instance for the `sectionsOfCodeShallNotBeCommentedOut` query @@ -43,7 +46,8 @@ predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `sectionsOfCodeShallNotBeCommentedOut` query "c/misra/sections-of-code-shall-not-be-commented-out" and - ruleId = "RULE-4-4" + ruleId = "DIR-4-4" and + category = "advisory" or query = // `Query` instance for the `identifiersInTheSameNameSpaceUnambiguous` query @@ -51,7 +55,8 @@ predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifiersInTheSameNameSpaceUnambiguous` query "c/misra/identifiers-in-the-same-name-space-unambiguous" and - ruleId = "DIR-4-5" + ruleId = "DIR-4-5" and + category = "advisory" or query = // `Query` instance for the `uOrUSuffixRepresentedInUnsignedType` query @@ -59,7 +64,8 @@ predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `uOrUSuffixRepresentedInUnsignedType` query "c/misra/u-or-u-suffix-represented-in-unsigned-type" and - ruleId = "RULE-7-2" + ruleId = "RULE-7-2" and + category = "required" or query = // `Query` instance for the `lowercaseCharacterLUsedInLiteralSuffix` query @@ -67,7 +73,8 @@ predicate isSyntaxQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lowercaseCharacterLUsedInLiteralSuffix` query "c/misra/lowercase-character-l-used-in-literal-suffix" and - ruleId = "RULE-7-3" + ruleId = "RULE-7-3" and + category = "required" } module SyntaxPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll new file mode 100644 index 0000000000..ab7333b4c0 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Types1Query = + TExprShiftedbyNegativeOrGreaterPrecisionOperandQuery() or + TConvertingAPointerToIntegerOrIntegerToPointerQuery() or + TPlainNumericalTypeUsedOverExplicitTypedefQuery() or + TSizeofOperatorUsedOnArrayTypeParamQuery() or + TStringLiteralAssignedToNonConstCharQuery() + +predicate isTypes1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `exprShiftedbyNegativeOrGreaterPrecisionOperand` query + Types1Package::exprShiftedbyNegativeOrGreaterPrecisionOperandQuery() and + queryId = + // `@id` for the `exprShiftedbyNegativeOrGreaterPrecisionOperand` query + "c/cert/expr-shiftedby-negative-or-greater-precision-operand" and + ruleId = "INT34-C" and + category = "rule" + or + query = + // `Query` instance for the `convertingAPointerToIntegerOrIntegerToPointer` query + Types1Package::convertingAPointerToIntegerOrIntegerToPointerQuery() and + queryId = + // `@id` for the `convertingAPointerToIntegerOrIntegerToPointer` query + "c/cert/converting-a-pointer-to-integer-or-integer-to-pointer" and + ruleId = "INT36-C" and + category = "rule" + or + query = + // `Query` instance for the `plainNumericalTypeUsedOverExplicitTypedef` query + Types1Package::plainNumericalTypeUsedOverExplicitTypedefQuery() and + queryId = + // `@id` for the `plainNumericalTypeUsedOverExplicitTypedef` query + "c/misra/plain-numerical-type-used-over-explicit-typedef" and + ruleId = "DIR-4-6" and + category = "advisory" + or + query = + // `Query` instance for the `sizeofOperatorUsedOnArrayTypeParam` query + Types1Package::sizeofOperatorUsedOnArrayTypeParamQuery() and + queryId = + // `@id` for the `sizeofOperatorUsedOnArrayTypeParam` query + "c/misra/sizeof-operator-used-on-array-type-param" and + ruleId = "RULE-12-5" and + category = "mandatory" + or + query = + // `Query` instance for the `stringLiteralAssignedToNonConstChar` query + Types1Package::stringLiteralAssignedToNonConstCharQuery() and + queryId = + // `@id` for the `stringLiteralAssignedToNonConstChar` query + "c/misra/string-literal-assigned-to-non-const-char" and + ruleId = "RULE-7-4" and + category = "required" +} + +module Types1Package { + Query exprShiftedbyNegativeOrGreaterPrecisionOperandQuery() { + //autogenerate `Query` type + result = + // `Query` type for `exprShiftedbyNegativeOrGreaterPrecisionOperand` query + TQueryC(TTypes1PackageQuery(TExprShiftedbyNegativeOrGreaterPrecisionOperandQuery())) + } + + Query convertingAPointerToIntegerOrIntegerToPointerQuery() { + //autogenerate `Query` type + result = + // `Query` type for `convertingAPointerToIntegerOrIntegerToPointer` query + TQueryC(TTypes1PackageQuery(TConvertingAPointerToIntegerOrIntegerToPointerQuery())) + } + + Query plainNumericalTypeUsedOverExplicitTypedefQuery() { + //autogenerate `Query` type + result = + // `Query` type for `plainNumericalTypeUsedOverExplicitTypedef` query + TQueryC(TTypes1PackageQuery(TPlainNumericalTypeUsedOverExplicitTypedefQuery())) + } + + Query sizeofOperatorUsedOnArrayTypeParamQuery() { + //autogenerate `Query` type + result = + // `Query` type for `sizeofOperatorUsedOnArrayTypeParam` query + TQueryC(TTypes1PackageQuery(TSizeofOperatorUsedOnArrayTypeParamQuery())) + } + + Query stringLiteralAssignedToNonConstCharQuery() { + //autogenerate `Query` type + result = + // `Query` type for `stringLiteralAssignedToNonConstChar` query + TQueryC(TTypes1PackageQuery(TStringLiteralAssignedToNonConstCharQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Types2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Types2.qll new file mode 100644 index 0000000000..3b2d3a4342 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Types2.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Types2Query = + TInvalidIntegerConstantMacroArgumentQuery() or + TInvalidLiteralForIntegerConstantMacroArgumentQuery() or + TIntegerConstantMacroArgumentUsesSuffixQuery() or + TIncorrectlySizedIntegerConstantMacroArgumentQuery() or + TUseOfBannedSmallIntegerConstantMacroQuery() + +predicate isTypes2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `invalidIntegerConstantMacroArgument` query + Types2Package::invalidIntegerConstantMacroArgumentQuery() and + queryId = + // `@id` for the `invalidIntegerConstantMacroArgument` query + "c/misra/invalid-integer-constant-macro-argument" and + ruleId = "RULE-7-5" and + category = "required" + or + query = + // `Query` instance for the `invalidLiteralForIntegerConstantMacroArgument` query + Types2Package::invalidLiteralForIntegerConstantMacroArgumentQuery() and + queryId = + // `@id` for the `invalidLiteralForIntegerConstantMacroArgument` query + "c/misra/invalid-literal-for-integer-constant-macro-argument" and + ruleId = "RULE-7-5" and + category = "required" + or + query = + // `Query` instance for the `integerConstantMacroArgumentUsesSuffix` query + Types2Package::integerConstantMacroArgumentUsesSuffixQuery() and + queryId = + // `@id` for the `integerConstantMacroArgumentUsesSuffix` query + "c/misra/integer-constant-macro-argument-uses-suffix" and + ruleId = "RULE-7-5" and + category = "required" + or + query = + // `Query` instance for the `incorrectlySizedIntegerConstantMacroArgument` query + Types2Package::incorrectlySizedIntegerConstantMacroArgumentQuery() and + queryId = + // `@id` for the `incorrectlySizedIntegerConstantMacroArgument` query + "c/misra/incorrectly-sized-integer-constant-macro-argument" and + ruleId = "RULE-7-5" and + category = "required" + or + query = + // `Query` instance for the `useOfBannedSmallIntegerConstantMacro` query + Types2Package::useOfBannedSmallIntegerConstantMacroQuery() and + queryId = + // `@id` for the `useOfBannedSmallIntegerConstantMacro` query + "c/misra/use-of-banned-small-integer-constant-macro" and + ruleId = "RULE-7-6" and + category = "required" +} + +module Types2Package { + Query invalidIntegerConstantMacroArgumentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidIntegerConstantMacroArgument` query + TQueryC(TTypes2PackageQuery(TInvalidIntegerConstantMacroArgumentQuery())) + } + + Query invalidLiteralForIntegerConstantMacroArgumentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidLiteralForIntegerConstantMacroArgument` query + TQueryC(TTypes2PackageQuery(TInvalidLiteralForIntegerConstantMacroArgumentQuery())) + } + + Query integerConstantMacroArgumentUsesSuffixQuery() { + //autogenerate `Query` type + result = + // `Query` type for `integerConstantMacroArgumentUsesSuffix` query + TQueryC(TTypes2PackageQuery(TIntegerConstantMacroArgumentUsesSuffixQuery())) + } + + Query incorrectlySizedIntegerConstantMacroArgumentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `incorrectlySizedIntegerConstantMacroArgument` query + TQueryC(TTypes2PackageQuery(TIncorrectlySizedIntegerConstantMacroArgumentQuery())) + } + + Query useOfBannedSmallIntegerConstantMacroQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useOfBannedSmallIntegerConstantMacro` query + TQueryC(TTypes2PackageQuery(TUseOfBannedSmallIntegerConstantMacroQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Allocations.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Allocations.qll index e4550ad95e..56bdf2b954 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Allocations.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Allocations.qll @@ -26,14 +26,15 @@ newtype AllocationsQuery = TOperatorDeleteMissingPartnerCertQuery() or TUsingDefaultOperatorNewForOverAlignedTypesQuery() -predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `placementNewNotProperlyAlignedAutosar` query AllocationsPackage::placementNewNotProperlyAlignedAutosarQuery() and queryId = // `@id` for the `placementNewNotProperlyAlignedAutosar` query "cpp/autosar/placement-new-not-properly-aligned-autosar" and - ruleId = "A18-5-10" + ruleId = "A18-5-10" and + category = "required" or query = // `Query` instance for the `placementNewInsufficientStorageAutosar` query @@ -41,7 +42,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `placementNewInsufficientStorageAutosar` query "cpp/autosar/placement-new-insufficient-storage-autosar" and - ruleId = "A18-5-10" + ruleId = "A18-5-10" and + category = "required" or query = // `Query` instance for the `doNotUseNonPlacementNew` query @@ -49,7 +51,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotUseNonPlacementNew` query "cpp/autosar/do-not-use-non-placement-new" and - ruleId = "A18-5-2" + ruleId = "A18-5-2" and + category = "required" or query = // `Query` instance for the `doNotUseNonPlacementDelete` query @@ -57,7 +60,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotUseNonPlacementDelete` query "cpp/autosar/do-not-use-non-placement-delete" and - ruleId = "A18-5-2" + ruleId = "A18-5-2" and + category = "required" or query = // `Query` instance for the `dynamicMemoryManagementFailureMode` query @@ -65,7 +69,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `dynamicMemoryManagementFailureMode` query "cpp/autosar/dynamic-memory-management-failure-mode" and - ruleId = "A18-5-6" + ruleId = "A18-5-6" and + category = "required" or query = // `Query` instance for the `unnecessaryUseOfDynamicStorage` query @@ -73,7 +78,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `unnecessaryUseOfDynamicStorage` query "cpp/autosar/unnecessary-use-of-dynamic-storage" and - ruleId = "A18-5-8" + ruleId = "A18-5-8" and + category = "required" or query = // `Query` instance for the `throwingOperatorNewReturnsNullAutosar` query @@ -81,7 +87,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `throwingOperatorNewReturnsNullAutosar` query "cpp/autosar/throwing-operator-new-returns-null-autosar" and - ruleId = "A18-5-9" + ruleId = "A18-5-9" and + category = "required" or query = // `Query` instance for the `throwingOperatorNewThrowsInvalidExceptionAutosar` query @@ -89,7 +96,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `throwingOperatorNewThrowsInvalidExceptionAutosar` query "cpp/autosar/throwing-operator-new-throws-invalid-exception-autosar" and - ruleId = "A18-5-9" + ruleId = "A18-5-9" and + category = "required" or query = // `Query` instance for the `throwingNoThrowOperatorNewDeleteAutosar` query @@ -97,7 +105,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `throwingNoThrowOperatorNewDeleteAutosar` query "cpp/autosar/throwing-no-throw-operator-new-delete-autosar" and - ruleId = "A18-5-9" + ruleId = "A18-5-9" and + category = "required" or query = // `Query` instance for the `operatorDeleteMissingPartnerAutosar` query @@ -105,7 +114,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `operatorDeleteMissingPartnerAutosar` query "cpp/autosar/operator-delete-missing-partner-autosar" and - ruleId = "A18-5-9" + ruleId = "A18-5-9" and + category = "required" or query = // `Query` instance for the `properlyDeallocateDynamicallyAllocatedResources` query @@ -113,7 +123,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `properlyDeallocateDynamicallyAllocatedResources` query "cpp/cert/properly-deallocate-dynamically-allocated-resources" and - ruleId = "MEM51-CPP" + ruleId = "MEM51-CPP" and + category = "rule" or query = // `Query` instance for the `detectAndHandleMemoryAllocationErrors` query @@ -121,7 +132,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `detectAndHandleMemoryAllocationErrors` query "cpp/cert/detect-and-handle-memory-allocation-errors" and - ruleId = "MEM52-CPP" + ruleId = "MEM52-CPP" and + category = "rule" or query = // `Query` instance for the `missingConstructorCallForManuallyManagedObject` query @@ -129,7 +141,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `missingConstructorCallForManuallyManagedObject` query "cpp/cert/missing-constructor-call-for-manually-managed-object" and - ruleId = "MEM53-CPP" + ruleId = "MEM53-CPP" and + category = "rule" or query = // `Query` instance for the `missingDestructorCallForManuallyManagedObject` query @@ -137,7 +150,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `missingDestructorCallForManuallyManagedObject` query "cpp/cert/missing-destructor-call-for-manually-managed-object" and - ruleId = "MEM53-CPP" + ruleId = "MEM53-CPP" and + category = "rule" or query = // `Query` instance for the `placementNewNotProperlyAlignedCert` query @@ -145,7 +159,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `placementNewNotProperlyAlignedCert` query "cpp/cert/placement-new-not-properly-aligned-cert" and - ruleId = "MEM54-CPP" + ruleId = "MEM54-CPP" and + category = "rule" or query = // `Query` instance for the `placementNewInsufficientStorageCert` query @@ -153,7 +168,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `placementNewInsufficientStorageCert` query "cpp/cert/placement-new-insufficient-storage-cert" and - ruleId = "MEM54-CPP" + ruleId = "MEM54-CPP" and + category = "rule" or query = // `Query` instance for the `throwingOperatorNewReturnsNullCert` query @@ -161,7 +177,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `throwingOperatorNewReturnsNullCert` query "cpp/cert/throwing-operator-new-returns-null-cert" and - ruleId = "MEM55-CPP" + ruleId = "MEM55-CPP" and + category = "rule" or query = // `Query` instance for the `throwingOperatorNewThrowsInvalidExceptionCert` query @@ -169,7 +186,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `throwingOperatorNewThrowsInvalidExceptionCert` query "cpp/cert/throwing-operator-new-throws-invalid-exception-cert" and - ruleId = "MEM55-CPP" + ruleId = "MEM55-CPP" and + category = "rule" or query = // `Query` instance for the `throwingNoThrowOperatorNewDeleteCert` query @@ -177,7 +195,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `throwingNoThrowOperatorNewDeleteCert` query "cpp/cert/throwing-no-throw-operator-new-delete-cert" and - ruleId = "MEM55-CPP" + ruleId = "MEM55-CPP" and + category = "rule" or query = // `Query` instance for the `operatorDeleteMissingPartnerCert` query @@ -185,7 +204,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `operatorDeleteMissingPartnerCert` query "cpp/cert/operator-delete-missing-partner-cert" and - ruleId = "MEM55-CPP" + ruleId = "MEM55-CPP" and + category = "rule" or query = // `Query` instance for the `usingDefaultOperatorNewForOverAlignedTypes` query @@ -193,7 +213,8 @@ predicate isAllocationsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `usingDefaultOperatorNewForOverAlignedTypes` query "cpp/cert/using-default-operator-new-for-over-aligned-types" and - ruleId = "MEM57-CPP" + ruleId = "MEM57-CPP" and + category = "rule" } module AllocationsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedFunctions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedFunctions.qll index bd611a42ec..c17972650b 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedFunctions.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedFunctions.qll @@ -16,14 +16,15 @@ newtype BannedFunctionsQuery = TDoNotUseRandForGeneratingPseudorandomNumbersQuery() or TPreferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctionsQuery() -predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `functionsMallocCallocReallocAndFreeUsed` query BannedFunctionsPackage::functionsMallocCallocReallocAndFreeUsedQuery() and queryId = // `@id` for the `functionsMallocCallocReallocAndFreeUsed` query "cpp/autosar/functions-malloc-calloc-realloc-and-free-used" and - ruleId = "A18-5-1" + ruleId = "A18-5-1" and + category = "required" or query = // `Query` instance for the `bindUsed` query @@ -31,7 +32,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `bindUsed` query "cpp/autosar/bind-used" and - ruleId = "A18-9-1" + ruleId = "A18-9-1" and + category = "required" or query = // `Query` instance for the `pseudorandomNumbersGeneratedUsingRand` query @@ -39,7 +41,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `pseudorandomNumbersGeneratedUsingRand` query "cpp/autosar/pseudorandom-numbers-generated-using-rand" and - ruleId = "A26-5-1" + ruleId = "A26-5-1" and + category = "required" or query = // `Query` instance for the `setjmpMacroAndTheLongjmpFunctionUsed` query @@ -47,7 +50,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `setjmpMacroAndTheLongjmpFunctionUsed` query "cpp/autosar/setjmp-macro-and-the-longjmp-function-used" and - ruleId = "M17-0-5" + ruleId = "M17-0-5" and + category = "required" or query = // `Query` instance for the `libraryFunctionsAbortExitGetenvAndSystemFromLibraryCstdlibUsed` query @@ -55,7 +59,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `libraryFunctionsAbortExitGetenvAndSystemFromLibraryCstdlibUsed` query "cpp/autosar/library-functions-abort-exit-getenv-and-system-from-library-cstdlib-used" and - ruleId = "M18-0-3" + ruleId = "M18-0-3" and + category = "required" or query = // `Query` instance for the `timeHandlingFunctionsOfLibraryCtimeUsed` query @@ -63,7 +68,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `timeHandlingFunctionsOfLibraryCtimeUsed` query "cpp/autosar/time-handling-functions-of-library-ctime-used" and - ruleId = "M18-0-4" + ruleId = "M18-0-4" and + category = "required" or query = // `Query` instance for the `unboundedFunctionsOfLibraryCstringUsed` query @@ -71,7 +77,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `unboundedFunctionsOfLibraryCstringUsed` query "cpp/autosar/unbounded-functions-of-library-cstring-used" and - ruleId = "M18-0-5" + ruleId = "M18-0-5" and + category = "required" or query = // `Query` instance for the `macroOffsetofUsed` query @@ -79,7 +86,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `macroOffsetofUsed` query "cpp/autosar/macro-offsetof-used" and - ruleId = "M18-2-1" + ruleId = "M18-2-1" and + category = "required" or query = // `Query` instance for the `doNotUseSetjmpOrLongjmp` query @@ -87,7 +95,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `doNotUseSetjmpOrLongjmp` query "cpp/cert/do-not-use-setjmp-or-longjmp" and - ruleId = "ERR52-CPP" + ruleId = "ERR52-CPP" and + category = "rule" or query = // `Query` instance for the `doNotUseRandForGeneratingPseudorandomNumbers` query @@ -95,7 +104,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `doNotUseRandForGeneratingPseudorandomNumbers` query "cpp/cert/do-not-use-rand-for-generating-pseudorandom-numbers" and - ruleId = "MSC50-CPP" + ruleId = "MSC50-CPP" and + category = "rule" or query = // `Query` instance for the `preferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions` query @@ -103,7 +113,8 @@ predicate isBannedFunctionsQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `preferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions` query "cpp/cert/prefer-special-member-functions-and-overloaded-operators-to-c-standard-library-functions" and - ruleId = "OOP57-CPP" + ruleId = "OOP57-CPP" and + category = "rule" } module BannedFunctionsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedLibraries.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedLibraries.qll index 0672896465..9a314e79c6 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedLibraries.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedLibraries.qll @@ -17,14 +17,15 @@ newtype BannedLibrariesQuery = TCstdioTypesUsedQuery() or TUsageOfAssemblerNotDocumentedQuery() -predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `reservedIdentifiersMacrosAndFunctionsAreDefinedRedefinedOrUndefined` query BannedLibrariesPackage::reservedIdentifiersMacrosAndFunctionsAreDefinedRedefinedOrUndefinedQuery() and queryId = // `@id` for the `reservedIdentifiersMacrosAndFunctionsAreDefinedRedefinedOrUndefined` query "cpp/autosar/reserved-identifiers-macros-and-functions-are-defined-redefined-or-undefined" and - ruleId = "A17-0-1" + ruleId = "A17-0-1" and + category = "required" or query = // `Query` instance for the `cLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders` query @@ -32,7 +33,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `cLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders` query "cpp/autosar/c-library-facilities-not-accessed-through-cpp-library-headers" and - ruleId = "A18-0-1" + ruleId = "A18-0-1" and + category = "required" or query = // `Query` instance for the `localeFunctionsUsed` query @@ -40,7 +42,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `localeFunctionsUsed` query "cpp/autosar/locale-functions-used" and - ruleId = "A18-0-3" + ruleId = "A18-0-3" and + category = "required" or query = // `Query` instance for the `localeMacrosUsed` query @@ -48,7 +51,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `localeMacrosUsed` query "cpp/autosar/locale-macros-used" and - ruleId = "A18-0-3" + ruleId = "A18-0-3" and + category = "required" or query = // `Query` instance for the `localeTypeLConvUsed` query @@ -56,7 +60,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `localeTypeLConvUsed` query "cpp/autosar/locale-type-l-conv-used" and - ruleId = "A18-0-3" + ruleId = "A18-0-3" and + category = "required" or query = // `Query` instance for the `csignalFunctionsUsed` query @@ -64,7 +69,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `csignalFunctionsUsed` query "cpp/autosar/csignal-functions-used" and - ruleId = "M18-7-1" + ruleId = "M18-7-1" and + category = "required" or query = // `Query` instance for the `csignalTypesUsed` query @@ -72,7 +78,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `csignalTypesUsed` query "cpp/autosar/csignal-types-used" and - ruleId = "M18-7-1" + ruleId = "M18-7-1" and + category = "required" or query = // `Query` instance for the `errnoUsed` query @@ -80,7 +87,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `errnoUsed` query "cpp/autosar/errno-used" and - ruleId = "M19-3-1" + ruleId = "M19-3-1" and + category = "required" or query = // `Query` instance for the `cstdioFunctionsUsed` query @@ -88,7 +96,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `cstdioFunctionsUsed` query "cpp/autosar/cstdio-functions-used" and - ruleId = "M27-0-1" + ruleId = "M27-0-1" and + category = "required" or query = // `Query` instance for the `cstdioMacrosUsed` query @@ -96,7 +105,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `cstdioMacrosUsed` query "cpp/autosar/cstdio-macros-used" and - ruleId = "M27-0-1" + ruleId = "M27-0-1" and + category = "required" or query = // `Query` instance for the `cstdioTypesUsed` query @@ -104,7 +114,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `cstdioTypesUsed` query "cpp/autosar/cstdio-types-used" and - ruleId = "M27-0-1" + ruleId = "M27-0-1" and + category = "required" or query = // `Query` instance for the `usageOfAssemblerNotDocumented` query @@ -112,7 +123,8 @@ predicate isBannedLibrariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `usageOfAssemblerNotDocumented` query "cpp/autosar/usage-of-assembler-not-documented" and - ruleId = "M7-4-1" + ruleId = "M7-4-1" and + category = "required" } module BannedLibrariesPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedSyntax.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedSyntax.qll index bd9b0d4924..c473616374 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedSyntax.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedSyntax.qll @@ -22,14 +22,15 @@ newtype BannedSyntaxQuery = TUsingDeclarationsUsedInHeaderFilesQuery() or TDoNotDefineACStyleVariadicFunctionQuery() -predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId) { +predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `friendDeclarationsUsed` query BannedSyntaxPackage::friendDeclarationsUsedQuery() and queryId = // `@id` for the `friendDeclarationsUsed` query "cpp/autosar/friend-declarations-used" and - ruleId = "A11-3-1" + ruleId = "A11-3-1" and + category = "required" or query = // `Query` instance for the `cStyleArraysUsed` query @@ -37,7 +38,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `cStyleArraysUsed` query "cpp/autosar/c-style-arrays-used" and - ruleId = "A18-1-1" + ruleId = "A18-1-1" and + category = "required" or query = // `Query` instance for the `volatileKeywordUsed` query @@ -45,7 +47,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `volatileKeywordUsed` query "cpp/autosar/volatile-keyword-used" and - ruleId = "A2-11-1" + ruleId = "A2-11-1" and + category = "required" or query = // `Query` instance for the `ternaryConditionalOperatorUsedAsSubExpression` query @@ -53,7 +56,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `ternaryConditionalOperatorUsedAsSubExpression` query "cpp/autosar/ternary-conditional-operator-used-as-sub-expression" and - ruleId = "A5-16-1" + ruleId = "A5-16-1" and + category = "required" or query = // `Query` instance for the `dynamicCastShouldNotBeUsed` query @@ -61,7 +65,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `dynamicCastShouldNotBeUsed` query "cpp/autosar/dynamic-cast-should-not-be-used" and - ruleId = "A5-2-1" + ruleId = "A5-2-1" and + category = "advisory" or query = // `Query` instance for the `traditionalCStyleCastsUsed` query @@ -69,7 +74,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `traditionalCStyleCastsUsed` query "cpp/autosar/traditional-c-style-casts-used" and - ruleId = "A5-2-2" + ruleId = "A5-2-2" and + category = "required" or query = // `Query` instance for the `reinterpretCastUsed` query @@ -77,7 +83,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `reinterpretCastUsed` query "cpp/autosar/reinterpret-cast-used" and - ruleId = "A5-2-4" + ruleId = "A5-2-4" and + category = "required" or query = // `Query` instance for the `gotoStatementUsed` query @@ -85,7 +92,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `gotoStatementUsed` query "cpp/autosar/goto-statement-used" and - ruleId = "A6-6-1" + ruleId = "A6-6-1" and + category = "required" or query = // `Query` instance for the `registerKeywordUsed` query @@ -93,7 +101,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `registerKeywordUsed` query "cpp/autosar/register-keyword-used" and - ruleId = "A7-1-4" + ruleId = "A7-1-4" and + category = "required" or query = // `Query` instance for the `typedefSpecifierUsed` query @@ -101,7 +110,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `typedefSpecifierUsed` query "cpp/autosar/typedef-specifier-used" and - ruleId = "A7-1-6" + ruleId = "A7-1-6" and + category = "required" or query = // `Query` instance for the `asmDeclarationUsed` query @@ -109,7 +119,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `asmDeclarationUsed` query "cpp/autosar/asm-declaration-used" and - ruleId = "A7-4-1" + ruleId = "A7-4-1" and + category = "required" or query = // `Query` instance for the `functionsDefinedUsingTheEllipsisNotation` query @@ -117,7 +128,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `functionsDefinedUsingTheEllipsisNotation` query "cpp/autosar/functions-defined-using-the-ellipsis-notation" and - ruleId = "A8-4-1" + ruleId = "A8-4-1" and + category = "required" or query = // `Query` instance for the `unionsUsed` query @@ -125,7 +137,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `unionsUsed` query "cpp/autosar/unions-used" and - ruleId = "A9-5-1" + ruleId = "A9-5-1" and + category = "required" or query = // `Query` instance for the `commaOperatorUsed` query @@ -133,7 +146,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `commaOperatorUsed` query "cpp/autosar/comma-operator-used" and - ruleId = "M5-18-1" + ruleId = "M5-18-1" and + category = "required" or query = // `Query` instance for the `usingDirectivesUsed` query @@ -141,7 +155,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `usingDirectivesUsed` query "cpp/autosar/using-directives-used" and - ruleId = "M7-3-4" + ruleId = "M7-3-4" and + category = "required" or query = // `Query` instance for the `usingDeclarationsUsedInHeaderFiles` query @@ -149,7 +164,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `usingDeclarationsUsedInHeaderFiles` query "cpp/autosar/using-declarations-used-in-header-files" and - ruleId = "M7-3-6" + ruleId = "M7-3-6" and + category = "required" or query = // `Query` instance for the `doNotDefineACStyleVariadicFunction` query @@ -157,7 +173,8 @@ predicate isBannedSyntaxQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotDefineACStyleVariadicFunction` query "cpp/cert/do-not-define-ac-style-variadic-function" and - ruleId = "DCL50-CPP" + ruleId = "DCL50-CPP" and + category = "rule" } module BannedSyntaxPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedTypes.qll index a03f046633..8328a0f4d4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedTypes.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/BannedTypes.qll @@ -9,14 +9,15 @@ newtype BannedTypesQuery = TAutoPtrTypeUsedQuery() or TTypeWcharTUsedQuery() -predicate isBannedTypesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isBannedTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `typeLongDoubleUsed` query BannedTypesPackage::typeLongDoubleUsedQuery() and queryId = // `@id` for the `typeLongDoubleUsed` query "cpp/autosar/type-long-double-used" and - ruleId = "A0-4-2" + ruleId = "A0-4-2" and + category = "required" or query = // `Query` instance for the `vectorboolSpecializationUsed` query @@ -24,7 +25,8 @@ predicate isBannedTypesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `vectorboolSpecializationUsed` query "cpp/autosar/vectorbool-specialization-used" and - ruleId = "A18-1-2" + ruleId = "A18-1-2" and + category = "required" or query = // `Query` instance for the `autoPtrTypeUsed` query @@ -32,7 +34,8 @@ predicate isBannedTypesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `autoPtrTypeUsed` query "cpp/autosar/auto-ptr-type-used" and - ruleId = "A18-1-3" + ruleId = "A18-1-3" and + category = "required" or query = // `Query` instance for the `typeWcharTUsed` query @@ -40,7 +43,8 @@ predicate isBannedTypesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `typeWcharTUsed` query "cpp/autosar/type-wchar-t-used" and - ruleId = "A2-13-3" + ruleId = "A2-13-3" and + category = "required" } module BannedTypesPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes.qll index a185b76287..3daf48c696 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes.qll @@ -13,7 +13,6 @@ newtype ClassesQuery = TClassDataMembersInitializationConditionQuery() or TRedundantMemberFunctionsShouldBeDefaultedOrLeftUndefinedQuery() or TNonTemplateMemberDefinedInTemplateQuery() or - TTrivialOrTemplateFunctionDefinedOutsideClassDefinitionQuery() or TNonTrivialNonTemplateFunctionDefinedInsideClassDefinitionQuery() or TInParametersForNotCheapToCopyTypesNotPassedByReferenceQuery() or TInParametersForCheapToCopyTypesNotPassedByValueQuery() or @@ -23,14 +22,15 @@ newtype ClassesQuery = TMemberDataInNonPodClassTypesNotPrivateQuery() or TOffsetUsedOnInvalidTypeOrMemberQuery() -predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isClassesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `nonPodTypeShouldBeDefinedAsClass` query ClassesPackage::nonPodTypeShouldBeDefinedAsClassQuery() and queryId = // `@id` for the `nonPodTypeShouldBeDefinedAsClass` query "cpp/autosar/non-pod-type-should-be-defined-as-class" and - ruleId = "A11-0-1" + ruleId = "A11-0-1" and + category = "advisory" or query = // `Query` instance for the `typeDefinedAsStructHasOnlyPublicDataMembers` query @@ -38,7 +38,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `typeDefinedAsStructHasOnlyPublicDataMembers` query "cpp/autosar/type-defined-as-struct-has-only-public-data-members" and - ruleId = "A11-0-2" + ruleId = "A11-0-2" and + category = "required" or query = // `Query` instance for the `typeDefinedAsStructHasNoMethods` query @@ -46,7 +47,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `typeDefinedAsStructHasNoMethods` query "cpp/autosar/type-defined-as-struct-has-no-methods" and - ruleId = "A11-0-2" + ruleId = "A11-0-2" and + category = "required" or query = // `Query` instance for the `typeDefinedAsStructIsNotBaseOfOtherClassOrStruct` query @@ -54,7 +56,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `typeDefinedAsStructIsNotBaseOfOtherClassOrStruct` query "cpp/autosar/type-defined-as-struct-is-not-base-of-other-class-or-struct" and - ruleId = "A11-0-2" + ruleId = "A11-0-2" and + category = "required" or query = // `Query` instance for the `typeDefinedAsStructIsDoesNotInheritFromStructOrClass` query @@ -62,7 +65,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `typeDefinedAsStructIsDoesNotInheritFromStructOrClass` query "cpp/autosar/type-defined-as-struct-is-does-not-inherit-from-struct-or-class" and - ruleId = "A11-0-2" + ruleId = "A11-0-2" and + category = "required" or query = // `Query` instance for the `missingSpecialMemberFunction` query @@ -70,7 +74,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `missingSpecialMemberFunction` query "cpp/autosar/missing-special-member-function" and - ruleId = "A12-0-1" + ruleId = "A12-0-1" and + category = "required" or query = // `Query` instance for the `classDataMembersInitializationCondition` query @@ -78,7 +83,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `classDataMembersInitializationCondition` query "cpp/autosar/class-data-members-initialization-condition" and - ruleId = "A12-6-1" + ruleId = "A12-6-1" and + category = "required" or query = // `Query` instance for the `redundantMemberFunctionsShouldBeDefaultedOrLeftUndefined` query @@ -86,7 +92,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `redundantMemberFunctionsShouldBeDefaultedOrLeftUndefined` query "cpp/autosar/redundant-member-functions-should-be-defaulted-or-left-undefined" and - ruleId = "A12-7-1" + ruleId = "A12-7-1" and + category = "required" or query = // `Query` instance for the `nonTemplateMemberDefinedInTemplate` query @@ -94,15 +101,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonTemplateMemberDefinedInTemplate` query "cpp/autosar/non-template-member-defined-in-template" and - ruleId = "A14-5-2" - or - query = - // `Query` instance for the `trivialOrTemplateFunctionDefinedOutsideClassDefinition` query - ClassesPackage::trivialOrTemplateFunctionDefinedOutsideClassDefinitionQuery() and - queryId = - // `@id` for the `trivialOrTemplateFunctionDefinedOutsideClassDefinition` query - "cpp/autosar/trivial-or-template-function-defined-outside-class-definition" and - ruleId = "A3-1-5" + ruleId = "A14-5-2" and + category = "advisory" or query = // `Query` instance for the `nonTrivialNonTemplateFunctionDefinedInsideClassDefinition` query @@ -110,7 +110,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonTrivialNonTemplateFunctionDefinedInsideClassDefinition` query "cpp/autosar/non-trivial-non-template-function-defined-inside-class-definition" and - ruleId = "A3-1-5" + ruleId = "A3-1-5" and + category = "required" or query = // `Query` instance for the `inParametersForNotCheapToCopyTypesNotPassedByReference` query @@ -118,7 +119,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `inParametersForNotCheapToCopyTypesNotPassedByReference` query "cpp/autosar/in-parameters-for-not-cheap-to-copy-types-not-passed-by-reference" and - ruleId = "A8-4-7" + ruleId = "A8-4-7" and + category = "required" or query = // `Query` instance for the `inParametersForCheapToCopyTypesNotPassedByValue` query @@ -126,7 +128,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `inParametersForCheapToCopyTypesNotPassedByValue` query "cpp/autosar/in-parameters-for-cheap-to-copy-types-not-passed-by-value" and - ruleId = "A8-4-7" + ruleId = "A8-4-7" and + category = "required" or query = // `Query` instance for the `returnsNonConstRawPointersOrReferencesToPrivateOrProtectedData` query @@ -134,7 +137,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `returnsNonConstRawPointersOrReferencesToPrivateOrProtectedData` query "cpp/autosar/returns-non-const-raw-pointers-or-references-to-private-or-protected-data" and - ruleId = "A9-3-1" + ruleId = "A9-3-1" and + category = "required" or query = // `Query` instance for the `dataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayout` query @@ -142,7 +146,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `dataTypesUsedForInterfacingWithHardwareOrProtocolsMustBeTrivialAndStandardLayout` query "cpp/autosar/data-types-used-for-interfacing-with-hardware-or-protocols-must-be-trivial-and-standard-layout" and - ruleId = "A9-6-1" + ruleId = "A9-6-1" and + category = "required" or query = // `Query` instance for the `dataTypesUsedForInterfacingWithHardwareOrProtocolsMustContainOnlyDefinedDataTypeSizes` query @@ -150,7 +155,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `dataTypesUsedForInterfacingWithHardwareOrProtocolsMustContainOnlyDefinedDataTypeSizes` query "cpp/autosar/data-types-used-for-interfacing-with-hardware-or-protocols-must-contain-only-defined-data-type-sizes" and - ruleId = "A9-6-1" + ruleId = "A9-6-1" and + category = "required" or query = // `Query` instance for the `memberDataInNonPodClassTypesNotPrivate` query @@ -158,7 +164,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `memberDataInNonPodClassTypesNotPrivate` query "cpp/autosar/member-data-in-non-pod-class-types-not-private" and - ruleId = "M11-0-1" + ruleId = "M11-0-1" and + category = "required" or query = // `Query` instance for the `offsetUsedOnInvalidTypeOrMember` query @@ -166,7 +173,8 @@ predicate isClassesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `offsetUsedOnInvalidTypeOrMember` query "cpp/cert/offset-used-on-invalid-type-or-member" and - ruleId = "EXP59-CPP" + ruleId = "EXP59-CPP" and + category = "rule" } module ClassesPackage { @@ -233,13 +241,6 @@ module ClassesPackage { TQueryCPP(TClassesPackageQuery(TNonTemplateMemberDefinedInTemplateQuery())) } - Query trivialOrTemplateFunctionDefinedOutsideClassDefinitionQuery() { - //autogenerate `Query` type - result = - // `Query` type for `trivialOrTemplateFunctionDefinedOutsideClassDefinition` query - TQueryCPP(TClassesPackageQuery(TTrivialOrTemplateFunctionDefinedOutsideClassDefinitionQuery())) - } - Query nonTrivialNonTemplateFunctionDefinedInsideClassDefinitionQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Comments.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Comments.qll index 0c06102ec7..9d4d186122 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Comments.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Comments.qll @@ -10,14 +10,15 @@ newtype CommentsQuery = TSlashStarUsedWithinACStyleCommentQuery() or TNullOnSharedLineQuery() -predicate isCommentsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isCommentsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `singleLineCommentEndsWithSlash` query CommentsPackage::singleLineCommentEndsWithSlashQuery() and queryId = // `@id` for the `singleLineCommentEndsWithSlash` query "cpp/autosar/single-line-comment-ends-with-slash" and - ruleId = "A2-7-1" + ruleId = "A2-7-1" and + category = "required" or query = // `Query` instance for the `sectionsOfCodeCommentedOut` query @@ -25,7 +26,8 @@ predicate isCommentsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `sectionsOfCodeCommentedOut` query "cpp/autosar/sections-of-code-commented-out" and - ruleId = "A2-7-2" + ruleId = "A2-7-2" and + category = "required" or query = // `Query` instance for the `undocumentedUserDefinedType` query @@ -33,7 +35,8 @@ predicate isCommentsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `undocumentedUserDefinedType` query "cpp/autosar/undocumented-user-defined-type" and - ruleId = "A2-7-3" + ruleId = "A2-7-3" and + category = "required" or query = // `Query` instance for the `slashStarUsedWithinACStyleComment` query @@ -41,7 +44,8 @@ predicate isCommentsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `slashStarUsedWithinACStyleComment` query "cpp/autosar/slash-star-used-within-ac-style-comment" and - ruleId = "M2-7-1" + ruleId = "M2-7-1" and + category = "required" or query = // `Query` instance for the `nullOnSharedLine` query @@ -49,7 +53,8 @@ predicate isCommentsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nullOnSharedLine` query "cpp/autosar/null-on-shared-line" and - ruleId = "M6-2-3" + ruleId = "M6-2-3" and + category = "required" } module CommentsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Concurrency.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Concurrency.qll index 3a2696c880..8038d4e51e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Concurrency.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Concurrency.qll @@ -14,14 +14,15 @@ newtype ConcurrencyQuery = TDoNotSpeculativelyLockALockedNonRecursiveMutexQuery() or TLockedALockedNonRecursiveMutexAuditQuery() -predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) { +predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotAllowAMutexToGoOutOfScopeWhileLocked` query ConcurrencyPackage::doNotAllowAMutexToGoOutOfScopeWhileLockedQuery() and queryId = // `@id` for the `doNotAllowAMutexToGoOutOfScopeWhileLocked` query "cpp/cert/do-not-allow-a-mutex-to-go-out-of-scope-while-locked" and - ruleId = "CON50-CPP" + ruleId = "CON50-CPP" and + category = "rule" or query = // `Query` instance for the `doNotDestroyAMutexWhileItIsLocked` query @@ -29,7 +30,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotDestroyAMutexWhileItIsLocked` query "cpp/cert/do-not-destroy-a-mutex-while-it-is-locked" and - ruleId = "CON50-CPP" + ruleId = "CON50-CPP" and + category = "rule" or query = // `Query` instance for the `ensureActivelyHeldLocksAreReleasedOnExceptionalConditions` query @@ -37,7 +39,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `ensureActivelyHeldLocksAreReleasedOnExceptionalConditions` query "cpp/cert/ensure-actively-held-locks-are-released-on-exceptional-conditions" and - ruleId = "CON51-CPP" + ruleId = "CON51-CPP" and + category = "rule" or query = // `Query` instance for the `preventBitFieldAccessFromMultipleThreads` query @@ -45,7 +48,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `preventBitFieldAccessFromMultipleThreads` query "cpp/cert/prevent-bit-field-access-from-multiple-threads" and - ruleId = "CON52-CPP" + ruleId = "CON52-CPP" and + category = "rule" or query = // `Query` instance for the `deadlockByLockingInPredefinedOrder` query @@ -53,7 +57,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `deadlockByLockingInPredefinedOrder` query "cpp/cert/deadlock-by-locking-in-predefined-order" and - ruleId = "CON53-CPP" + ruleId = "CON53-CPP" and + category = "rule" or query = // `Query` instance for the `wrapFunctionsThatCanSpuriouslyWakeUpInLoop` query @@ -61,7 +66,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `wrapFunctionsThatCanSpuriouslyWakeUpInLoop` query "cpp/cert/wrap-functions-that-can-spuriously-wake-up-in-loop" and - ruleId = "CON54-CPP" + ruleId = "CON54-CPP" and + category = "rule" or query = // `Query` instance for the `preserveSafetyWhenUsingConditionVariables` query @@ -69,7 +75,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `preserveSafetyWhenUsingConditionVariables` query "cpp/cert/preserve-safety-when-using-condition-variables" and - ruleId = "CON55-CPP" + ruleId = "CON55-CPP" and + category = "rule" or query = // `Query` instance for the `doNotSpeculativelyLockALockedNonRecursiveMutex` query @@ -77,7 +84,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotSpeculativelyLockALockedNonRecursiveMutex` query "cpp/cert/do-not-speculatively-lock-a-locked-non-recursive-mutex" and - ruleId = "CON56-CPP" + ruleId = "CON56-CPP" and + category = "rule" or query = // `Query` instance for the `lockedALockedNonRecursiveMutexAudit` query @@ -85,7 +93,8 @@ predicate isConcurrencyQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `lockedALockedNonRecursiveMutexAudit` query "cpp/cert/locked-a-locked-non-recursive-mutex-audit" and - ruleId = "CON56-CPP" + ruleId = "CON56-CPP" and + category = "rule" } module ConcurrencyPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conditionals.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conditionals.qll index 4ca7053d08..979e51e5c8 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conditionals.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conditionals.qll @@ -22,14 +22,15 @@ newtype ConditionalsQuery = TGotoStatementJumpConditionQuery() or TContinueInForLoopConditionQuery() -predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `nonBooleanIfCondition` query ConditionalsPackage::nonBooleanIfConditionQuery() and queryId = // `@id` for the `nonBooleanIfCondition` query "cpp/autosar/non-boolean-if-condition" and - ruleId = "A5-0-2" + ruleId = "A5-0-2" and + category = "required" or query = // `Query` instance for the `nonBooleanIterationCondition` query @@ -37,7 +38,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `nonBooleanIterationCondition` query "cpp/autosar/non-boolean-iteration-condition" and - ruleId = "A5-0-2" + ruleId = "A5-0-2" and + category = "required" or query = // `Query` instance for the `switchLessThanTwoCases` query @@ -45,7 +47,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `switchLessThanTwoCases` query "cpp/autosar/switch-less-than-two-cases" and - ruleId = "A6-4-1" + ruleId = "A6-4-1" and + category = "required" or query = // `Query` instance for the `switchCompoundCondition` query @@ -53,7 +56,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `switchCompoundCondition` query "cpp/autosar/switch-compound-condition" and - ruleId = "M6-3-1" + ruleId = "M6-3-1" and + category = "required" or query = // `Query` instance for the `loopCompoundCondition` query @@ -61,7 +65,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `loopCompoundCondition` query "cpp/autosar/loop-compound-condition" and - ruleId = "M6-3-1" + ruleId = "M6-3-1" and + category = "required" or query = // `Query` instance for the `ifCompoundCondition` query @@ -69,7 +74,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `ifCompoundCondition` query "cpp/autosar/if-compound-condition" and - ruleId = "M6-4-1" + ruleId = "M6-4-1" and + category = "required" or query = // `Query` instance for the `ifElseTerminationCondition` query @@ -77,7 +83,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `ifElseTerminationCondition` query "cpp/autosar/if-else-termination-condition" and - ruleId = "M6-4-2" + ruleId = "M6-4-2" and + category = "required" or query = // `Query` instance for the `switchDoesNotStartWithCase` query @@ -85,7 +92,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `switchDoesNotStartWithCase` query "cpp/autosar/switch-does-not-start-with-case" and - ruleId = "M6-4-3" + ruleId = "M6-4-3" and + category = "required" or query = // `Query` instance for the `switchStatementNotWellFormed` query @@ -93,7 +101,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `switchStatementNotWellFormed` query "cpp/autosar/switch-statement-not-well-formed" and - ruleId = "M6-4-3" + ruleId = "M6-4-3" and + category = "required" or query = // `Query` instance for the `nestedCaseInSwitch` query @@ -101,7 +110,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `nestedCaseInSwitch` query "cpp/autosar/nested-case-in-switch" and - ruleId = "M6-4-4" + ruleId = "M6-4-4" and + category = "required" or query = // `Query` instance for the `nonEmptySwitchClauseDoesNotTerminate` query @@ -109,7 +119,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `nonEmptySwitchClauseDoesNotTerminate` query "cpp/autosar/non-empty-switch-clause-does-not-terminate" and - ruleId = "M6-4-5" + ruleId = "M6-4-5" and + category = "required" or query = // `Query` instance for the `missingDefaultInSwitch` query @@ -117,7 +128,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `missingDefaultInSwitch` query "cpp/autosar/missing-default-in-switch" and - ruleId = "M6-4-6" + ruleId = "M6-4-6" and + category = "required" or query = // `Query` instance for the `switchFinalClauseNotDefault` query @@ -125,7 +137,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `switchFinalClauseNotDefault` query "cpp/autosar/switch-final-clause-not-default" and - ruleId = "M6-4-6" + ruleId = "M6-4-6" and + category = "required" or query = // `Query` instance for the `booleanInSwitchCondition` query @@ -133,7 +146,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `booleanInSwitchCondition` query "cpp/autosar/boolean-in-switch-condition" and - ruleId = "M6-4-7" + ruleId = "M6-4-7" and + category = "required" or query = // `Query` instance for the `gotoBlockCondition` query @@ -141,7 +155,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `gotoBlockCondition` query "cpp/autosar/goto-block-condition" and - ruleId = "M6-6-1" + ruleId = "M6-6-1" and + category = "required" or query = // `Query` instance for the `gotoStatementJumpCondition` query @@ -149,7 +164,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `gotoStatementJumpCondition` query "cpp/autosar/goto-statement-jump-condition" and - ruleId = "M6-6-2" + ruleId = "M6-6-2" and + category = "required" or query = // `Query` instance for the `continueInForLoopCondition` query @@ -157,7 +173,8 @@ predicate isConditionalsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `continueInForLoopCondition` query "cpp/autosar/continue-in-for-loop-condition" and - ruleId = "M6-6-3" + ruleId = "M6-6-3" and + category = "required" } module ConditionalsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Const.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Const.qll index 0b2c492115..f542ddf486 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Const.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Const.qll @@ -5,10 +5,8 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype ConstQuery = TRemoveConstOrVolatileQualificationAutosarQuery() or - TDeclarationUnmodifiedParamMissingConstSpecifierQuery() or TDeclarationUnmodifiedObjectMissingConstSpecifierQuery() or TVariableMissingConstexprQuery() or - TFunctionMissingConstexprQuery() or TCvQualifiersNotPlacedOnTheRightHandSideQuery() or TOutputParametersUsedQuery() or TInOutParametersDeclaredAsTNotModifiedQuery() or @@ -18,22 +16,15 @@ newtype ConstQuery = TMemberFunctionConstIfPossibleQuery() or TRemoveConstOrVolatileQualificationCertQuery() -predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { +predicate isConstQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `removeConstOrVolatileQualificationAutosar` query ConstPackage::removeConstOrVolatileQualificationAutosarQuery() and queryId = // `@id` for the `removeConstOrVolatileQualificationAutosar` query "cpp/autosar/remove-const-or-volatile-qualification-autosar" and - ruleId = "A5-2-3" - or - query = - // `Query` instance for the `declarationUnmodifiedParamMissingConstSpecifier` query - ConstPackage::declarationUnmodifiedParamMissingConstSpecifierQuery() and - queryId = - // `@id` for the `declarationUnmodifiedParamMissingConstSpecifier` query - "cpp/autosar/declaration-unmodified-param-missing-const-specifier" and - ruleId = "A7-1-1" + ruleId = "A5-2-3" and + category = "required" or query = // `Query` instance for the `declarationUnmodifiedObjectMissingConstSpecifier` query @@ -41,7 +32,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `declarationUnmodifiedObjectMissingConstSpecifier` query "cpp/autosar/declaration-unmodified-object-missing-const-specifier" and - ruleId = "A7-1-1" + ruleId = "A7-1-1" and + category = "required" or query = // `Query` instance for the `variableMissingConstexpr` query @@ -49,15 +41,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `variableMissingConstexpr` query "cpp/autosar/variable-missing-constexpr" and - ruleId = "A7-1-2" - or - query = - // `Query` instance for the `functionMissingConstexpr` query - ConstPackage::functionMissingConstexprQuery() and - queryId = - // `@id` for the `functionMissingConstexpr` query - "cpp/autosar/function-missing-constexpr" and - ruleId = "A7-1-2" + ruleId = "A7-1-2" and + category = "required" or query = // `Query` instance for the `cvQualifiersNotPlacedOnTheRightHandSide` query @@ -65,7 +50,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `cvQualifiersNotPlacedOnTheRightHandSide` query "cpp/autosar/cv-qualifiers-not-placed-on-the-right-hand-side" and - ruleId = "A7-1-3" + ruleId = "A7-1-3" and + category = "required" or query = // `Query` instance for the `outputParametersUsed` query @@ -73,7 +59,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `outputParametersUsed` query "cpp/autosar/output-parameters-used" and - ruleId = "A8-4-8" + ruleId = "A8-4-8" and + category = "required" or query = // `Query` instance for the `inOutParametersDeclaredAsTNotModified` query @@ -81,7 +68,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `inOutParametersDeclaredAsTNotModified` query "cpp/autosar/in-out-parameters-declared-as-t-not-modified" and - ruleId = "A8-4-9" + ruleId = "A8-4-9" and + category = "required" or query = // `Query` instance for the `pointerOrReferenceParameterToConst` query @@ -89,7 +77,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerOrReferenceParameterToConst` query "cpp/autosar/pointer-or-reference-parameter-to-const" and - ruleId = "M7-1-2" + ruleId = "M7-1-2" and + category = "required" or query = // `Query` instance for the `constMemberFunctionReturnsNonConstPointer` query @@ -97,7 +86,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `constMemberFunctionReturnsNonConstPointer` query "cpp/autosar/const-member-function-returns-non-const-pointer" and - ruleId = "M9-3-1" + ruleId = "M9-3-1" and + category = "required" or query = // `Query` instance for the `memberFunctionStaticIfPossible` query @@ -105,7 +95,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `memberFunctionStaticIfPossible` query "cpp/autosar/member-function-static-if-possible" and - ruleId = "M9-3-3" + ruleId = "M9-3-3" and + category = "required" or query = // `Query` instance for the `memberFunctionConstIfPossible` query @@ -113,7 +104,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `memberFunctionConstIfPossible` query "cpp/autosar/member-function-const-if-possible" and - ruleId = "M9-3-3" + ruleId = "M9-3-3" and + category = "required" or query = // `Query` instance for the `removeConstOrVolatileQualificationCert` query @@ -121,7 +113,8 @@ predicate isConstQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `removeConstOrVolatileQualificationCert` query "cpp/cert/remove-const-or-volatile-qualification-cert" and - ruleId = "EXP55-CPP" + ruleId = "EXP55-CPP" and + category = "rule" } module ConstPackage { @@ -132,13 +125,6 @@ module ConstPackage { TQueryCPP(TConstPackageQuery(TRemoveConstOrVolatileQualificationAutosarQuery())) } - Query declarationUnmodifiedParamMissingConstSpecifierQuery() { - //autogenerate `Query` type - result = - // `Query` type for `declarationUnmodifiedParamMissingConstSpecifier` query - TQueryCPP(TConstPackageQuery(TDeclarationUnmodifiedParamMissingConstSpecifierQuery())) - } - Query declarationUnmodifiedObjectMissingConstSpecifierQuery() { //autogenerate `Query` type result = @@ -153,13 +139,6 @@ module ConstPackage { TQueryCPP(TConstPackageQuery(TVariableMissingConstexprQuery())) } - Query functionMissingConstexprQuery() { - //autogenerate `Query` type - result = - // `Query` type for `functionMissingConstexpr` query - TQueryCPP(TConstPackageQuery(TFunctionMissingConstexprQuery())) - } - Query cvQualifiersNotPlacedOnTheRightHandSideQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode.qll index d7b456252a..f11741fde5 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode.qll @@ -12,6 +12,7 @@ newtype DeadCodeQuery = TUnusedTypeDeclarationsQuery() or TUnreachableCodeQuery() or TUnusedFunctionQuery() or + TUnusedSplMemberFunctionQuery() or TInfeasiblePathQuery() or TUnusedLocalVariableQuery() or TUnusedGlobalOrNamespaceVariableQuery() or @@ -21,14 +22,15 @@ newtype DeadCodeQuery = TSingleUseMemberPODVariableQuery() or TDeadCodeQuery() -predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { +predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `uselessAssignment` query DeadCodePackage::uselessAssignmentQuery() and queryId = // `@id` for the `uselessAssignment` query "cpp/autosar/useless-assignment" and - ruleId = "A0-1-1" + ruleId = "A0-1-1" and + category = "required" or query = // `Query` instance for the `unusedReturnValue` query @@ -36,7 +38,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedReturnValue` query "cpp/autosar/unused-return-value" and - ruleId = "A0-1-2" + ruleId = "A0-1-2" and + category = "required" or query = // `Query` instance for the `unusedLocalFunction` query @@ -44,7 +47,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedLocalFunction` query "cpp/autosar/unused-local-function" and - ruleId = "A0-1-3" + ruleId = "A0-1-3" and + category = "required" or query = // `Query` instance for the `unusedParameter` query @@ -52,7 +56,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedParameter` query "cpp/autosar/unused-parameter" and - ruleId = "A0-1-4" + ruleId = "A0-1-4" and + category = "required" or query = // `Query` instance for the `unusedVirtualParameter` query @@ -60,7 +65,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedVirtualParameter` query "cpp/autosar/unused-virtual-parameter" and - ruleId = "A0-1-5" + ruleId = "A0-1-5" and + category = "required" or query = // `Query` instance for the `unusedTypeDeclarations` query @@ -68,7 +74,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedTypeDeclarations` query "cpp/autosar/unused-type-declarations" and - ruleId = "A0-1-6" + ruleId = "A0-1-6" and + category = "advisory" or query = // `Query` instance for the `unreachableCode` query @@ -76,7 +83,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unreachableCode` query "cpp/autosar/unreachable-code" and - ruleId = "M0-1-1" + ruleId = "M0-1-1" and + category = "required" or query = // `Query` instance for the `unusedFunction` query @@ -84,7 +92,17 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedFunction` query "cpp/autosar/unused-function" and - ruleId = "M0-1-10" + ruleId = "M0-1-10" and + category = "advisory" + or + query = + // `Query` instance for the `unusedSplMemberFunction` query + DeadCodePackage::unusedSplMemberFunctionQuery() and + queryId = + // `@id` for the `unusedSplMemberFunction` query + "cpp/autosar/unused-spl-member-function" and + ruleId = "M0-1-10" and + category = "advisory" or query = // `Query` instance for the `infeasiblePath` query @@ -92,7 +110,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `infeasiblePath` query "cpp/autosar/infeasible-path" and - ruleId = "M0-1-2" + ruleId = "M0-1-2" and + category = "required" or query = // `Query` instance for the `unusedLocalVariable` query @@ -100,7 +119,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedLocalVariable` query "cpp/autosar/unused-local-variable" and - ruleId = "M0-1-3" + ruleId = "M0-1-3" and + category = "required" or query = // `Query` instance for the `unusedGlobalOrNamespaceVariable` query @@ -108,7 +128,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedGlobalOrNamespaceVariable` query "cpp/autosar/unused-global-or-namespace-variable" and - ruleId = "M0-1-3" + ruleId = "M0-1-3" and + category = "required" or query = // `Query` instance for the `unusedMemberVariable` query @@ -116,7 +137,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unusedMemberVariable` query "cpp/autosar/unused-member-variable" and - ruleId = "M0-1-3" + ruleId = "M0-1-3" and + category = "required" or query = // `Query` instance for the `singleUseLocalPODVariable` query @@ -124,7 +146,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `singleUseLocalPODVariable` query "cpp/autosar/single-use-local-pod-variable" and - ruleId = "M0-1-4" + ruleId = "M0-1-4" and + category = "required" or query = // `Query` instance for the `singleUseGlobalOrNamespacePODVariable` query @@ -132,7 +155,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `singleUseGlobalOrNamespacePODVariable` query "cpp/autosar/single-use-global-or-namespace-pod-variable" and - ruleId = "M0-1-4" + ruleId = "M0-1-4" and + category = "required" or query = // `Query` instance for the `singleUseMemberPODVariable` query @@ -140,7 +164,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `singleUseMemberPODVariable` query "cpp/autosar/single-use-member-pod-variable" and - ruleId = "M0-1-4" + ruleId = "M0-1-4" and + category = "required" or query = // `Query` instance for the `deadCode` query @@ -148,7 +173,8 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `deadCode` query "cpp/autosar/dead-code" and - ruleId = "M0-1-9" + ruleId = "M0-1-9" and + category = "required" } module DeadCodePackage { @@ -208,6 +234,13 @@ module DeadCodePackage { TQueryCPP(TDeadCodePackageQuery(TUnusedFunctionQuery())) } + Query unusedSplMemberFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unusedSplMemberFunction` query + TQueryCPP(TDeadCodePackageQuery(TUnusedSplMemberFunctionQuery())) + } + Query infeasiblePathQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations.qll index c8fc673e36..92a06429c2 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations.qll @@ -9,6 +9,7 @@ newtype DeclarationsQuery = TGlobalSizedOperatorDeleteNotDefinedQuery() or TGlobalUnsizedOperatorDeleteNotDefinedQuery() or TVariableWidthIntegerTypesUsedQuery() or + TVariableWidthPlainCharTypeUsedQuery() or TAutoSpecifierNotUsedAppropriatelyInFunctionDefinitionQuery() or TAutoSpecifierNotUsedAppropriatelyInVariableDefinitionQuery() or TIdentifierDeclarationAndInitializationNotOnSeparateLinesQuery() or @@ -22,14 +23,15 @@ newtype DeclarationsQuery = TTypesNotIdenticalInObjectDeclarationsQuery() or TTypesNotIdenticalInReturnDeclarationsQuery() -predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `operatorNewAndOperatorDeleteNotDefinedLocally` query DeclarationsPackage::operatorNewAndOperatorDeleteNotDefinedLocallyQuery() and queryId = // `@id` for the `operatorNewAndOperatorDeleteNotDefinedLocally` query "cpp/autosar/operator-new-and-operator-delete-not-defined-locally" and - ruleId = "A18-5-11" + ruleId = "A18-5-11" and + category = "required" or query = // `Query` instance for the `operatorNewAndOperatorDeleteNotDefinedGlobally` query @@ -37,7 +39,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `operatorNewAndOperatorDeleteNotDefinedGlobally` query "cpp/autosar/operator-new-and-operator-delete-not-defined-globally" and - ruleId = "A18-5-11" + ruleId = "A18-5-11" and + category = "required" or query = // `Query` instance for the `globalSizedOperatorDeleteNotDefined` query @@ -45,7 +48,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `globalSizedOperatorDeleteNotDefined` query "cpp/autosar/global-sized-operator-delete-not-defined" and - ruleId = "A18-5-4" + ruleId = "A18-5-4" and + category = "required" or query = // `Query` instance for the `globalUnsizedOperatorDeleteNotDefined` query @@ -53,7 +57,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `globalUnsizedOperatorDeleteNotDefined` query "cpp/autosar/global-unsized-operator-delete-not-defined" and - ruleId = "A18-5-4" + ruleId = "A18-5-4" and + category = "required" or query = // `Query` instance for the `variableWidthIntegerTypesUsed` query @@ -61,7 +66,17 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `variableWidthIntegerTypesUsed` query "cpp/autosar/variable-width-integer-types-used" and - ruleId = "A3-9-1" + ruleId = "A3-9-1" and + category = "required" + or + query = + // `Query` instance for the `variableWidthPlainCharTypeUsed` query + DeclarationsPackage::variableWidthPlainCharTypeUsedQuery() and + queryId = + // `@id` for the `variableWidthPlainCharTypeUsed` query + "cpp/autosar/variable-width-plain-char-type-used" and + ruleId = "A3-9-1" and + category = "required" or query = // `Query` instance for the `autoSpecifierNotUsedAppropriatelyInFunctionDefinition` query @@ -69,7 +84,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `autoSpecifierNotUsedAppropriatelyInFunctionDefinition` query "cpp/autosar/auto-specifier-not-used-appropriately-in-function-definition" and - ruleId = "A7-1-5" + ruleId = "A7-1-5" and + category = "required" or query = // `Query` instance for the `autoSpecifierNotUsedAppropriatelyInVariableDefinition` query @@ -77,7 +93,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `autoSpecifierNotUsedAppropriatelyInVariableDefinition` query "cpp/autosar/auto-specifier-not-used-appropriately-in-variable-definition" and - ruleId = "A7-1-5" + ruleId = "A7-1-5" and + category = "required" or query = // `Query` instance for the `identifierDeclarationAndInitializationNotOnSeparateLines` query @@ -85,7 +102,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `identifierDeclarationAndInitializationNotOnSeparateLines` query "cpp/autosar/identifier-declaration-and-initialization-not-on-separate-lines" and - ruleId = "A7-1-7" + ruleId = "A7-1-7" and + category = "required" or query = // `Query` instance for the `classStructEnumDeclaredInDefinition` query @@ -93,7 +111,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `classStructEnumDeclaredInDefinition` query "cpp/autosar/class-struct-enum-declared-in-definition" and - ruleId = "A7-1-9" + ruleId = "A7-1-9" and + category = "required" or query = // `Query` instance for the `enumerationUnderlyingBaseTypeNotExplicitlyDefined` query @@ -101,7 +120,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `enumerationUnderlyingBaseTypeNotExplicitlyDefined` query "cpp/autosar/enumeration-underlying-base-type-not-explicitly-defined" and - ruleId = "A7-2-2" + ruleId = "A7-2-2" and + category = "required" or query = // `Query` instance for the `enumerationsNotDeclaredAsScopedEnumClasses` query @@ -109,7 +129,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `enumerationsNotDeclaredAsScopedEnumClasses` query "cpp/autosar/enumerations-not-declared-as-scoped-enum-classes" and - ruleId = "A7-2-3" + ruleId = "A7-2-3" and + category = "required" or query = // `Query` instance for the `noneFirstOrAllEnumeratorsNotInitialized` query @@ -117,7 +138,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `noneFirstOrAllEnumeratorsNotInitialized` query "cpp/autosar/none-first-or-all-enumerators-not-initialized" and - ruleId = "A7-2-4" + ruleId = "A7-2-4" and + category = "required" or query = // `Query` instance for the `functionsDeclaredAtBlockScope` query @@ -125,7 +147,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `functionsDeclaredAtBlockScope` query "cpp/autosar/functions-declared-at-block-scope" and - ruleId = "M3-1-2" + ruleId = "M3-1-2" and + category = "required" or query = // `Query` instance for the `declarationsOfAnObjectShallHaveCompatibleTypes` query @@ -133,7 +156,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `declarationsOfAnObjectShallHaveCompatibleTypes` query "cpp/autosar/declarations-of-an-object-shall-have-compatible-types" and - ruleId = "M3-2-1" + ruleId = "M3-2-1" and + category = "required" or query = // `Query` instance for the `declarationsOfAFunctionShallHaveCompatibleTypes` query @@ -141,7 +165,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `declarationsOfAFunctionShallHaveCompatibleTypes` query "cpp/autosar/declarations-of-a-function-shall-have-compatible-types" and - ruleId = "M3-2-1" + ruleId = "M3-2-1" and + category = "required" or query = // `Query` instance for the `typesNotIdenticalInObjectDeclarations` query @@ -149,7 +174,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `typesNotIdenticalInObjectDeclarations` query "cpp/autosar/types-not-identical-in-object-declarations" and - ruleId = "M3-9-1" + ruleId = "M3-9-1" and + category = "required" or query = // `Query` instance for the `typesNotIdenticalInReturnDeclarations` query @@ -157,7 +183,8 @@ predicate isDeclarationsQueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `typesNotIdenticalInReturnDeclarations` query "cpp/autosar/types-not-identical-in-return-declarations" and - ruleId = "M3-9-1" + ruleId = "M3-9-1" and + category = "required" } module DeclarationsPackage { @@ -196,6 +223,13 @@ module DeclarationsPackage { TQueryCPP(TDeclarationsPackageQuery(TVariableWidthIntegerTypesUsedQuery())) } + Query variableWidthPlainCharTypeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `variableWidthPlainCharTypeUsed` query + TQueryCPP(TDeclarationsPackageQuery(TVariableWidthPlainCharTypeUsedQuery())) + } + Query autoSpecifierNotUsedAppropriatelyInFunctionDefinitionQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/ExceptionSafety.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/ExceptionSafety.qll index 7611aad062..bab954a505 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/ExceptionSafety.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/ExceptionSafety.qll @@ -10,14 +10,15 @@ newtype ExceptionSafetyQuery = TGuaranteeExceptionSafetyQuery() or TDoNotLeakResourcesWhenHandlingExceptionsQuery() -predicate isExceptionSafetyQueryMetadata(Query query, string queryId, string ruleId) { +predicate isExceptionSafetyQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `exceptionSafetyGuaranteesNotProvided` query ExceptionSafetyPackage::exceptionSafetyGuaranteesNotProvidedQuery() and queryId = // `@id` for the `exceptionSafetyGuaranteesNotProvided` query "cpp/autosar/exception-safety-guarantees-not-provided" and - ruleId = "A15-0-2" + ruleId = "A15-0-2" and + category = "required" or query = // `Query` instance for the `exceptionSafetyGuaranteeOfACalledFunction` query @@ -25,7 +26,8 @@ predicate isExceptionSafetyQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `exceptionSafetyGuaranteeOfACalledFunction` query "cpp/autosar/exception-safety-guarantee-of-a-called-function" and - ruleId = "A15-0-3" + ruleId = "A15-0-3" and + category = "required" or query = // `Query` instance for the `validResourcesStateBeforeThrow` query @@ -33,7 +35,8 @@ predicate isExceptionSafetyQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `validResourcesStateBeforeThrow` query "cpp/autosar/valid-resources-state-before-throw" and - ruleId = "A15-1-4" + ruleId = "A15-1-4" and + category = "required" or query = // `Query` instance for the `guaranteeExceptionSafety` query @@ -41,7 +44,8 @@ predicate isExceptionSafetyQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `guaranteeExceptionSafety` query "cpp/cert/guarantee-exception-safety" and - ruleId = "ERR56-CPP" + ruleId = "ERR56-CPP" and + category = "rule" or query = // `Query` instance for the `doNotLeakResourcesWhenHandlingExceptions` query @@ -49,7 +53,8 @@ predicate isExceptionSafetyQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `doNotLeakResourcesWhenHandlingExceptions` query "cpp/cert/do-not-leak-resources-when-handling-exceptions" and - ruleId = "ERR57-CPP" + ruleId = "ERR57-CPP" and + category = "rule" } module ExceptionSafetyPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions1.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions1.qll index fc77c5e011..c4937348e8 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions1.qll @@ -39,14 +39,15 @@ newtype Exceptions1Query = TExceptionObjectsMustBeNothrowCopyConstructibleQuery() or TCatchExceptionsByLvalueReferenceQuery() -predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `exceptionThrownOnCompletion` query Exceptions1Package::exceptionThrownOnCompletionQuery() and queryId = // `@id` for the `exceptionThrownOnCompletion` query "cpp/autosar/exception-thrown-on-completion" and - ruleId = "A15-0-1" + ruleId = "A15-0-1" and + category = "required" or query = // `Query` instance for the `recoverableUncheckedExceptions` query @@ -54,7 +55,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `recoverableUncheckedExceptions` query "cpp/autosar/recoverable-unchecked-exceptions" and - ruleId = "A15-0-4" + ruleId = "A15-0-4" and + category = "required" or query = // `Query` instance for the `unrecoverableCheckedExceptions` query @@ -62,7 +64,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `unrecoverableCheckedExceptions` query "cpp/autosar/unrecoverable-checked-exceptions" and - ruleId = "A15-0-5" + ruleId = "A15-0-5" and + category = "required" or query = // `Query` instance for the `pointerExceptionObject` query @@ -70,7 +73,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `pointerExceptionObject` query "cpp/autosar/pointer-exception-object" and - ruleId = "A15-1-2" + ruleId = "A15-1-2" and + category = "required" or query = // `Query` instance for the `thrownExceptionsShouldBeUnique` query @@ -78,7 +82,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `thrownExceptionsShouldBeUnique` query "cpp/autosar/thrown-exceptions-should-be-unique" and - ruleId = "A15-1-3" + ruleId = "A15-1-3" and + category = "advisory" or query = // `Query` instance for the `constructorsThatAreNotNoexceptInvokedBeforeProgramStartup` query @@ -86,7 +91,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `constructorsThatAreNotNoexceptInvokedBeforeProgramStartup` query "cpp/autosar/constructors-that-are-not-noexcept-invoked-before-program-startup" and - ruleId = "A15-2-1" + ruleId = "A15-2-1" and + category = "required" or query = // `Query` instance for the `missingCatchHandlerInMain` query @@ -94,7 +100,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `missingCatchHandlerInMain` query "cpp/autosar/missing-catch-handler-in-main" and - ruleId = "A15-3-3" + ruleId = "A15-3-3" and + category = "required" or query = // `Query` instance for the `catchAllEllipsisUsedInNonMain` query @@ -102,7 +109,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `catchAllEllipsisUsedInNonMain` query "cpp/autosar/catch-all-ellipsis-used-in-non-main" and - ruleId = "A15-3-4" + ruleId = "A15-3-4" and + category = "required" or query = // `Query` instance for the `useOfDynamicExceptionSpecification` query @@ -110,7 +118,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `useOfDynamicExceptionSpecification` query "cpp/autosar/use-of-dynamic-exception-specification" and - ruleId = "A15-4-1" + ruleId = "A15-4-1" and + category = "required" or query = // `Query` instance for the `noExceptFunctionThrows` query @@ -118,7 +127,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `noExceptFunctionThrows` query "cpp/autosar/no-except-function-throws" and - ruleId = "A15-4-2" + ruleId = "A15-4-2" and + category = "required" or query = // `Query` instance for the `missingNoExcept` query @@ -126,7 +136,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `missingNoExcept` query "cpp/autosar/missing-no-except" and - ruleId = "A15-4-4" + ruleId = "A15-4-4" and + category = "required" or query = // `Query` instance for the `explicitAbruptTerminationAutosar` query @@ -134,7 +145,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `explicitAbruptTerminationAutosar` query "cpp/autosar/explicit-abrupt-termination-autosar" and - ruleId = "A15-5-2" + ruleId = "A15-5-2" and + category = "required" or query = // `Query` instance for the `conditionVariablePostConditionFailedAutosar` query @@ -142,7 +154,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `conditionVariablePostConditionFailedAutosar` query "cpp/autosar/condition-variable-post-condition-failed-autosar" and - ruleId = "A15-5-3" + ruleId = "A15-5-3" and + category = "required" or query = // `Query` instance for the `joinableThreadCopiedOrDestroyedAutosar` query @@ -150,7 +163,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `joinableThreadCopiedOrDestroyedAutosar` query "cpp/autosar/joinable-thread-copied-or-destroyed-autosar" and - ruleId = "A15-5-3" + ruleId = "A15-5-3" and + category = "required" or query = // `Query` instance for the `rethrowNestedWithoutCaptureAutosar` query @@ -158,7 +172,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `rethrowNestedWithoutCaptureAutosar` query "cpp/autosar/rethrow-nested-without-capture-autosar" and - ruleId = "A15-5-3" + ruleId = "A15-5-3" and + category = "required" or query = // `Query` instance for the `exitHandlerThrowsExceptionAutosar` query @@ -166,7 +181,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `exitHandlerThrowsExceptionAutosar` query "cpp/autosar/exit-handler-throws-exception-autosar" and - ruleId = "A15-5-3" + ruleId = "A15-5-3" and + category = "required" or query = // `Query` instance for the `gotoToCatchBlock` query @@ -174,7 +190,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `gotoToCatchBlock` query "cpp/autosar/goto-to-catch-block" and - ruleId = "M15-0-3" + ruleId = "M15-0-3" and + category = "required" or query = // `Query` instance for the `switchToCatchBlock` query @@ -182,7 +199,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `switchToCatchBlock` query "cpp/autosar/switch-to-catch-block" and - ruleId = "M15-0-3" + ruleId = "M15-0-3" and + category = "required" or query = // `Query` instance for the `exceptionThrownDuringThrow` query @@ -190,7 +208,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `exceptionThrownDuringThrow` query "cpp/autosar/exception-thrown-during-throw" and - ruleId = "M15-1-1" + ruleId = "M15-1-1" and + category = "required" or query = // `Query` instance for the `nullThrownExplicitly` query @@ -198,7 +217,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `nullThrownExplicitly` query "cpp/autosar/null-thrown-explicitly" and - ruleId = "M15-1-2" + ruleId = "M15-1-2" and + category = "required" or query = // `Query` instance for the `emptyThrowOutsideCatch` query @@ -206,7 +226,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `emptyThrowOutsideCatch` query "cpp/autosar/empty-throw-outside-catch" and - ruleId = "M15-1-3" + ruleId = "M15-1-3" and + category = "required" or query = // `Query` instance for the `catchAllExplicitlyThrownExceptions` query @@ -214,7 +235,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `catchAllExplicitlyThrownExceptions` query "cpp/autosar/catch-all-explicitly-thrown-exceptions" and - ruleId = "M15-3-4" + ruleId = "M15-3-4" and + category = "required" or query = // `Query` instance for the `catchAllHandlerLast` query @@ -222,7 +244,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `catchAllHandlerLast` query "cpp/autosar/catch-all-handler-last" and - ruleId = "M15-3-7" + ruleId = "M15-3-7" and + category = "required" or query = // `Query` instance for the `conditionVariablePostConditionFailedCert` query @@ -230,7 +253,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `conditionVariablePostConditionFailedCert` query "cpp/cert/condition-variable-post-condition-failed-cert" and - ruleId = "ERR50-CPP" + ruleId = "ERR50-CPP" and + category = "rule" or query = // `Query` instance for the `joinableThreadCopiedOrDestroyedCert` query @@ -238,7 +262,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `joinableThreadCopiedOrDestroyedCert` query "cpp/cert/joinable-thread-copied-or-destroyed-cert" and - ruleId = "ERR50-CPP" + ruleId = "ERR50-CPP" and + category = "rule" or query = // `Query` instance for the `rethrowNestedWithoutCaptureCert` query @@ -246,7 +271,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `rethrowNestedWithoutCaptureCert` query "cpp/cert/rethrow-nested-without-capture-cert" and - ruleId = "ERR50-CPP" + ruleId = "ERR50-CPP" and + category = "rule" or query = // `Query` instance for the `explicitAbruptTerminationCert` query @@ -254,7 +280,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `explicitAbruptTerminationCert` query "cpp/cert/explicit-abrupt-termination-cert" and - ruleId = "ERR50-CPP" + ruleId = "ERR50-CPP" and + category = "rule" or query = // `Query` instance for the `exitHandlerThrowsExceptionCert` query @@ -262,7 +289,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `exitHandlerThrowsExceptionCert` query "cpp/cert/exit-handler-throws-exception-cert" and - ruleId = "ERR50-CPP" + ruleId = "ERR50-CPP" and + category = "rule" or query = // `Query` instance for the `handleAllExceptions` query @@ -270,7 +298,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `handleAllExceptions` query "cpp/cert/handle-all-exceptions" and - ruleId = "ERR51-CPP" + ruleId = "ERR51-CPP" and + category = "rule" or query = // `Query` instance for the `destroyedValueReferencedInConstructorDestructorCatchBlock` query @@ -278,7 +307,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `destroyedValueReferencedInConstructorDestructorCatchBlock` query "cpp/cert/destroyed-value-referenced-in-constructor-destructor-catch-block" and - ruleId = "ERR53-CPP" + ruleId = "ERR53-CPP" and + category = "rule" or query = // `Query` instance for the `honorExceptionSpecifications` query @@ -286,7 +316,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `honorExceptionSpecifications` query "cpp/cert/honor-exception-specifications" and - ruleId = "ERR55-CPP" + ruleId = "ERR55-CPP" and + category = "rule" or query = // `Query` instance for the `handleAllExceptionsThrownBeforeMainBeginsExecuting` query @@ -294,7 +325,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `handleAllExceptionsThrownBeforeMainBeginsExecuting` query "cpp/cert/handle-all-exceptions-thrown-before-main-begins-executing" and - ruleId = "ERR58-CPP" + ruleId = "ERR58-CPP" and + category = "rule" or query = // `Query` instance for the `exceptionObjectsMustBeNothrowCopyConstructible` query @@ -302,7 +334,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `exceptionObjectsMustBeNothrowCopyConstructible` query "cpp/cert/exception-objects-must-be-nothrow-copy-constructible" and - ruleId = "ERR60-CPP" + ruleId = "ERR60-CPP" and + category = "rule" or query = // `Query` instance for the `catchExceptionsByLvalueReference` query @@ -310,7 +343,8 @@ predicate isExceptions1QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `catchExceptionsByLvalueReference` query "cpp/cert/catch-exceptions-by-lvalue-reference" and - ruleId = "ERR61-CPP" + ruleId = "ERR61-CPP" and + category = "rule" } module Exceptions1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions2.qll index 6e0a3e1f2b..fa990c8f0f 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Exceptions2.qll @@ -22,14 +22,15 @@ newtype Exceptions2Query = TDoNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctionsQuery() or TCatchBlockShadowingCertQuery() -predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `onlyThrowStdExceptionDerivedTypes` query Exceptions2Package::onlyThrowStdExceptionDerivedTypesQuery() and queryId = // `@id` for the `onlyThrowStdExceptionDerivedTypes` query "cpp/autosar/only-throw-std-exception-derived-types" and - ruleId = "A15-1-1" + ruleId = "A15-1-1" and + category = "advisory" or query = // `Query` instance for the `constructorErrorLeavesObjectInInvalidState` query @@ -37,7 +38,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `constructorErrorLeavesObjectInInvalidState` query "cpp/autosar/constructor-error-leaves-object-in-invalid-state" and - ruleId = "A15-2-2" + ruleId = "A15-2-2" and + category = "required" or query = // `Query` instance for the `classTypeExceptionNotCaughtByReference` query @@ -45,7 +47,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `classTypeExceptionNotCaughtByReference` query "cpp/autosar/class-type-exception-not-caught-by-reference" and - ruleId = "A15-3-5" + ruleId = "A15-3-5" and + category = "required" or query = // `Query` instance for the `incompatibleNoexceptSpecification` query @@ -53,7 +56,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `incompatibleNoexceptSpecification` query "cpp/autosar/incompatible-noexcept-specification" and - ruleId = "A15-4-3" + ruleId = "A15-4-3" and + category = "required" or query = // `Query` instance for the `inconsistentNoexceptFalseSpecification` query @@ -61,7 +65,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `inconsistentNoexceptFalseSpecification` query "cpp/autosar/inconsistent-noexcept-false-specification" and - ruleId = "A15-4-3" + ruleId = "A15-4-3" and + category = "required" or query = // `Query` instance for the `inconsistentNoexceptTrueSpecification` query @@ -69,7 +74,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `inconsistentNoexceptTrueSpecification` query "cpp/autosar/inconsistent-noexcept-true-specification" and - ruleId = "A15-4-3" + ruleId = "A15-4-3" and + category = "required" or query = // `Query` instance for the `incompatibleNoexceptSpecificationForOverriders` query @@ -77,7 +83,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `incompatibleNoexceptSpecificationForOverriders` query "cpp/autosar/incompatible-noexcept-specification-for-overriders" and - ruleId = "A15-4-3" + ruleId = "A15-4-3" and + category = "required" or query = // `Query` instance for the `missingCheckedExceptions` query @@ -85,7 +92,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `missingCheckedExceptions` query "cpp/autosar/missing-checked-exceptions" and - ruleId = "A15-4-5" + ruleId = "A15-4-5" and + category = "required" or query = // `Query` instance for the `inconsistentCheckedExceptions` query @@ -93,7 +101,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `inconsistentCheckedExceptions` query "cpp/autosar/inconsistent-checked-exceptions" and - ruleId = "A15-4-5" + ruleId = "A15-4-5" and + category = "required" or query = // `Query` instance for the `specialFunctionExitsWithException` query @@ -101,7 +110,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `specialFunctionExitsWithException` query "cpp/autosar/special-function-exits-with-exception" and - ruleId = "A15-5-1" + ruleId = "A15-5-1" and + category = "required" or query = // `Query` instance for the `specialFunctionMissingNoExceptSpecification` query @@ -109,7 +119,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `specialFunctionMissingNoExceptSpecification` query "cpp/autosar/special-function-missing-no-except-specification" and - ruleId = "A15-5-1" + ruleId = "A15-5-1" and + category = "required" or query = // `Query` instance for the `exceptionRaisedDuringStartup` query @@ -117,7 +128,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `exceptionRaisedDuringStartup` query "cpp/autosar/exception-raised-during-startup" and - ruleId = "M15-3-1" + ruleId = "M15-3-1" and + category = "required" or query = // `Query` instance for the `exceptionRaisedDuringTermination` query @@ -125,7 +137,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `exceptionRaisedDuringTermination` query "cpp/autosar/exception-raised-during-termination" and - ruleId = "M15-3-1" + ruleId = "M15-3-1" and + category = "required" or query = // `Query` instance for the `destroyedValueReferencedInDestructorCatchBlock` query @@ -133,7 +146,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `destroyedValueReferencedInDestructorCatchBlock` query "cpp/autosar/destroyed-value-referenced-in-destructor-catch-block" and - ruleId = "M15-3-3" + ruleId = "M15-3-3" and + category = "required" or query = // `Query` instance for the `catchBlockShadowingMisra` query @@ -141,7 +155,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `catchBlockShadowingMisra` query "cpp/autosar/catch-block-shadowing-misra" and - ruleId = "M15-3-6" + ruleId = "M15-3-6" and + category = "required" or query = // `Query` instance for the `doNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions` query @@ -149,7 +164,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions` query "cpp/cert/do-not-let-exceptions-escape-from-destructors-or-deallocation-functions" and - ruleId = "DCL57-CPP" + ruleId = "DCL57-CPP" and + category = "rule" or query = // `Query` instance for the `catchBlockShadowingCert` query @@ -157,7 +173,8 @@ predicate isExceptions2QueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `catchBlockShadowingCert` query "cpp/cert/catch-block-shadowing-cert" and - ruleId = "ERR54-CPP" + ruleId = "ERR54-CPP" and + category = "rule" } module Exceptions2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions.qll index 049209d3fa..9797b874e5 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Expressions.qll @@ -21,14 +21,15 @@ newtype ExpressionsQuery = TPassReferenceTypeToVaStartQuery() or TPassNonTrivialObjectToVaStartQuery() -predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `operationsAssumingMemoryLayoutPerformedOnObjects` query ExpressionsPackage::operationsAssumingMemoryLayoutPerformedOnObjectsQuery() and queryId = // `@id` for the `operationsAssumingMemoryLayoutPerformedOnObjects` query "cpp/autosar/operations-assuming-memory-layout-performed-on-objects" and - ruleId = "A12-0-2" + ruleId = "A12-0-2" and + category = "required" or query = // `Query` instance for the `enumUsedInArithmeticContexts` query @@ -36,7 +37,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `enumUsedInArithmeticContexts` query "cpp/autosar/enum-used-in-arithmetic-contexts" and - ruleId = "A4-5-1" + ruleId = "A4-5-1" and + category = "required" or query = // `Query` instance for the `divisorEqualToZero` query @@ -44,7 +46,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `divisorEqualToZero` query "cpp/autosar/divisor-equal-to-zero" and - ruleId = "A5-6-1" + ruleId = "A5-6-1" and + category = "required" or query = // `Query` instance for the `functionErroneousReturnValueNotTested` query @@ -52,7 +55,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `functionErroneousReturnValueNotTested` query "cpp/autosar/function-erroneous-return-value-not-tested" and - ruleId = "M0-3-2" + ruleId = "M0-3-2" and + category = "required" or query = // `Query` instance for the `boolOperandsToDisallowedBuiltInOperators` query @@ -60,7 +64,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `boolOperandsToDisallowedBuiltInOperators` query "cpp/autosar/bool-operands-to-disallowed-built-in-operators" and - ruleId = "M4-5-1" + ruleId = "M4-5-1" and + category = "required" or query = // `Query` instance for the `charUsedAsOperandsToDisallowedBuiltInOperators` query @@ -68,7 +73,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `charUsedAsOperandsToDisallowedBuiltInOperators` query "cpp/autosar/char-used-as-operands-to-disallowed-built-in-operators" and - ruleId = "M4-5-3" + ruleId = "M4-5-3" and + category = "required" or query = // `Query` instance for the `ternaryOperatorConditionNotTypeBool` query @@ -76,7 +82,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `ternaryOperatorConditionNotTypeBool` query "cpp/autosar/ternary-operator-condition-not-type-bool" and - ruleId = "M5-0-14" + ruleId = "M5-0-14" and + category = "required" or query = // `Query` instance for the `bitwiseOperatorOperandsHaveDifferentUnderlyingType` query @@ -84,7 +91,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `bitwiseOperatorOperandsHaveDifferentUnderlyingType` query "cpp/autosar/bitwise-operator-operands-have-different-underlying-type" and - ruleId = "M5-0-20" + ruleId = "M5-0-20" and + category = "required" or query = // `Query` instance for the `bitwiseOperatorAppliedToSignedTypes` query @@ -92,7 +100,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `bitwiseOperatorAppliedToSignedTypes` query "cpp/autosar/bitwise-operator-applied-to-signed-types" and - ruleId = "M5-0-21" + ruleId = "M5-0-21" and + category = "required" or query = // `Query` instance for the `cvalueExpressionConvertedToDifferentUnderlyingType` query @@ -100,7 +109,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `cvalueExpressionConvertedToDifferentUnderlyingType` query "cpp/autosar/cvalue-expression-converted-to-different-underlying-type" and - ruleId = "M5-0-3" + ruleId = "M5-0-3" and + category = "required" or query = // `Query` instance for the `constantUnsignedIntegerExpressionsWrapAround` query @@ -108,7 +118,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `constantUnsignedIntegerExpressionsWrapAround` query "cpp/autosar/constant-unsigned-integer-expressions-wrap-around" and - ruleId = "M5-19-1" + ruleId = "M5-19-1" and + category = "required" or query = // `Query` instance for the `rightBitShiftOperandIsNegativeOrTooWide` query @@ -116,7 +127,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `rightBitShiftOperandIsNegativeOrTooWide` query "cpp/autosar/right-bit-shift-operand-is-negative-or-too-wide" and - ruleId = "M5-8-1" + ruleId = "M5-8-1" and + category = "required" or query = // `Query` instance for the `floatsTestedForEquality` query @@ -124,7 +136,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `floatsTestedForEquality` query "cpp/autosar/floats-tested-for-equality" and - ruleId = "M6-2-2" + ruleId = "M6-2-2" and + category = "required" or query = // `Query` instance for the `passPromotablePrimitiveTypeToVaStart` query @@ -132,7 +145,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `passPromotablePrimitiveTypeToVaStart` query "cpp/cert/pass-promotable-primitive-type-to-va-start" and - ruleId = "EXP58-CPP" + ruleId = "EXP58-CPP" and + category = "rule" or query = // `Query` instance for the `passReferenceTypeToVaStart` query @@ -140,7 +154,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `passReferenceTypeToVaStart` query "cpp/cert/pass-reference-type-to-va-start" and - ruleId = "EXP58-CPP" + ruleId = "EXP58-CPP" and + category = "rule" or query = // `Query` instance for the `passNonTrivialObjectToVaStart` query @@ -148,7 +163,8 @@ predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `passNonTrivialObjectToVaStart` query "cpp/cert/pass-non-trivial-object-to-va-start" and - ruleId = "EXP58-CPP" + ruleId = "EXP58-CPP" and + category = "rule" } module ExpressionsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/FloatingPoint.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/FloatingPoint.qll new file mode 100644 index 0000000000..0ff7a07214 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/FloatingPoint.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype FloatingPointQuery = + TPossibleMisuseOfInfiniteFloatingPointValueQuery() or + TPossibleMisuseOfNaNFloatingPointValueQuery() + +predicate isFloatingPointQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `possibleMisuseOfInfiniteFloatingPointValue` query + FloatingPointPackage::possibleMisuseOfInfiniteFloatingPointValueQuery() and + queryId = + // `@id` for the `possibleMisuseOfInfiniteFloatingPointValue` query + "cpp/misra/possible-misuse-of-infinite-floating-point-value" and + ruleId = "DIR-0-3-1" and + category = "advisory" + or + query = + // `Query` instance for the `possibleMisuseOfNaNFloatingPointValue` query + FloatingPointPackage::possibleMisuseOfNaNFloatingPointValueQuery() and + queryId = + // `@id` for the `possibleMisuseOfNaNFloatingPointValue` query + "cpp/misra/possible-misuse-of-nan-floating-point-value" and + ruleId = "DIR-0-3-1" and + category = "advisory" +} + +module FloatingPointPackage { + Query possibleMisuseOfInfiniteFloatingPointValueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `possibleMisuseOfInfiniteFloatingPointValue` query + TQueryCPP(TFloatingPointPackageQuery(TPossibleMisuseOfInfiniteFloatingPointValueQuery())) + } + + Query possibleMisuseOfNaNFloatingPointValueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `possibleMisuseOfNaNFloatingPointValue` query + TQueryCPP(TFloatingPointPackageQuery(TPossibleMisuseOfNaNFloatingPointValueQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Freed.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Freed.qll index a7122cb236..00d3bbdf99 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Freed.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Freed.qll @@ -14,14 +14,15 @@ newtype FreedQuery = TObjectAccessedAfterLifetimeCertQuery() or TUseAfterFreeQuery() -predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { +predicate isFreedQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `newDeleteArrayMismatch` query FreedPackage::newDeleteArrayMismatchQuery() and queryId = // `@id` for the `newDeleteArrayMismatch` query "cpp/autosar/new-delete-array-mismatch" and - ruleId = "A18-5-3" + ruleId = "A18-5-3" and + category = "required" or query = // `Query` instance for the `newArrayDeleteMismatch` query @@ -29,7 +30,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `newArrayDeleteMismatch` query "cpp/autosar/new-array-delete-mismatch" and - ruleId = "A18-5-3" + ruleId = "A18-5-3" and + category = "required" or query = // `Query` instance for the `objectAccessedBeforeLifetimeAutosar` query @@ -37,7 +39,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `objectAccessedBeforeLifetimeAutosar` query "cpp/autosar/object-accessed-before-lifetime-autosar" and - ruleId = "A3-8-1" + ruleId = "A3-8-1" and + category = "required" or query = // `Query` instance for the `objectAccessedAfterLifetimeAutosar` query @@ -45,7 +48,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `objectAccessedAfterLifetimeAutosar` query "cpp/autosar/object-accessed-after-lifetime-autosar" and - ruleId = "A3-8-1" + ruleId = "A3-8-1" and + category = "required" or query = // `Query` instance for the `assignmentOfEscapingAutoStorage` query @@ -53,7 +57,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `assignmentOfEscapingAutoStorage` query "cpp/autosar/assignment-of-escaping-auto-storage" and - ruleId = "M7-5-2" + ruleId = "M7-5-2" and + category = "required" or query = // `Query` instance for the `doNotDeleteAnArrayThroughAPointerOfTheIncorrectType` query @@ -61,7 +66,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotDeleteAnArrayThroughAPointerOfTheIncorrectType` query "cpp/cert/do-not-delete-an-array-through-a-pointer-of-the-incorrect-type" and - ruleId = "EXP51-CPP" + ruleId = "EXP51-CPP" and + category = "rule" or query = // `Query` instance for the `objectAccessedBeforeLifetimeCert` query @@ -69,7 +75,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `objectAccessedBeforeLifetimeCert` query "cpp/cert/object-accessed-before-lifetime-cert" and - ruleId = "EXP54-CPP" + ruleId = "EXP54-CPP" and + category = "rule" or query = // `Query` instance for the `objectAccessedAfterLifetimeCert` query @@ -77,7 +84,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `objectAccessedAfterLifetimeCert` query "cpp/cert/object-accessed-after-lifetime-cert" and - ruleId = "EXP54-CPP" + ruleId = "EXP54-CPP" and + category = "rule" or query = // `Query` instance for the `useAfterFree` query @@ -85,7 +93,8 @@ predicate isFreedQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useAfterFree` query "cpp/cert/use-after-free" and - ruleId = "MEM50-CPP" + ruleId = "MEM50-CPP" and + category = "rule" } module FreedPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Functions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Functions.qll index 73ef5a2ea3..c83fd189d8 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Functions.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Functions.qll @@ -19,14 +19,15 @@ newtype FunctionsQuery = TNonVoidFunctionDoesNotReturnCertQuery() or TFunctionNoReturnAttributeConditionCertQuery() -predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `cStandardLibraryFunctionCalls` query FunctionsPackage::cStandardLibraryFunctionCallsQuery() and queryId = // `@id` for the `cStandardLibraryFunctionCalls` query "cpp/autosar/c-standard-library-function-calls" and - ruleId = "A17-1-1" + ruleId = "A17-1-1" and + category = "required" or query = // `Query` instance for the `trivialAccessorAndMutatorFunctionsNotInlined` query @@ -34,7 +35,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `trivialAccessorAndMutatorFunctionsNotInlined` query "cpp/autosar/trivial-accessor-and-mutator-functions-not-inlined" and - ruleId = "A3-1-6" + ruleId = "A3-1-6" and + category = "advisory" or query = // `Query` instance for the `invalidFunctionReturnType` query @@ -42,7 +44,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `invalidFunctionReturnType` query "cpp/autosar/invalid-function-return-type" and - ruleId = "A7-5-1" + ruleId = "A7-5-1" and + category = "required" or query = // `Query` instance for the `recursiveFunctions` query @@ -50,7 +53,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `recursiveFunctions` query "cpp/autosar/recursive-functions" and - ruleId = "A7-5-2" + ruleId = "A7-5-2" and + category = "required" or query = // `Query` instance for the `functionNoReturnAttributeConditionAutosar` query @@ -58,7 +62,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionNoReturnAttributeConditionAutosar` query "cpp/autosar/function-no-return-attribute-condition-autosar" and - ruleId = "A7-6-1" + ruleId = "A7-6-1" and + category = "required" or query = // `Query` instance for the `nonVoidFunctionDoesNotReturnAutosar` query @@ -66,7 +71,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonVoidFunctionDoesNotReturnAutosar` query "cpp/autosar/non-void-function-does-not-return-autosar" and - ruleId = "A8-4-2" + ruleId = "A8-4-2" and + category = "required" or query = // `Query` instance for the `functionReturnMultipleValueCondition` query @@ -74,7 +80,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionReturnMultipleValueCondition` query "cpp/autosar/function-return-multiple-value-condition" and - ruleId = "A8-4-4" + ruleId = "A8-4-4" and + category = "advisory" or query = // `Query` instance for the `assmemblerInstructionsCondition` query @@ -82,7 +89,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `assmemblerInstructionsCondition` query "cpp/autosar/assmembler-instructions-condition" and - ruleId = "M7-4-2" + ruleId = "M7-4-2" and + category = "required" or query = // `Query` instance for the `assemblyLanguageCondition` query @@ -90,7 +98,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `assemblyLanguageCondition` query "cpp/autosar/assembly-language-condition" and - ruleId = "M7-4-3" + ruleId = "M7-4-3" and + category = "required" or query = // `Query` instance for the `functionReturnAutomaticVarCondition` query @@ -98,7 +107,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionReturnAutomaticVarCondition` query "cpp/autosar/function-return-automatic-var-condition" and - ruleId = "M7-5-1" + ruleId = "M7-5-1" and + category = "required" or query = // `Query` instance for the `functionIdentifierCondition` query @@ -106,7 +116,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionIdentifierCondition` query "cpp/autosar/function-identifier-condition" and - ruleId = "M8-4-4" + ruleId = "M8-4-4" and + category = "required" or query = // `Query` instance for the `functionWithMismatchedLanguageLinkage` query @@ -114,7 +125,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionWithMismatchedLanguageLinkage` query "cpp/cert/function-with-mismatched-language-linkage" and - ruleId = "EXP56-CPP" + ruleId = "EXP56-CPP" and + category = "rule" or query = // `Query` instance for the `nonVoidFunctionDoesNotReturnCert` query @@ -122,7 +134,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonVoidFunctionDoesNotReturnCert` query "cpp/cert/non-void-function-does-not-return-cert" and - ruleId = "MSC52-CPP" + ruleId = "MSC52-CPP" and + category = "rule" or query = // `Query` instance for the `functionNoReturnAttributeConditionCert` query @@ -130,7 +143,8 @@ predicate isFunctionsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionNoReturnAttributeConditionCert` query "cpp/cert/function-no-return-attribute-condition-cert" and - ruleId = "MSC53-CPP" + ruleId = "MSC53-CPP" and + category = "rule" } module FunctionsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/IO.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/IO.qll index cefa031820..d8cadfc184 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/IO.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/IO.qll @@ -8,14 +8,15 @@ newtype IOQuery = TInterleavedInputOutputWithoutPositionQuery() or TCloseFilesWhenTheyAreNoLongerNeededQuery() -predicate isIOQueryMetadata(Query query, string queryId, string ruleId) { +predicate isIOQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `interleavedInputOutputWithoutFlush` query IOPackage::interleavedInputOutputWithoutFlushQuery() and queryId = // `@id` for the `interleavedInputOutputWithoutFlush` query "cpp/autosar/interleaved-input-output-without-flush" and - ruleId = "A27-0-3" + ruleId = "A27-0-3" and + category = "required" or query = // `Query` instance for the `interleavedInputOutputWithoutPosition` query @@ -23,7 +24,8 @@ predicate isIOQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `interleavedInputOutputWithoutPosition` query "cpp/cert/interleaved-input-output-without-position" and - ruleId = "FIO50-CPP" + ruleId = "FIO50-CPP" and + category = "rule" or query = // `Query` instance for the `closeFilesWhenTheyAreNoLongerNeeded` query @@ -31,7 +33,8 @@ predicate isIOQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `closeFilesWhenTheyAreNoLongerNeeded` query "cpp/cert/close-files-when-they-are-no-longer-needed" and - ruleId = "FIO51-CPP" + ruleId = "FIO51-CPP" and + category = "rule" } module IOPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/ImportMisra23.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/ImportMisra23.qll new file mode 100644 index 0000000000..d31affb27c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/ImportMisra23.qll @@ -0,0 +1,1438 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype ImportMisra23Query = + TSectionsOfCodeShouldNotBeCommentedOutQuery() or + TOneDefinitionRuleViolatedQuery() or + TVariableDeclaredInInnerScopeHidesOuterScopeQuery() or + TObjectAccessedBeforeLifetimeMisraQuery() or + TObjectAccessedAfterLifetimeMisraQuery() or + TCastRemovesConstOrVolatileFromPointerOrReferenceQuery() or + TIfElseIfEndConditionQuery() or + TGotoShallJumpToLabelDeclaredLaterInTheFunctionQuery() or + TFunctionDeclaredWithTheNoreturnAttributeReturnQuery() or + TNonVoidFunctionShallReturnAValueOnAllPathsQuery() or + TDeclarationOfAnObjectIndirectionsLevelQuery() or + THandlersReferToNonStaticMembersFromTheirClassQuery() or + TIncludeDirectivesPrecededByPreprocessorDirectivesQuery() or + TIdentifiersUsedInTheControllingExpressionOfQuery() or + TCharsThatShouldNotOccurInHeaderFileNameQuery() or + TAndPreprocessorOperatorsShouldNotBeUsedQuery() or + TTokensThatLookLikeDirectivesInAMacroArgumentQuery() or + TPointerToAnIncompleteClassTypeDeletedQuery() or + TPointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery() or + TCallToSetlocaleInvalidatesOldPointersMisraQuery() or + TCallToSetlocaleInvalidatesOldPointersWarnMisraQuery() or + TObjectUsedWhileInPotentiallyMovedFromStateQuery() or + TReadsAndWritesOnStreamNotSeparatedByPositioningQuery() or + TCommaOperatorShouldNotBeUsedQuery() or + TCopyAndMoveAssignmentsShallHandleSelfAssignmentQuery() or + TUseSingleLocalDeclaratorsQuery() or + TUseSingleGlobalOrMemberDeclaratorsQuery() or + TEnumerationNotDefinedWithAnExplicitUnderlyingTypeQuery() or + TAsmDeclarationShallNotBeUsedQuery() or + TNonUniqueEnumerationConstantQuery() or + TBitFieldShallHaveAnAppropriateTypeQuery() or + TSignedIntegerNamedBitFieldHaveALengthOfOneBitQuery() or + TVirtualAndNonVirtualClassInTheHierarchyQuery() or + TOverridingShallSpecifyDifferentDefaultArgumentsQuery() or + TPotentiallyVirtualPointerOnlyComparesToNullptrQuery() or + TObjectsDynamicTypeUsedFromConstructorOrDestructorQuery() or + TInitializeAllVirtualBaseClassesQuery() or + TInitializerListConstructorIsTheOnlyConstructorQuery() or + TAddressOfOperatorOverloadedQuery() or + TFunctionTemplatesExplicitlySpecializedQuery() or + TExceptionObjectHavePointerTypeQuery() or + TEmptyThrowOnlyWithinACatchHandlerQuery() or + TNoexceptFunctionShouldNotPropagateToTheCallerQuery() or + TFunctionLikeMacrosDefinedQuery() or + TMacroParameterFollowingHashQuery() or + TAMixedUseMacroArgumentSubjectToExpansionQuery() or + TCsignalFacilitiesUsedQuery() or + TCsignalTypesShallNotBeUsedQuery() or + TAtofAtoiAtolAndAtollUsedQuery() or + TMacroOffsetofShallNotBeUsedQuery() or + TGlobalSizedOperatorDeleteShallBeDefinedQuery() or + TGlobalUnsizedOperatorDeleteShallBeDefinedQuery() or + TVectorShouldNotBeSpecializedWithBoolQuery() or + TForwardingReferencesAndForwardNotUsedTogetherQuery() or + TCstdioFunctionsShallNotBeUsedQuery() or + TCstdioMacrosShallNotBeUsedQuery() or + TCstdioTypesShallNotBeUsedQuery() or + TBackslashCharacterMisuseQuery() or + TNonTerminatedEscapeSequencesQuery() or + TOctalConstantsUsedQuery() or + TUnsignedIntegerLiteralsNotAppropriatelySuffixedQuery() or + TLowercaseLStartsInLiteralSuffixQuery() or + TCharacterSequenceUsedWithinACStyleCommentQuery() or + TLineSplicingUsedInCommentsQuery() or + TGlobalNamespaceDeclarationsQuery() or + TNonGlobalFunctionMainQuery() or + TInheritedNonOverridableMemberFunctionQuery() or + TInheritedOverridableMemberFunctionQuery() or + TDefinitionShallBeConsideredForUnqualifiedLookupQuery() or + TNameShallBeReferredUsingAQualifiedIdOrThisQuery() or + TNameShallBeReferredUsingAQualifiedIdOrThisAuditQuery() or + TReturnReferenceOrPointerToAutomaticLocalVariableQuery() or + TNullptrNotTheOnlyFormOfTheNullPointerConstantQuery() or + TArrayPassedAsFunctionArgumentDecayToAPointerQuery() or + TResultOfAnAssignmentOperatorShouldNotBeUsedQuery() or + TFunctionsCallThemselvesEitherDirectlyOrIndirectlyQuery() or + TCastsBetweenAPointerToFunctionAndAnyOtherTypeQuery() or + TReinterpretCastShallNotBeUsedQuery() or + TUnsignedOperationWithConstantOperandsWrapsQuery() or + TBuiltInUnaryOperatorAppliedToUnsignedExpressionQuery() or + TSwitchBodyCompoundConditionQuery() or + TLoopBodyCompoundConditionQuery() or + TGotoStatementShouldNotBeUsedQuery() or + TGotoReferenceALabelInSurroundingBlockQuery() + +predicate isImportMisra23QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `sectionsOfCodeShouldNotBeCommentedOut` query + ImportMisra23Package::sectionsOfCodeShouldNotBeCommentedOutQuery() and + queryId = + // `@id` for the `sectionsOfCodeShouldNotBeCommentedOut` query + "cpp/misra/sections-of-code-should-not-be-commented-out" and + ruleId = "DIR-5-7-2" and + category = "advisory" + or + query = + // `Query` instance for the `oneDefinitionRuleViolated` query + ImportMisra23Package::oneDefinitionRuleViolatedQuery() and + queryId = + // `@id` for the `oneDefinitionRuleViolated` query + "cpp/misra/one-definition-rule-violated" and + ruleId = "RULE-6-2-1" and + category = "required" + or + query = + // `Query` instance for the `variableDeclaredInInnerScopeHidesOuterScope` query + ImportMisra23Package::variableDeclaredInInnerScopeHidesOuterScopeQuery() and + queryId = + // `@id` for the `variableDeclaredInInnerScopeHidesOuterScope` query + "cpp/misra/variable-declared-in-inner-scope-hides-outer-scope" and + ruleId = "RULE-6-4-1" and + category = "required" + or + query = + // `Query` instance for the `objectAccessedBeforeLifetimeMisra` query + ImportMisra23Package::objectAccessedBeforeLifetimeMisraQuery() and + queryId = + // `@id` for the `objectAccessedBeforeLifetimeMisra` query + "cpp/misra/object-accessed-before-lifetime-misra" and + ruleId = "RULE-6-8-1" and + category = "required" + or + query = + // `Query` instance for the `objectAccessedAfterLifetimeMisra` query + ImportMisra23Package::objectAccessedAfterLifetimeMisraQuery() and + queryId = + // `@id` for the `objectAccessedAfterLifetimeMisra` query + "cpp/misra/object-accessed-after-lifetime-misra" and + ruleId = "RULE-6-8-1" and + category = "required" + or + query = + // `Query` instance for the `castRemovesConstOrVolatileFromPointerOrReference` query + ImportMisra23Package::castRemovesConstOrVolatileFromPointerOrReferenceQuery() and + queryId = + // `@id` for the `castRemovesConstOrVolatileFromPointerOrReference` query + "cpp/misra/cast-removes-const-or-volatile-from-pointer-or-reference" and + ruleId = "RULE-8-2-3" and + category = "required" + or + query = + // `Query` instance for the `ifElseIfEndCondition` query + ImportMisra23Package::ifElseIfEndConditionQuery() and + queryId = + // `@id` for the `ifElseIfEndCondition` query + "cpp/misra/if-else-if-end-condition" and + ruleId = "RULE-9-4-1" and + category = "required" + or + query = + // `Query` instance for the `gotoShallJumpToLabelDeclaredLaterInTheFunction` query + ImportMisra23Package::gotoShallJumpToLabelDeclaredLaterInTheFunctionQuery() and + queryId = + // `@id` for the `gotoShallJumpToLabelDeclaredLaterInTheFunction` query + "cpp/misra/goto-shall-jump-to-label-declared-later-in-the-function" and + ruleId = "RULE-9-6-3" and + category = "required" + or + query = + // `Query` instance for the `functionDeclaredWithTheNoreturnAttributeReturn` query + ImportMisra23Package::functionDeclaredWithTheNoreturnAttributeReturnQuery() and + queryId = + // `@id` for the `functionDeclaredWithTheNoreturnAttributeReturn` query + "cpp/misra/function-declared-with-the-noreturn-attribute-return" and + ruleId = "RULE-9-6-4" and + category = "required" + or + query = + // `Query` instance for the `nonVoidFunctionShallReturnAValueOnAllPaths` query + ImportMisra23Package::nonVoidFunctionShallReturnAValueOnAllPathsQuery() and + queryId = + // `@id` for the `nonVoidFunctionShallReturnAValueOnAllPaths` query + "cpp/misra/non-void-function-shall-return-a-value-on-all-paths" and + ruleId = "RULE-9-6-5" and + category = "required" + or + query = + // `Query` instance for the `declarationOfAnObjectIndirectionsLevel` query + ImportMisra23Package::declarationOfAnObjectIndirectionsLevelQuery() and + queryId = + // `@id` for the `declarationOfAnObjectIndirectionsLevel` query + "cpp/misra/declaration-of-an-object-indirections-level" and + ruleId = "RULE-11-3-2" and + category = "advisory" + or + query = + // `Query` instance for the `handlersReferToNonStaticMembersFromTheirClass` query + ImportMisra23Package::handlersReferToNonStaticMembersFromTheirClassQuery() and + queryId = + // `@id` for the `handlersReferToNonStaticMembersFromTheirClass` query + "cpp/misra/handlers-refer-to-non-static-members-from-their-class" and + ruleId = "RULE-18-3-3" and + category = "required" + or + query = + // `Query` instance for the `includeDirectivesPrecededByPreprocessorDirectives` query + ImportMisra23Package::includeDirectivesPrecededByPreprocessorDirectivesQuery() and + queryId = + // `@id` for the `includeDirectivesPrecededByPreprocessorDirectives` query + "cpp/misra/include-directives-preceded-by-preprocessor-directives" and + ruleId = "RULE-19-0-3" and + category = "advisory" + or + query = + // `Query` instance for the `identifiersUsedInTheControllingExpressionOf` query + ImportMisra23Package::identifiersUsedInTheControllingExpressionOfQuery() and + queryId = + // `@id` for the `identifiersUsedInTheControllingExpressionOf` query + "cpp/misra/identifiers-used-in-the-controlling-expression-of" and + ruleId = "RULE-19-1-3" and + category = "required" + or + query = + // `Query` instance for the `charsThatShouldNotOccurInHeaderFileName` query + ImportMisra23Package::charsThatShouldNotOccurInHeaderFileNameQuery() and + queryId = + // `@id` for the `charsThatShouldNotOccurInHeaderFileName` query + "cpp/misra/chars-that-should-not-occur-in-header-file-name" and + ruleId = "RULE-19-2-3" and + category = "required" + or + query = + // `Query` instance for the `andPreprocessorOperatorsShouldNotBeUsed` query + ImportMisra23Package::andPreprocessorOperatorsShouldNotBeUsedQuery() and + queryId = + // `@id` for the `andPreprocessorOperatorsShouldNotBeUsed` query + "cpp/misra/and-preprocessor-operators-should-not-be-used" and + ruleId = "RULE-19-3-1" and + category = "advisory" + or + query = + // `Query` instance for the `tokensThatLookLikeDirectivesInAMacroArgument` query + ImportMisra23Package::tokensThatLookLikeDirectivesInAMacroArgumentQuery() and + queryId = + // `@id` for the `tokensThatLookLikeDirectivesInAMacroArgument` query + "cpp/misra/tokens-that-look-like-directives-in-a-macro-argument" and + ruleId = "RULE-19-3-5" and + category = "required" + or + query = + // `Query` instance for the `pointerToAnIncompleteClassTypeDeleted` query + ImportMisra23Package::pointerToAnIncompleteClassTypeDeletedQuery() and + queryId = + // `@id` for the `pointerToAnIncompleteClassTypeDeleted` query + "cpp/misra/pointer-to-an-incomplete-class-type-deleted" and + ruleId = "RULE-21-6-5" and + category = "required" + or + query = + // `Query` instance for the `pointersReturnedByLocaleFunctionsMustBeUsedAsConst` query + ImportMisra23Package::pointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery() and + queryId = + // `@id` for the `pointersReturnedByLocaleFunctionsMustBeUsedAsConst` query + "cpp/misra/pointers-returned-by-locale-functions-must-be-used-as-const" and + ruleId = "RULE-25-5-2" and + category = "mandatory" + or + query = + // `Query` instance for the `callToSetlocaleInvalidatesOldPointersMisra` query + ImportMisra23Package::callToSetlocaleInvalidatesOldPointersMisraQuery() and + queryId = + // `@id` for the `callToSetlocaleInvalidatesOldPointersMisra` query + "cpp/misra/call-to-setlocale-invalidates-old-pointers-misra" and + ruleId = "RULE-25-5-3" and + category = "mandatory" + or + query = + // `Query` instance for the `callToSetlocaleInvalidatesOldPointersWarnMisra` query + ImportMisra23Package::callToSetlocaleInvalidatesOldPointersWarnMisraQuery() and + queryId = + // `@id` for the `callToSetlocaleInvalidatesOldPointersWarnMisra` query + "cpp/misra/call-to-setlocale-invalidates-old-pointers-warn-misra" and + ruleId = "RULE-25-5-3" and + category = "mandatory" + or + query = + // `Query` instance for the `objectUsedWhileInPotentiallyMovedFromState` query + ImportMisra23Package::objectUsedWhileInPotentiallyMovedFromStateQuery() and + queryId = + // `@id` for the `objectUsedWhileInPotentiallyMovedFromState` query + "cpp/misra/object-used-while-in-potentially-moved-from-state" and + ruleId = "RULE-28-6-3" and + category = "required" + or + query = + // `Query` instance for the `readsAndWritesOnStreamNotSeparatedByPositioning` query + ImportMisra23Package::readsAndWritesOnStreamNotSeparatedByPositioningQuery() and + queryId = + // `@id` for the `readsAndWritesOnStreamNotSeparatedByPositioning` query + "cpp/misra/reads-and-writes-on-stream-not-separated-by-positioning" and + ruleId = "RULE-30-0-2" and + category = "required" + or + query = + // `Query` instance for the `commaOperatorShouldNotBeUsed` query + ImportMisra23Package::commaOperatorShouldNotBeUsedQuery() and + queryId = + // `@id` for the `commaOperatorShouldNotBeUsed` query + "cpp/misra/comma-operator-should-not-be-used" and + ruleId = "RULE-8-19-1" and + category = "advisory" + or + query = + // `Query` instance for the `copyAndMoveAssignmentsShallHandleSelfAssignment` query + ImportMisra23Package::copyAndMoveAssignmentsShallHandleSelfAssignmentQuery() and + queryId = + // `@id` for the `copyAndMoveAssignmentsShallHandleSelfAssignment` query + "cpp/misra/copy-and-move-assignments-shall-handle-self-assignment" and + ruleId = "DIR-15-8-1" and + category = "required" + or + query = + // `Query` instance for the `useSingleLocalDeclarators` query + ImportMisra23Package::useSingleLocalDeclaratorsQuery() and + queryId = + // `@id` for the `useSingleLocalDeclarators` query + "cpp/misra/use-single-local-declarators" and + ruleId = "RULE-10-0-1" and + category = "advisory" + or + query = + // `Query` instance for the `useSingleGlobalOrMemberDeclarators` query + ImportMisra23Package::useSingleGlobalOrMemberDeclaratorsQuery() and + queryId = + // `@id` for the `useSingleGlobalOrMemberDeclarators` query + "cpp/misra/use-single-global-or-member-declarators" and + ruleId = "RULE-10-0-1" and + category = "advisory" + or + query = + // `Query` instance for the `enumerationNotDefinedWithAnExplicitUnderlyingType` query + ImportMisra23Package::enumerationNotDefinedWithAnExplicitUnderlyingTypeQuery() and + queryId = + // `@id` for the `enumerationNotDefinedWithAnExplicitUnderlyingType` query + "cpp/misra/enumeration-not-defined-with-an-explicit-underlying-type" and + ruleId = "RULE-10-2-1" and + category = "required" + or + query = + // `Query` instance for the `asmDeclarationShallNotBeUsed` query + ImportMisra23Package::asmDeclarationShallNotBeUsedQuery() and + queryId = + // `@id` for the `asmDeclarationShallNotBeUsed` query + "cpp/misra/asm-declaration-shall-not-be-used" and + ruleId = "RULE-10-4-1" and + category = "required" + or + query = + // `Query` instance for the `nonUniqueEnumerationConstant` query + ImportMisra23Package::nonUniqueEnumerationConstantQuery() and + queryId = + // `@id` for the `nonUniqueEnumerationConstant` query + "cpp/misra/non-unique-enumeration-constant" and + ruleId = "RULE-11-6-3" and + category = "required" + or + query = + // `Query` instance for the `bitFieldShallHaveAnAppropriateType` query + ImportMisra23Package::bitFieldShallHaveAnAppropriateTypeQuery() and + queryId = + // `@id` for the `bitFieldShallHaveAnAppropriateType` query + "cpp/misra/bit-field-shall-have-an-appropriate-type" and + ruleId = "RULE-12-2-2" and + category = "required" + or + query = + // `Query` instance for the `signedIntegerNamedBitFieldHaveALengthOfOneBit` query + ImportMisra23Package::signedIntegerNamedBitFieldHaveALengthOfOneBitQuery() and + queryId = + // `@id` for the `signedIntegerNamedBitFieldHaveALengthOfOneBit` query + "cpp/misra/signed-integer-named-bit-field-have-a-length-of-one-bit" and + ruleId = "RULE-12-2-3" and + category = "required" + or + query = + // `Query` instance for the `virtualAndNonVirtualClassInTheHierarchy` query + ImportMisra23Package::virtualAndNonVirtualClassInTheHierarchyQuery() and + queryId = + // `@id` for the `virtualAndNonVirtualClassInTheHierarchy` query + "cpp/misra/virtual-and-non-virtual-class-in-the-hierarchy" and + ruleId = "RULE-13-1-2" and + category = "required" + or + query = + // `Query` instance for the `overridingShallSpecifyDifferentDefaultArguments` query + ImportMisra23Package::overridingShallSpecifyDifferentDefaultArgumentsQuery() and + queryId = + // `@id` for the `overridingShallSpecifyDifferentDefaultArguments` query + "cpp/misra/overriding-shall-specify-different-default-arguments" and + ruleId = "RULE-13-3-2" and + category = "required" + or + query = + // `Query` instance for the `potentiallyVirtualPointerOnlyComparesToNullptr` query + ImportMisra23Package::potentiallyVirtualPointerOnlyComparesToNullptrQuery() and + queryId = + // `@id` for the `potentiallyVirtualPointerOnlyComparesToNullptr` query + "cpp/misra/potentially-virtual-pointer-only-compares-to-nullptr" and + ruleId = "RULE-13-3-4" and + category = "required" + or + query = + // `Query` instance for the `objectsDynamicTypeUsedFromConstructorOrDestructor` query + ImportMisra23Package::objectsDynamicTypeUsedFromConstructorOrDestructorQuery() and + queryId = + // `@id` for the `objectsDynamicTypeUsedFromConstructorOrDestructor` query + "cpp/misra/objects-dynamic-type-used-from-constructor-or-destructor" and + ruleId = "RULE-15-1-1" and + category = "required" + or + query = + // `Query` instance for the `initializeAllVirtualBaseClasses` query + ImportMisra23Package::initializeAllVirtualBaseClassesQuery() and + queryId = + // `@id` for the `initializeAllVirtualBaseClasses` query + "cpp/misra/initialize-all-virtual-base-classes" and + ruleId = "RULE-15-1-2" and + category = "advisory" + or + query = + // `Query` instance for the `initializerListConstructorIsTheOnlyConstructor` query + ImportMisra23Package::initializerListConstructorIsTheOnlyConstructorQuery() and + queryId = + // `@id` for the `initializerListConstructorIsTheOnlyConstructor` query + "cpp/misra/initializer-list-constructor-is-the-only-constructor" and + ruleId = "RULE-15-1-5" and + category = "required" + or + query = + // `Query` instance for the `addressOfOperatorOverloaded` query + ImportMisra23Package::addressOfOperatorOverloadedQuery() and + queryId = + // `@id` for the `addressOfOperatorOverloaded` query + "cpp/misra/address-of-operator-overloaded" and + ruleId = "RULE-16-5-2" and + category = "required" + or + query = + // `Query` instance for the `functionTemplatesExplicitlySpecialized` query + ImportMisra23Package::functionTemplatesExplicitlySpecializedQuery() and + queryId = + // `@id` for the `functionTemplatesExplicitlySpecialized` query + "cpp/misra/function-templates-explicitly-specialized" and + ruleId = "RULE-17-8-1" and + category = "required" + or + query = + // `Query` instance for the `exceptionObjectHavePointerType` query + ImportMisra23Package::exceptionObjectHavePointerTypeQuery() and + queryId = + // `@id` for the `exceptionObjectHavePointerType` query + "cpp/misra/exception-object-have-pointer-type" and + ruleId = "RULE-18-1-1" and + category = "required" + or + query = + // `Query` instance for the `emptyThrowOnlyWithinACatchHandler` query + ImportMisra23Package::emptyThrowOnlyWithinACatchHandlerQuery() and + queryId = + // `@id` for the `emptyThrowOnlyWithinACatchHandler` query + "cpp/misra/empty-throw-only-within-a-catch-handler" and + ruleId = "RULE-18-1-2" and + category = "required" + or + query = + // `Query` instance for the `noexceptFunctionShouldNotPropagateToTheCaller` query + ImportMisra23Package::noexceptFunctionShouldNotPropagateToTheCallerQuery() and + queryId = + // `@id` for the `noexceptFunctionShouldNotPropagateToTheCaller` query + "cpp/misra/noexcept-function-should-not-propagate-to-the-caller" and + ruleId = "RULE-18-5-1" and + category = "advisory" + or + query = + // `Query` instance for the `functionLikeMacrosDefined` query + ImportMisra23Package::functionLikeMacrosDefinedQuery() and + queryId = + // `@id` for the `functionLikeMacrosDefined` query + "cpp/misra/function-like-macros-defined" and + ruleId = "RULE-19-0-2" and + category = "required" + or + query = + // `Query` instance for the `macroParameterFollowingHash` query + ImportMisra23Package::macroParameterFollowingHashQuery() and + queryId = + // `@id` for the `macroParameterFollowingHash` query + "cpp/misra/macro-parameter-following-hash" and + ruleId = "RULE-19-3-2" and + category = "required" + or + query = + // `Query` instance for the `aMixedUseMacroArgumentSubjectToExpansion` query + ImportMisra23Package::aMixedUseMacroArgumentSubjectToExpansionQuery() and + queryId = + // `@id` for the `aMixedUseMacroArgumentSubjectToExpansion` query + "cpp/misra/a-mixed-use-macro-argument-subject-to-expansion" and + ruleId = "RULE-19-3-3" and + category = "required" + or + query = + // `Query` instance for the `csignalFacilitiesUsed` query + ImportMisra23Package::csignalFacilitiesUsedQuery() and + queryId = + // `@id` for the `csignalFacilitiesUsed` query + "cpp/misra/csignal-facilities-used" and + ruleId = "RULE-21-10-3" and + category = "required" + or + query = + // `Query` instance for the `csignalTypesShallNotBeUsed` query + ImportMisra23Package::csignalTypesShallNotBeUsedQuery() and + queryId = + // `@id` for the `csignalTypesShallNotBeUsed` query + "cpp/misra/csignal-types-shall-not-be-used" and + ruleId = "RULE-21-10-3" and + category = "required" + or + query = + // `Query` instance for the `atofAtoiAtolAndAtollUsed` query + ImportMisra23Package::atofAtoiAtolAndAtollUsedQuery() and + queryId = + // `@id` for the `atofAtoiAtolAndAtollUsed` query + "cpp/misra/atof-atoi-atol-and-atoll-used" and + ruleId = "RULE-21-2-1" and + category = "required" + or + query = + // `Query` instance for the `macroOffsetofShallNotBeUsed` query + ImportMisra23Package::macroOffsetofShallNotBeUsedQuery() and + queryId = + // `@id` for the `macroOffsetofShallNotBeUsed` query + "cpp/misra/macro-offsetof-shall-not-be-used" and + ruleId = "RULE-21-2-4" and + category = "required" + or + query = + // `Query` instance for the `globalSizedOperatorDeleteShallBeDefined` query + ImportMisra23Package::globalSizedOperatorDeleteShallBeDefinedQuery() and + queryId = + // `@id` for the `globalSizedOperatorDeleteShallBeDefined` query + "cpp/misra/global-sized-operator-delete-shall-be-defined" and + ruleId = "RULE-21-6-4" and + category = "required" + or + query = + // `Query` instance for the `globalUnsizedOperatorDeleteShallBeDefined` query + ImportMisra23Package::globalUnsizedOperatorDeleteShallBeDefinedQuery() and + queryId = + // `@id` for the `globalUnsizedOperatorDeleteShallBeDefined` query + "cpp/misra/global-unsized-operator-delete-shall-be-defined" and + ruleId = "RULE-21-6-4" and + category = "required" + or + query = + // `Query` instance for the `vectorShouldNotBeSpecializedWithBool` query + ImportMisra23Package::vectorShouldNotBeSpecializedWithBoolQuery() and + queryId = + // `@id` for the `vectorShouldNotBeSpecializedWithBool` query + "cpp/misra/vector-should-not-be-specialized-with-bool" and + ruleId = "RULE-26-3-1" and + category = "advisory" + or + query = + // `Query` instance for the `forwardingReferencesAndForwardNotUsedTogether` query + ImportMisra23Package::forwardingReferencesAndForwardNotUsedTogetherQuery() and + queryId = + // `@id` for the `forwardingReferencesAndForwardNotUsedTogether` query + "cpp/misra/forwarding-references-and-forward-not-used-together" and + ruleId = "RULE-28-6-2" and + category = "required" + or + query = + // `Query` instance for the `cstdioFunctionsShallNotBeUsed` query + ImportMisra23Package::cstdioFunctionsShallNotBeUsedQuery() and + queryId = + // `@id` for the `cstdioFunctionsShallNotBeUsed` query + "cpp/misra/cstdio-functions-shall-not-be-used" and + ruleId = "RULE-30-0-1" and + category = "required" + or + query = + // `Query` instance for the `cstdioMacrosShallNotBeUsed` query + ImportMisra23Package::cstdioMacrosShallNotBeUsedQuery() and + queryId = + // `@id` for the `cstdioMacrosShallNotBeUsed` query + "cpp/misra/cstdio-macros-shall-not-be-used" and + ruleId = "RULE-30-0-1" and + category = "required" + or + query = + // `Query` instance for the `cstdioTypesShallNotBeUsed` query + ImportMisra23Package::cstdioTypesShallNotBeUsedQuery() and + queryId = + // `@id` for the `cstdioTypesShallNotBeUsed` query + "cpp/misra/cstdio-types-shall-not-be-used" and + ruleId = "RULE-30-0-1" and + category = "required" + or + query = + // `Query` instance for the `backslashCharacterMisuse` query + ImportMisra23Package::backslashCharacterMisuseQuery() and + queryId = + // `@id` for the `backslashCharacterMisuse` query + "cpp/misra/backslash-character-misuse" and + ruleId = "RULE-5-13-1" and + category = "required" + or + query = + // `Query` instance for the `nonTerminatedEscapeSequences` query + ImportMisra23Package::nonTerminatedEscapeSequencesQuery() and + queryId = + // `@id` for the `nonTerminatedEscapeSequences` query + "cpp/misra/non-terminated-escape-sequences" and + ruleId = "RULE-5-13-2" and + category = "required" + or + query = + // `Query` instance for the `octalConstantsUsed` query + ImportMisra23Package::octalConstantsUsedQuery() and + queryId = + // `@id` for the `octalConstantsUsed` query + "cpp/misra/octal-constants-used" and + ruleId = "RULE-5-13-3" and + category = "required" + or + query = + // `Query` instance for the `unsignedIntegerLiteralsNotAppropriatelySuffixed` query + ImportMisra23Package::unsignedIntegerLiteralsNotAppropriatelySuffixedQuery() and + queryId = + // `@id` for the `unsignedIntegerLiteralsNotAppropriatelySuffixed` query + "cpp/misra/unsigned-integer-literals-not-appropriately-suffixed" and + ruleId = "RULE-5-13-4" and + category = "required" + or + query = + // `Query` instance for the `lowercaseLStartsInLiteralSuffix` query + ImportMisra23Package::lowercaseLStartsInLiteralSuffixQuery() and + queryId = + // `@id` for the `lowercaseLStartsInLiteralSuffix` query + "cpp/misra/lowercase-l-starts-in-literal-suffix" and + ruleId = "RULE-5-13-5" and + category = "required" + or + query = + // `Query` instance for the `characterSequenceUsedWithinACStyleComment` query + ImportMisra23Package::characterSequenceUsedWithinACStyleCommentQuery() and + queryId = + // `@id` for the `characterSequenceUsedWithinACStyleComment` query + "cpp/misra/character-sequence-used-within-ac-style-comment" and + ruleId = "RULE-5-7-1" and + category = "required" + or + query = + // `Query` instance for the `lineSplicingUsedInComments` query + ImportMisra23Package::lineSplicingUsedInCommentsQuery() and + queryId = + // `@id` for the `lineSplicingUsedInComments` query + "cpp/misra/line-splicing-used-in-comments" and + ruleId = "RULE-5-7-3" and + category = "required" + or + query = + // `Query` instance for the `globalNamespaceDeclarations` query + ImportMisra23Package::globalNamespaceDeclarationsQuery() and + queryId = + // `@id` for the `globalNamespaceDeclarations` query + "cpp/misra/global-namespace-declarations" and + ruleId = "RULE-6-0-3" and + category = "advisory" + or + query = + // `Query` instance for the `nonGlobalFunctionMain` query + ImportMisra23Package::nonGlobalFunctionMainQuery() and + queryId = + // `@id` for the `nonGlobalFunctionMain` query + "cpp/misra/non-global-function-main" and + ruleId = "RULE-6-0-4" and + category = "required" + or + query = + // `Query` instance for the `inheritedNonOverridableMemberFunction` query + ImportMisra23Package::inheritedNonOverridableMemberFunctionQuery() and + queryId = + // `@id` for the `inheritedNonOverridableMemberFunction` query + "cpp/misra/inherited-non-overridable-member-function" and + ruleId = "RULE-6-4-2" and + category = "required" + or + query = + // `Query` instance for the `inheritedOverridableMemberFunction` query + ImportMisra23Package::inheritedOverridableMemberFunctionQuery() and + queryId = + // `@id` for the `inheritedOverridableMemberFunction` query + "cpp/misra/inherited-overridable-member-function" and + ruleId = "RULE-6-4-2" and + category = "required" + or + query = + // `Query` instance for the `definitionShallBeConsideredForUnqualifiedLookup` query + ImportMisra23Package::definitionShallBeConsideredForUnqualifiedLookupQuery() and + queryId = + // `@id` for the `definitionShallBeConsideredForUnqualifiedLookup` query + "cpp/misra/definition-shall-be-considered-for-unqualified-lookup" and + ruleId = "RULE-6-4-2" and + category = "required" + or + query = + // `Query` instance for the `nameShallBeReferredUsingAQualifiedIdOrThis` query + ImportMisra23Package::nameShallBeReferredUsingAQualifiedIdOrThisQuery() and + queryId = + // `@id` for the `nameShallBeReferredUsingAQualifiedIdOrThis` query + "cpp/misra/name-shall-be-referred-using-a-qualified-id-or-this" and + ruleId = "RULE-6-4-3" and + category = "required" + or + query = + // `Query` instance for the `nameShallBeReferredUsingAQualifiedIdOrThisAudit` query + ImportMisra23Package::nameShallBeReferredUsingAQualifiedIdOrThisAuditQuery() and + queryId = + // `@id` for the `nameShallBeReferredUsingAQualifiedIdOrThisAudit` query + "cpp/misra/name-shall-be-referred-using-a-qualified-id-or-this-audit" and + ruleId = "RULE-6-4-3" and + category = "required" + or + query = + // `Query` instance for the `returnReferenceOrPointerToAutomaticLocalVariable` query + ImportMisra23Package::returnReferenceOrPointerToAutomaticLocalVariableQuery() and + queryId = + // `@id` for the `returnReferenceOrPointerToAutomaticLocalVariable` query + "cpp/misra/return-reference-or-pointer-to-automatic-local-variable" and + ruleId = "RULE-6-8-2" and + category = "mandatory" + or + query = + // `Query` instance for the `nullptrNotTheOnlyFormOfTheNullPointerConstant` query + ImportMisra23Package::nullptrNotTheOnlyFormOfTheNullPointerConstantQuery() and + queryId = + // `@id` for the `nullptrNotTheOnlyFormOfTheNullPointerConstant` query + "cpp/misra/nullptr-not-the-only-form-of-the-null-pointer-constant" and + ruleId = "RULE-7-11-1" and + category = "required" + or + query = + // `Query` instance for the `arrayPassedAsFunctionArgumentDecayToAPointer` query + ImportMisra23Package::arrayPassedAsFunctionArgumentDecayToAPointerQuery() and + queryId = + // `@id` for the `arrayPassedAsFunctionArgumentDecayToAPointer` query + "cpp/misra/array-passed-as-function-argument-decay-to-a-pointer" and + ruleId = "RULE-7-11-2" and + category = "required" + or + query = + // `Query` instance for the `resultOfAnAssignmentOperatorShouldNotBeUsed` query + ImportMisra23Package::resultOfAnAssignmentOperatorShouldNotBeUsedQuery() and + queryId = + // `@id` for the `resultOfAnAssignmentOperatorShouldNotBeUsed` query + "cpp/misra/result-of-an-assignment-operator-should-not-be-used" and + ruleId = "RULE-8-18-2" and + category = "advisory" + or + query = + // `Query` instance for the `functionsCallThemselvesEitherDirectlyOrIndirectly` query + ImportMisra23Package::functionsCallThemselvesEitherDirectlyOrIndirectlyQuery() and + queryId = + // `@id` for the `functionsCallThemselvesEitherDirectlyOrIndirectly` query + "cpp/misra/functions-call-themselves-either-directly-or-indirectly" and + ruleId = "RULE-8-2-10" and + category = "required" + or + query = + // `Query` instance for the `castsBetweenAPointerToFunctionAndAnyOtherType` query + ImportMisra23Package::castsBetweenAPointerToFunctionAndAnyOtherTypeQuery() and + queryId = + // `@id` for the `castsBetweenAPointerToFunctionAndAnyOtherType` query + "cpp/misra/casts-between-a-pointer-to-function-and-any-other-type" and + ruleId = "RULE-8-2-4" and + category = "required" + or + query = + // `Query` instance for the `reinterpretCastShallNotBeUsed` query + ImportMisra23Package::reinterpretCastShallNotBeUsedQuery() and + queryId = + // `@id` for the `reinterpretCastShallNotBeUsed` query + "cpp/misra/reinterpret-cast-shall-not-be-used" and + ruleId = "RULE-8-2-5" and + category = "required" + or + query = + // `Query` instance for the `unsignedOperationWithConstantOperandsWraps` query + ImportMisra23Package::unsignedOperationWithConstantOperandsWrapsQuery() and + queryId = + // `@id` for the `unsignedOperationWithConstantOperandsWraps` query + "cpp/misra/unsigned-operation-with-constant-operands-wraps" and + ruleId = "RULE-8-20-1" and + category = "advisory" + or + query = + // `Query` instance for the `builtInUnaryOperatorAppliedToUnsignedExpression` query + ImportMisra23Package::builtInUnaryOperatorAppliedToUnsignedExpressionQuery() and + queryId = + // `@id` for the `builtInUnaryOperatorAppliedToUnsignedExpression` query + "cpp/misra/built-in-unary-operator-applied-to-unsigned-expression" and + ruleId = "RULE-8-3-1" and + category = "advisory" + or + query = + // `Query` instance for the `switchBodyCompoundCondition` query + ImportMisra23Package::switchBodyCompoundConditionQuery() and + queryId = + // `@id` for the `switchBodyCompoundCondition` query + "cpp/misra/switch-body-compound-condition" and + ruleId = "RULE-9-3-1" and + category = "required" + or + query = + // `Query` instance for the `loopBodyCompoundCondition` query + ImportMisra23Package::loopBodyCompoundConditionQuery() and + queryId = + // `@id` for the `loopBodyCompoundCondition` query + "cpp/misra/loop-body-compound-condition" and + ruleId = "RULE-9-3-1" and + category = "required" + or + query = + // `Query` instance for the `gotoStatementShouldNotBeUsed` query + ImportMisra23Package::gotoStatementShouldNotBeUsedQuery() and + queryId = + // `@id` for the `gotoStatementShouldNotBeUsed` query + "cpp/misra/goto-statement-should-not-be-used" and + ruleId = "RULE-9-6-1" and + category = "advisory" + or + query = + // `Query` instance for the `gotoReferenceALabelInSurroundingBlock` query + ImportMisra23Package::gotoReferenceALabelInSurroundingBlockQuery() and + queryId = + // `@id` for the `gotoReferenceALabelInSurroundingBlock` query + "cpp/misra/goto-reference-a-label-in-surrounding-block" and + ruleId = "RULE-9-6-2" and + category = "required" +} + +module ImportMisra23Package { + Query sectionsOfCodeShouldNotBeCommentedOutQuery() { + //autogenerate `Query` type + result = + // `Query` type for `sectionsOfCodeShouldNotBeCommentedOut` query + TQueryCPP(TImportMisra23PackageQuery(TSectionsOfCodeShouldNotBeCommentedOutQuery())) + } + + Query oneDefinitionRuleViolatedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `oneDefinitionRuleViolated` query + TQueryCPP(TImportMisra23PackageQuery(TOneDefinitionRuleViolatedQuery())) + } + + Query variableDeclaredInInnerScopeHidesOuterScopeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `variableDeclaredInInnerScopeHidesOuterScope` query + TQueryCPP(TImportMisra23PackageQuery(TVariableDeclaredInInnerScopeHidesOuterScopeQuery())) + } + + Query objectAccessedBeforeLifetimeMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectAccessedBeforeLifetimeMisra` query + TQueryCPP(TImportMisra23PackageQuery(TObjectAccessedBeforeLifetimeMisraQuery())) + } + + Query objectAccessedAfterLifetimeMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectAccessedAfterLifetimeMisra` query + TQueryCPP(TImportMisra23PackageQuery(TObjectAccessedAfterLifetimeMisraQuery())) + } + + Query castRemovesConstOrVolatileFromPointerOrReferenceQuery() { + //autogenerate `Query` type + result = + // `Query` type for `castRemovesConstOrVolatileFromPointerOrReference` query + TQueryCPP(TImportMisra23PackageQuery(TCastRemovesConstOrVolatileFromPointerOrReferenceQuery())) + } + + Query ifElseIfEndConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `ifElseIfEndCondition` query + TQueryCPP(TImportMisra23PackageQuery(TIfElseIfEndConditionQuery())) + } + + Query gotoShallJumpToLabelDeclaredLaterInTheFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoShallJumpToLabelDeclaredLaterInTheFunction` query + TQueryCPP(TImportMisra23PackageQuery(TGotoShallJumpToLabelDeclaredLaterInTheFunctionQuery())) + } + + Query functionDeclaredWithTheNoreturnAttributeReturnQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionDeclaredWithTheNoreturnAttributeReturn` query + TQueryCPP(TImportMisra23PackageQuery(TFunctionDeclaredWithTheNoreturnAttributeReturnQuery())) + } + + Query nonVoidFunctionShallReturnAValueOnAllPathsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonVoidFunctionShallReturnAValueOnAllPaths` query + TQueryCPP(TImportMisra23PackageQuery(TNonVoidFunctionShallReturnAValueOnAllPathsQuery())) + } + + Query declarationOfAnObjectIndirectionsLevelQuery() { + //autogenerate `Query` type + result = + // `Query` type for `declarationOfAnObjectIndirectionsLevel` query + TQueryCPP(TImportMisra23PackageQuery(TDeclarationOfAnObjectIndirectionsLevelQuery())) + } + + Query handlersReferToNonStaticMembersFromTheirClassQuery() { + //autogenerate `Query` type + result = + // `Query` type for `handlersReferToNonStaticMembersFromTheirClass` query + TQueryCPP(TImportMisra23PackageQuery(THandlersReferToNonStaticMembersFromTheirClassQuery())) + } + + Query includeDirectivesPrecededByPreprocessorDirectivesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `includeDirectivesPrecededByPreprocessorDirectives` query + TQueryCPP(TImportMisra23PackageQuery(TIncludeDirectivesPrecededByPreprocessorDirectivesQuery())) + } + + Query identifiersUsedInTheControllingExpressionOfQuery() { + //autogenerate `Query` type + result = + // `Query` type for `identifiersUsedInTheControllingExpressionOf` query + TQueryCPP(TImportMisra23PackageQuery(TIdentifiersUsedInTheControllingExpressionOfQuery())) + } + + Query charsThatShouldNotOccurInHeaderFileNameQuery() { + //autogenerate `Query` type + result = + // `Query` type for `charsThatShouldNotOccurInHeaderFileName` query + TQueryCPP(TImportMisra23PackageQuery(TCharsThatShouldNotOccurInHeaderFileNameQuery())) + } + + Query andPreprocessorOperatorsShouldNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `andPreprocessorOperatorsShouldNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TAndPreprocessorOperatorsShouldNotBeUsedQuery())) + } + + Query tokensThatLookLikeDirectivesInAMacroArgumentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `tokensThatLookLikeDirectivesInAMacroArgument` query + TQueryCPP(TImportMisra23PackageQuery(TTokensThatLookLikeDirectivesInAMacroArgumentQuery())) + } + + Query pointerToAnIncompleteClassTypeDeletedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointerToAnIncompleteClassTypeDeleted` query + TQueryCPP(TImportMisra23PackageQuery(TPointerToAnIncompleteClassTypeDeletedQuery())) + } + + Query pointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointersReturnedByLocaleFunctionsMustBeUsedAsConst` query + TQueryCPP(TImportMisra23PackageQuery(TPointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery())) + } + + Query callToSetlocaleInvalidatesOldPointersMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `callToSetlocaleInvalidatesOldPointersMisra` query + TQueryCPP(TImportMisra23PackageQuery(TCallToSetlocaleInvalidatesOldPointersMisraQuery())) + } + + Query callToSetlocaleInvalidatesOldPointersWarnMisraQuery() { + //autogenerate `Query` type + result = + // `Query` type for `callToSetlocaleInvalidatesOldPointersWarnMisra` query + TQueryCPP(TImportMisra23PackageQuery(TCallToSetlocaleInvalidatesOldPointersWarnMisraQuery())) + } + + Query objectUsedWhileInPotentiallyMovedFromStateQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectUsedWhileInPotentiallyMovedFromState` query + TQueryCPP(TImportMisra23PackageQuery(TObjectUsedWhileInPotentiallyMovedFromStateQuery())) + } + + Query readsAndWritesOnStreamNotSeparatedByPositioningQuery() { + //autogenerate `Query` type + result = + // `Query` type for `readsAndWritesOnStreamNotSeparatedByPositioning` query + TQueryCPP(TImportMisra23PackageQuery(TReadsAndWritesOnStreamNotSeparatedByPositioningQuery())) + } + + Query commaOperatorShouldNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `commaOperatorShouldNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TCommaOperatorShouldNotBeUsedQuery())) + } + + Query copyAndMoveAssignmentsShallHandleSelfAssignmentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `copyAndMoveAssignmentsShallHandleSelfAssignment` query + TQueryCPP(TImportMisra23PackageQuery(TCopyAndMoveAssignmentsShallHandleSelfAssignmentQuery())) + } + + Query useSingleLocalDeclaratorsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useSingleLocalDeclarators` query + TQueryCPP(TImportMisra23PackageQuery(TUseSingleLocalDeclaratorsQuery())) + } + + Query useSingleGlobalOrMemberDeclaratorsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useSingleGlobalOrMemberDeclarators` query + TQueryCPP(TImportMisra23PackageQuery(TUseSingleGlobalOrMemberDeclaratorsQuery())) + } + + Query enumerationNotDefinedWithAnExplicitUnderlyingTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `enumerationNotDefinedWithAnExplicitUnderlyingType` query + TQueryCPP(TImportMisra23PackageQuery(TEnumerationNotDefinedWithAnExplicitUnderlyingTypeQuery())) + } + + Query asmDeclarationShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `asmDeclarationShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TAsmDeclarationShallNotBeUsedQuery())) + } + + Query nonUniqueEnumerationConstantQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonUniqueEnumerationConstant` query + TQueryCPP(TImportMisra23PackageQuery(TNonUniqueEnumerationConstantQuery())) + } + + Query bitFieldShallHaveAnAppropriateTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `bitFieldShallHaveAnAppropriateType` query + TQueryCPP(TImportMisra23PackageQuery(TBitFieldShallHaveAnAppropriateTypeQuery())) + } + + Query signedIntegerNamedBitFieldHaveALengthOfOneBitQuery() { + //autogenerate `Query` type + result = + // `Query` type for `signedIntegerNamedBitFieldHaveALengthOfOneBit` query + TQueryCPP(TImportMisra23PackageQuery(TSignedIntegerNamedBitFieldHaveALengthOfOneBitQuery())) + } + + Query virtualAndNonVirtualClassInTheHierarchyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `virtualAndNonVirtualClassInTheHierarchy` query + TQueryCPP(TImportMisra23PackageQuery(TVirtualAndNonVirtualClassInTheHierarchyQuery())) + } + + Query overridingShallSpecifyDifferentDefaultArgumentsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `overridingShallSpecifyDifferentDefaultArguments` query + TQueryCPP(TImportMisra23PackageQuery(TOverridingShallSpecifyDifferentDefaultArgumentsQuery())) + } + + Query potentiallyVirtualPointerOnlyComparesToNullptrQuery() { + //autogenerate `Query` type + result = + // `Query` type for `potentiallyVirtualPointerOnlyComparesToNullptr` query + TQueryCPP(TImportMisra23PackageQuery(TPotentiallyVirtualPointerOnlyComparesToNullptrQuery())) + } + + Query objectsDynamicTypeUsedFromConstructorOrDestructorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `objectsDynamicTypeUsedFromConstructorOrDestructor` query + TQueryCPP(TImportMisra23PackageQuery(TObjectsDynamicTypeUsedFromConstructorOrDestructorQuery())) + } + + Query initializeAllVirtualBaseClassesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `initializeAllVirtualBaseClasses` query + TQueryCPP(TImportMisra23PackageQuery(TInitializeAllVirtualBaseClassesQuery())) + } + + Query initializerListConstructorIsTheOnlyConstructorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `initializerListConstructorIsTheOnlyConstructor` query + TQueryCPP(TImportMisra23PackageQuery(TInitializerListConstructorIsTheOnlyConstructorQuery())) + } + + Query addressOfOperatorOverloadedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `addressOfOperatorOverloaded` query + TQueryCPP(TImportMisra23PackageQuery(TAddressOfOperatorOverloadedQuery())) + } + + Query functionTemplatesExplicitlySpecializedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionTemplatesExplicitlySpecialized` query + TQueryCPP(TImportMisra23PackageQuery(TFunctionTemplatesExplicitlySpecializedQuery())) + } + + Query exceptionObjectHavePointerTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `exceptionObjectHavePointerType` query + TQueryCPP(TImportMisra23PackageQuery(TExceptionObjectHavePointerTypeQuery())) + } + + Query emptyThrowOnlyWithinACatchHandlerQuery() { + //autogenerate `Query` type + result = + // `Query` type for `emptyThrowOnlyWithinACatchHandler` query + TQueryCPP(TImportMisra23PackageQuery(TEmptyThrowOnlyWithinACatchHandlerQuery())) + } + + Query noexceptFunctionShouldNotPropagateToTheCallerQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noexceptFunctionShouldNotPropagateToTheCaller` query + TQueryCPP(TImportMisra23PackageQuery(TNoexceptFunctionShouldNotPropagateToTheCallerQuery())) + } + + Query functionLikeMacrosDefinedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionLikeMacrosDefined` query + TQueryCPP(TImportMisra23PackageQuery(TFunctionLikeMacrosDefinedQuery())) + } + + Query macroParameterFollowingHashQuery() { + //autogenerate `Query` type + result = + // `Query` type for `macroParameterFollowingHash` query + TQueryCPP(TImportMisra23PackageQuery(TMacroParameterFollowingHashQuery())) + } + + Query aMixedUseMacroArgumentSubjectToExpansionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `aMixedUseMacroArgumentSubjectToExpansion` query + TQueryCPP(TImportMisra23PackageQuery(TAMixedUseMacroArgumentSubjectToExpansionQuery())) + } + + Query csignalFacilitiesUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `csignalFacilitiesUsed` query + TQueryCPP(TImportMisra23PackageQuery(TCsignalFacilitiesUsedQuery())) + } + + Query csignalTypesShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `csignalTypesShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TCsignalTypesShallNotBeUsedQuery())) + } + + Query atofAtoiAtolAndAtollUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `atofAtoiAtolAndAtollUsed` query + TQueryCPP(TImportMisra23PackageQuery(TAtofAtoiAtolAndAtollUsedQuery())) + } + + Query macroOffsetofShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `macroOffsetofShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TMacroOffsetofShallNotBeUsedQuery())) + } + + Query globalSizedOperatorDeleteShallBeDefinedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `globalSizedOperatorDeleteShallBeDefined` query + TQueryCPP(TImportMisra23PackageQuery(TGlobalSizedOperatorDeleteShallBeDefinedQuery())) + } + + Query globalUnsizedOperatorDeleteShallBeDefinedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `globalUnsizedOperatorDeleteShallBeDefined` query + TQueryCPP(TImportMisra23PackageQuery(TGlobalUnsizedOperatorDeleteShallBeDefinedQuery())) + } + + Query vectorShouldNotBeSpecializedWithBoolQuery() { + //autogenerate `Query` type + result = + // `Query` type for `vectorShouldNotBeSpecializedWithBool` query + TQueryCPP(TImportMisra23PackageQuery(TVectorShouldNotBeSpecializedWithBoolQuery())) + } + + Query forwardingReferencesAndForwardNotUsedTogetherQuery() { + //autogenerate `Query` type + result = + // `Query` type for `forwardingReferencesAndForwardNotUsedTogether` query + TQueryCPP(TImportMisra23PackageQuery(TForwardingReferencesAndForwardNotUsedTogetherQuery())) + } + + Query cstdioFunctionsShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `cstdioFunctionsShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TCstdioFunctionsShallNotBeUsedQuery())) + } + + Query cstdioMacrosShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `cstdioMacrosShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TCstdioMacrosShallNotBeUsedQuery())) + } + + Query cstdioTypesShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `cstdioTypesShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TCstdioTypesShallNotBeUsedQuery())) + } + + Query backslashCharacterMisuseQuery() { + //autogenerate `Query` type + result = + // `Query` type for `backslashCharacterMisuse` query + TQueryCPP(TImportMisra23PackageQuery(TBackslashCharacterMisuseQuery())) + } + + Query nonTerminatedEscapeSequencesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonTerminatedEscapeSequences` query + TQueryCPP(TImportMisra23PackageQuery(TNonTerminatedEscapeSequencesQuery())) + } + + Query octalConstantsUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `octalConstantsUsed` query + TQueryCPP(TImportMisra23PackageQuery(TOctalConstantsUsedQuery())) + } + + Query unsignedIntegerLiteralsNotAppropriatelySuffixedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unsignedIntegerLiteralsNotAppropriatelySuffixed` query + TQueryCPP(TImportMisra23PackageQuery(TUnsignedIntegerLiteralsNotAppropriatelySuffixedQuery())) + } + + Query lowercaseLStartsInLiteralSuffixQuery() { + //autogenerate `Query` type + result = + // `Query` type for `lowercaseLStartsInLiteralSuffix` query + TQueryCPP(TImportMisra23PackageQuery(TLowercaseLStartsInLiteralSuffixQuery())) + } + + Query characterSequenceUsedWithinACStyleCommentQuery() { + //autogenerate `Query` type + result = + // `Query` type for `characterSequenceUsedWithinACStyleComment` query + TQueryCPP(TImportMisra23PackageQuery(TCharacterSequenceUsedWithinACStyleCommentQuery())) + } + + Query lineSplicingUsedInCommentsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `lineSplicingUsedInComments` query + TQueryCPP(TImportMisra23PackageQuery(TLineSplicingUsedInCommentsQuery())) + } + + Query globalNamespaceDeclarationsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `globalNamespaceDeclarations` query + TQueryCPP(TImportMisra23PackageQuery(TGlobalNamespaceDeclarationsQuery())) + } + + Query nonGlobalFunctionMainQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonGlobalFunctionMain` query + TQueryCPP(TImportMisra23PackageQuery(TNonGlobalFunctionMainQuery())) + } + + Query inheritedNonOverridableMemberFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inheritedNonOverridableMemberFunction` query + TQueryCPP(TImportMisra23PackageQuery(TInheritedNonOverridableMemberFunctionQuery())) + } + + Query inheritedOverridableMemberFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inheritedOverridableMemberFunction` query + TQueryCPP(TImportMisra23PackageQuery(TInheritedOverridableMemberFunctionQuery())) + } + + Query definitionShallBeConsideredForUnqualifiedLookupQuery() { + //autogenerate `Query` type + result = + // `Query` type for `definitionShallBeConsideredForUnqualifiedLookup` query + TQueryCPP(TImportMisra23PackageQuery(TDefinitionShallBeConsideredForUnqualifiedLookupQuery())) + } + + Query nameShallBeReferredUsingAQualifiedIdOrThisQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nameShallBeReferredUsingAQualifiedIdOrThis` query + TQueryCPP(TImportMisra23PackageQuery(TNameShallBeReferredUsingAQualifiedIdOrThisQuery())) + } + + Query nameShallBeReferredUsingAQualifiedIdOrThisAuditQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nameShallBeReferredUsingAQualifiedIdOrThisAudit` query + TQueryCPP(TImportMisra23PackageQuery(TNameShallBeReferredUsingAQualifiedIdOrThisAuditQuery())) + } + + Query returnReferenceOrPointerToAutomaticLocalVariableQuery() { + //autogenerate `Query` type + result = + // `Query` type for `returnReferenceOrPointerToAutomaticLocalVariable` query + TQueryCPP(TImportMisra23PackageQuery(TReturnReferenceOrPointerToAutomaticLocalVariableQuery())) + } + + Query nullptrNotTheOnlyFormOfTheNullPointerConstantQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nullptrNotTheOnlyFormOfTheNullPointerConstant` query + TQueryCPP(TImportMisra23PackageQuery(TNullptrNotTheOnlyFormOfTheNullPointerConstantQuery())) + } + + Query arrayPassedAsFunctionArgumentDecayToAPointerQuery() { + //autogenerate `Query` type + result = + // `Query` type for `arrayPassedAsFunctionArgumentDecayToAPointer` query + TQueryCPP(TImportMisra23PackageQuery(TArrayPassedAsFunctionArgumentDecayToAPointerQuery())) + } + + Query resultOfAnAssignmentOperatorShouldNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `resultOfAnAssignmentOperatorShouldNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TResultOfAnAssignmentOperatorShouldNotBeUsedQuery())) + } + + Query functionsCallThemselvesEitherDirectlyOrIndirectlyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionsCallThemselvesEitherDirectlyOrIndirectly` query + TQueryCPP(TImportMisra23PackageQuery(TFunctionsCallThemselvesEitherDirectlyOrIndirectlyQuery())) + } + + Query castsBetweenAPointerToFunctionAndAnyOtherTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `castsBetweenAPointerToFunctionAndAnyOtherType` query + TQueryCPP(TImportMisra23PackageQuery(TCastsBetweenAPointerToFunctionAndAnyOtherTypeQuery())) + } + + Query reinterpretCastShallNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `reinterpretCastShallNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TReinterpretCastShallNotBeUsedQuery())) + } + + Query unsignedOperationWithConstantOperandsWrapsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unsignedOperationWithConstantOperandsWraps` query + TQueryCPP(TImportMisra23PackageQuery(TUnsignedOperationWithConstantOperandsWrapsQuery())) + } + + Query builtInUnaryOperatorAppliedToUnsignedExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `builtInUnaryOperatorAppliedToUnsignedExpression` query + TQueryCPP(TImportMisra23PackageQuery(TBuiltInUnaryOperatorAppliedToUnsignedExpressionQuery())) + } + + Query switchBodyCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchBodyCompoundCondition` query + TQueryCPP(TImportMisra23PackageQuery(TSwitchBodyCompoundConditionQuery())) + } + + Query loopBodyCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `loopBodyCompoundCondition` query + TQueryCPP(TImportMisra23PackageQuery(TLoopBodyCompoundConditionQuery())) + } + + Query gotoStatementShouldNotBeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoStatementShouldNotBeUsed` query + TQueryCPP(TImportMisra23PackageQuery(TGotoStatementShouldNotBeUsedQuery())) + } + + Query gotoReferenceALabelInSurroundingBlockQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoReferenceALabelInSurroundingBlock` query + TQueryCPP(TImportMisra23PackageQuery(TGotoReferenceALabelInSurroundingBlockQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Includes.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Includes.qll index f446959ac5..f4eb83bc3e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Includes.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Includes.qll @@ -11,14 +11,15 @@ newtype IncludesQuery = TExternalLinkageNotDeclaredInHeaderFileQuery() or TIncludeGuardsNotProvidedQuery() -predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `unusedIncludeDirectives` query IncludesPackage::unusedIncludeDirectivesQuery() and queryId = // `@id` for the `unusedIncludeDirectives` query "cpp/autosar/unused-include-directives" and - ruleId = "A16-2-2" + ruleId = "A16-2-2" and + category = "required" or query = // `Query` instance for the `violationsOfOneDefinitionRule` query @@ -26,7 +27,8 @@ predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `violationsOfOneDefinitionRule` query "cpp/autosar/violations-of-one-definition-rule" and - ruleId = "A3-1-1" + ruleId = "A3-1-1" and + category = "required" or query = // `Query` instance for the `headerFileExpectedFileNameExtension` query @@ -34,7 +36,8 @@ predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `headerFileExpectedFileNameExtension` query "cpp/autosar/header-file-expected-file-name-extension" and - ruleId = "A3-1-2" + ruleId = "A3-1-2" and + category = "required" or query = // `Query` instance for the `fileNameExtensionCpp` query @@ -42,7 +45,8 @@ predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `fileNameExtensionCpp` query "cpp/autosar/file-name-extension-cpp" and - ruleId = "A3-1-3" + ruleId = "A3-1-3" and + category = "advisory" or query = // `Query` instance for the `externalLinkageNotDeclaredInHeaderFile` query @@ -50,7 +54,8 @@ predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `externalLinkageNotDeclaredInHeaderFile` query "cpp/autosar/external-linkage-not-declared-in-header-file" and - ruleId = "A3-3-1" + ruleId = "A3-3-1" and + category = "required" or query = // `Query` instance for the `includeGuardsNotProvided` query @@ -58,7 +63,8 @@ predicate isIncludesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `includeGuardsNotProvided` query "cpp/autosar/include-guards-not-provided" and - ruleId = "M16-2-3" + ruleId = "M16-2-3" and + category = "required" } module IncludesPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Inheritance.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Inheritance.qll index fe8ae3f155..a3775b87d6 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Inheritance.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Inheritance.qll @@ -18,14 +18,15 @@ newtype InheritanceQuery = TDoNotSliceDerivedObjectsQuery() or TDoNotDeleteAPolymorphicObjectWithoutAVirtualDestructorQuery() -predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) { +predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `publicInheritanceNotUsedForIsARelationship` query InheritancePackage::publicInheritanceNotUsedForIsARelationshipQuery() and queryId = // `@id` for the `publicInheritanceNotUsedForIsARelationship` query "cpp/autosar/public-inheritance-not-used-for-is-a-relationship" and - ruleId = "A10-0-1" + ruleId = "A10-0-1" and + category = "required" or query = // `Query` instance for the `nonPublicInheritanceNotUsedForHasARelationship` query @@ -33,7 +34,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `nonPublicInheritanceNotUsedForHasARelationship` query "cpp/autosar/non-public-inheritance-not-used-for-has-a-relationship" and - ruleId = "A10-0-2" + ruleId = "A10-0-2" and + category = "required" or query = // `Query` instance for the `classDerivedFromMoreThanOneNonInterfaceBaseClass` query @@ -41,7 +43,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `classDerivedFromMoreThanOneNonInterfaceBaseClass` query "cpp/autosar/class-derived-from-more-than-one-non-interface-base-class" and - ruleId = "A10-1-1" + ruleId = "A10-1-1" and + category = "required" or query = // `Query` instance for the `hierarchiesShouldBeBasedOnInterfaceClasses` query @@ -49,7 +52,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `hierarchiesShouldBeBasedOnInterfaceClasses` query "cpp/autosar/hierarchies-should-be-based-on-interface-classes" and - ruleId = "A10-4-1" + ruleId = "A10-4-1" and + category = "advisory" or query = // `Query` instance for the `classesShouldNotBeDerivedFromVirtualBases` query @@ -57,7 +61,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `classesShouldNotBeDerivedFromVirtualBases` query "cpp/autosar/classes-should-not-be-derived-from-virtual-bases" and - ruleId = "M10-1-1" + ruleId = "M10-1-1" and + category = "advisory" or query = // `Query` instance for the `baseClassCanBeVirtualOnlyInDiamondHierarchy` query @@ -65,7 +70,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `baseClassCanBeVirtualOnlyInDiamondHierarchy` query "cpp/autosar/base-class-can-be-virtual-only-in-diamond-hierarchy" and - ruleId = "M10-1-2" + ruleId = "M10-1-2" and + category = "required" or query = // `Query` instance for the `accessibleBaseClassBothVirtualAndNonVirtualInHierarchy` query @@ -73,7 +79,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `accessibleBaseClassBothVirtualAndNonVirtualInHierarchy` query "cpp/autosar/accessible-base-class-both-virtual-and-non-virtual-in-hierarchy" and - ruleId = "M10-1-3" + ruleId = "M10-1-3" and + category = "required" or query = // `Query` instance for the `uniqueAccessibleEntityNamesInMultipleInheritance` query @@ -81,7 +88,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `uniqueAccessibleEntityNamesInMultipleInheritance` query "cpp/autosar/unique-accessible-entity-names-in-multiple-inheritance" and - ruleId = "M10-2-1" + ruleId = "M10-2-1" and + category = "advisory" or query = // `Query` instance for the `dynamicTypeOfThisUsedFromConstructorOrDestructor` query @@ -89,7 +97,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `dynamicTypeOfThisUsedFromConstructorOrDestructor` query "cpp/autosar/dynamic-type-of-this-used-from-constructor-or-destructor" and - ruleId = "M12-1-1" + ruleId = "M12-1-1" and + category = "required" or query = // `Query` instance for the `downcastingShouldNotBePerformedOnPolymorphicTypes` query @@ -97,7 +106,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `downcastingShouldNotBePerformedOnPolymorphicTypes` query "cpp/autosar/downcasting-should-not-be-performed-on-polymorphic-types" and - ruleId = "M5-2-3" + ruleId = "M5-2-3" and + category = "advisory" or query = // `Query` instance for the `doNotInvokeVirtualFunctionsFromConstructorsOrDestructors` query @@ -105,7 +115,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotInvokeVirtualFunctionsFromConstructorsOrDestructors` query "cpp/cert/do-not-invoke-virtual-functions-from-constructors-or-destructors" and - ruleId = "OOP50-CPP" + ruleId = "OOP50-CPP" and + category = "rule" or query = // `Query` instance for the `doNotSliceDerivedObjects` query @@ -113,7 +124,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotSliceDerivedObjects` query "cpp/cert/do-not-slice-derived-objects" and - ruleId = "OOP51-CPP" + ruleId = "OOP51-CPP" and + category = "rule" or query = // `Query` instance for the `doNotDeleteAPolymorphicObjectWithoutAVirtualDestructor` query @@ -121,7 +133,8 @@ predicate isInheritanceQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotDeleteAPolymorphicObjectWithoutAVirtualDestructor` query "cpp/cert/do-not-delete-a-polymorphic-object-without-a-virtual-destructor" and - ruleId = "OOP52-CPP" + ruleId = "OOP52-CPP" and + category = "rule" } module InheritancePackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Initialization.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Initialization.qll index 45ce339b25..6c8d0f5258 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Initialization.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Initialization.qll @@ -26,14 +26,15 @@ newtype InitializationQuery = TBadlySeededRandomNumberGeneratorQuery() or TUseCanonicalOrderForMemberInitQuery() -predicate isInitializationQueryMetadata(Query query, string queryId, string ruleId) { +predicate isInitializationQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `explicitConstructorBaseClassInitialization` query InitializationPackage::explicitConstructorBaseClassInitializationQuery() and queryId = // `@id` for the `explicitConstructorBaseClassInitialization` query "cpp/autosar/explicit-constructor-base-class-initialization" and - ruleId = "A12-1-1" + ruleId = "A12-1-1" and + category = "required" or query = // `Query` instance for the `nonStaticMemberMultipleInit` query @@ -41,7 +42,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `nonStaticMemberMultipleInit` query "cpp/autosar/non-static-member-multiple-init" and - ruleId = "A12-1-2" + ruleId = "A12-1-2" and + category = "required" or query = // `Query` instance for the `missedNSDMIOpportunity` query @@ -49,7 +51,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `missedNSDMIOpportunity` query "cpp/autosar/missed-nsdmi-opportunity" and - ruleId = "A12-1-3" + ruleId = "A12-1-3" and + category = "required" or query = // `Query` instance for the `constructorWithFundamentalArgMissingExplicit` query @@ -57,7 +60,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `constructorWithFundamentalArgMissingExplicit` query "cpp/autosar/constructor-with-fundamental-arg-missing-explicit" and - ruleId = "A12-1-4" + ruleId = "A12-1-4" and + category = "required" or query = // `Query` instance for the `avoidDuplicationInConstructors` query @@ -65,7 +69,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `avoidDuplicationInConstructors` query "cpp/autosar/avoid-duplication-in-constructors" and - ruleId = "A12-1-5" + ruleId = "A12-1-5" and + category = "required" or query = // `Query` instance for the `useInheritingConstructors` query @@ -73,7 +78,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `useInheritingConstructors` query "cpp/autosar/use-inheriting-constructors" and - ruleId = "A12-1-6" + ruleId = "A12-1-6" and + category = "required" or query = // `Query` instance for the `moveConstructorUsesCopySemantics` query @@ -81,7 +87,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `moveConstructorUsesCopySemantics` query "cpp/autosar/move-constructor-uses-copy-semantics" and - ruleId = "A12-8-4" + ruleId = "A12-8-4" and + category = "required" or query = // `Query` instance for the `randomNumberEnginesDefaultInitialized` query @@ -89,7 +96,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `randomNumberEnginesDefaultInitialized` query "cpp/autosar/random-number-engines-default-initialized" and - ruleId = "A26-5-2" + ruleId = "A26-5-2" and + category = "required" or query = // `Query` instance for the `staticOrThreadLocalObjectsNonConstantInit` query @@ -97,7 +105,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `staticOrThreadLocalObjectsNonConstantInit` query "cpp/autosar/static-or-thread-local-objects-non-constant-init" and - ruleId = "A3-3-2" + ruleId = "A3-3-2" and + category = "required" or query = // `Query` instance for the `initializationListOutOfOrder` query @@ -105,7 +114,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `initializationListOutOfOrder` query "cpp/autosar/initialization-list-out-of-order" and - ruleId = "A8-5-1" + ruleId = "A8-5-1" and + category = "required" or query = // `Query` instance for the `useBracedVariableInitialization` query @@ -113,7 +123,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `useBracedVariableInitialization` query "cpp/autosar/use-braced-variable-initialization" and - ruleId = "A8-5-2" + ruleId = "A8-5-2" and + category = "required" or query = // `Query` instance for the `avoidAutoWithBracedInitialization` query @@ -121,7 +132,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `avoidAutoWithBracedInitialization` query "cpp/autosar/avoid-auto-with-braced-initialization" and - ruleId = "A8-5-3" + ruleId = "A8-5-3" and + category = "required" or query = // `Query` instance for the `confusingUseOfInitializerListConstructors` query @@ -129,7 +141,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `confusingUseOfInitializerListConstructors` query "cpp/autosar/confusing-use-of-initializer-list-constructors" and - ruleId = "A8-5-4" + ruleId = "A8-5-4" and + category = "advisory" or query = // `Query` instance for the `multipleLocalDeclarators` query @@ -137,7 +150,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `multipleLocalDeclarators` query "cpp/autosar/multiple-local-declarators" and - ruleId = "M8-0-1" + ruleId = "M8-0-1" and + category = "required" or query = // `Query` instance for the `multipleGlobalOrMemberDeclarators` query @@ -145,7 +159,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `multipleGlobalOrMemberDeclarators` query "cpp/autosar/multiple-global-or-member-declarators" and - ruleId = "M8-0-1" + ruleId = "M8-0-1" and + category = "required" or query = // `Query` instance for the `useInitBracesToMatchTypeStructure` query @@ -153,7 +168,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `useInitBracesToMatchTypeStructure` query "cpp/autosar/use-init-braces-to-match-type-structure" and - ruleId = "M8-5-2" + ruleId = "M8-5-2" and + category = "required" or query = // `Query` instance for the `missingExplicitInitializers` query @@ -161,7 +177,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `missingExplicitInitializers` query "cpp/autosar/missing-explicit-initializers" and - ruleId = "M8-5-2" + ruleId = "M8-5-2" and + category = "required" or query = // `Query` instance for the `nestedZeroValueInitialization` query @@ -169,7 +186,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `nestedZeroValueInitialization` query "cpp/autosar/nested-zero-value-initialization" and - ruleId = "M8-5-2" + ruleId = "M8-5-2" and + category = "required" or query = // `Query` instance for the `cyclesDuringStaticObjectInit` query @@ -177,7 +195,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `cyclesDuringStaticObjectInit` query "cpp/cert/cycles-during-static-object-init" and - ruleId = "DCL56-CPP" + ruleId = "DCL56-CPP" and + category = "rule" or query = // `Query` instance for the `badlySeededRandomNumberGenerator` query @@ -185,7 +204,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `badlySeededRandomNumberGenerator` query "cpp/cert/badly-seeded-random-number-generator" and - ruleId = "MSC51-CPP" + ruleId = "MSC51-CPP" and + category = "rule" or query = // `Query` instance for the `useCanonicalOrderForMemberInit` query @@ -193,7 +213,8 @@ predicate isInitializationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `useCanonicalOrderForMemberInit` query "cpp/cert/use-canonical-order-for-member-init" and - ruleId = "OOP53-CPP" + ruleId = "OOP53-CPP" and + category = "rule" } module InitializationPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/IntegerConversion.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/IntegerConversion.qll index 2387f7f4c4..56b5f68ce4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/IntegerConversion.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/IntegerConversion.qll @@ -15,14 +15,17 @@ newtype IntegerConversionQuery = TExplicitWideningConversionOfACValueExprQuery() or TExplicitSignednessConversionOfCValueQuery() -predicate isIntegerConversionQueryMetadata(Query query, string queryId, string ruleId) { +predicate isIntegerConversionQueryMetadata( + Query query, string queryId, string ruleId, string category +) { query = // `Query` instance for the `integerExpressionLeadToDataLoss` query IntegerConversionPackage::integerExpressionLeadToDataLossQuery() and queryId = // `@id` for the `integerExpressionLeadToDataLoss` query "cpp/autosar/integer-expression-lead-to-data-loss" and - ruleId = "A4-7-1" + ruleId = "A4-7-1" and + category = "required" or query = // `Query` instance for the `intMultToLong` query @@ -30,7 +33,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `intMultToLong` query "cpp/autosar/int-mult-to-long" and - ruleId = "A4-7-1" + ruleId = "A4-7-1" and + category = "required" or query = // `Query` instance for the `implicitChangeOfTheSignednessOfTheUnderlyingType` query @@ -38,7 +42,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `implicitChangeOfTheSignednessOfTheUnderlyingType` query "cpp/autosar/implicit-change-of-the-signedness-of-the-underlying-type" and - ruleId = "M5-0-4" + ruleId = "M5-0-4" and + category = "required" or query = // `Query` instance for the `implicitNonConstFloatingIntegralConversion` query @@ -46,7 +51,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `implicitNonConstFloatingIntegralConversion` query "cpp/autosar/implicit-non-const-floating-integral-conversion" and - ruleId = "M5-0-5" + ruleId = "M5-0-5" and + category = "required" or query = // `Query` instance for the `implicitConstFloatingIntegralConversion` query @@ -54,7 +60,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `implicitConstFloatingIntegralConversion` query "cpp/autosar/implicit-const-floating-integral-conversion" and - ruleId = "M5-0-5" + ruleId = "M5-0-5" and + category = "required" or query = // `Query` instance for the `implicitNonConstConversionToSmallerUnderlyingType` query @@ -62,7 +69,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `implicitNonConstConversionToSmallerUnderlyingType` query "cpp/autosar/implicit-non-const-conversion-to-smaller-underlying-type" and - ruleId = "M5-0-6" + ruleId = "M5-0-6" and + category = "required" or query = // `Query` instance for the `implicitConstConversionToSmallerUnderlyingType` query @@ -70,7 +78,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `implicitConstConversionToSmallerUnderlyingType` query "cpp/autosar/implicit-const-conversion-to-smaller-underlying-type" and - ruleId = "M5-0-6" + ruleId = "M5-0-6" and + category = "required" or query = // `Query` instance for the `explicitFloatingIntegralConversionOfACValueExpr` query @@ -78,7 +87,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `explicitFloatingIntegralConversionOfACValueExpr` query "cpp/autosar/explicit-floating-integral-conversion-of-ac-value-expr" and - ruleId = "M5-0-7" + ruleId = "M5-0-7" and + category = "required" or query = // `Query` instance for the `explicitWideningConversionOfACValueExpr` query @@ -86,7 +96,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `explicitWideningConversionOfACValueExpr` query "cpp/autosar/explicit-widening-conversion-of-ac-value-expr" and - ruleId = "M5-0-8" + ruleId = "M5-0-8" and + category = "required" or query = // `Query` instance for the `explicitSignednessConversionOfCValue` query @@ -94,7 +105,8 @@ predicate isIntegerConversionQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `explicitSignednessConversionOfCValue` query "cpp/autosar/explicit-signedness-conversion-of-c-value" and - ruleId = "M5-0-9" + ruleId = "M5-0-9" and + category = "required" } module IntegerConversionPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Invariants.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Invariants.qll index a084dfcdf8..7633df480a 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Invariants.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Invariants.qll @@ -11,14 +11,15 @@ newtype InvariantsQuery = THonorTerminationReplacementHandlerRequirementsQuery() or THonorNewReplacementHandlerRequirementsQuery() -predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `memoryManagementFunctionInvariants` query InvariantsPackage::memoryManagementFunctionInvariantsQuery() and queryId = // `@id` for the `memoryManagementFunctionInvariants` query "cpp/autosar/memory-management-function-invariants" and - ruleId = "A18-5-5" + ruleId = "A18-5-5" and + category = "required" or query = // `Query` instance for the `orderingPredicatesInvariants` query @@ -26,7 +27,8 @@ predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `orderingPredicatesInvariants` query "cpp/autosar/ordering-predicates-invariants" and - ruleId = "A25-4-1" + ruleId = "A25-4-1" and + category = "required" or query = // `Query` instance for the `provideAValidOrderingPredicate` query @@ -34,7 +36,8 @@ predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `provideAValidOrderingPredicate` query "cpp/cert/provide-a-valid-ordering-predicate" and - ruleId = "CTR57-CPP" + ruleId = "CTR57-CPP" and + category = "rule" or query = // `Query` instance for the `signalHandlerMustBeAPlainOldFunction` query @@ -42,7 +45,8 @@ predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `signalHandlerMustBeAPlainOldFunction` query "cpp/cert/signal-handler-must-be-a-plain-old-function" and - ruleId = "MSC54-CPP" + ruleId = "MSC54-CPP" and + category = "rule" or query = // `Query` instance for the `honorTerminationReplacementHandlerRequirements` query @@ -50,7 +54,8 @@ predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `honorTerminationReplacementHandlerRequirements` query "cpp/cert/honor-termination-replacement-handler-requirements" and - ruleId = "OOP56-CPP" + ruleId = "OOP56-CPP" and + category = "rule" or query = // `Query` instance for the `honorNewReplacementHandlerRequirements` query @@ -58,7 +63,8 @@ predicate isInvariantsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `honorNewReplacementHandlerRequirements` query "cpp/cert/honor-new-replacement-handler-requirements" and - ruleId = "OOP56-CPP" + ruleId = "OOP56-CPP" and + category = "rule" } module InvariantsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Iterators.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Iterators.qll index 619e955fd4..5dac989603 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Iterators.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Iterators.qll @@ -12,14 +12,15 @@ newtype IteratorsQuery = TDoNotUseAnAdditiveOperatorOnAnIteratorQuery() or TUseValidReferencesForElementsOfStringQuery() -predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `iteratorImplicitlyConvertedToConstIterator` query IteratorsPackage::iteratorImplicitlyConvertedToConstIteratorQuery() and queryId = // `@id` for the `iteratorImplicitlyConvertedToConstIterator` query "cpp/autosar/iterator-implicitly-converted-to-const-iterator" and - ruleId = "A23-0-1" + ruleId = "A23-0-1" and + category = "required" or query = // `Query` instance for the `validContainerElementAccess` query @@ -27,7 +28,8 @@ predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `validContainerElementAccess` query "cpp/autosar/valid-container-element-access" and - ruleId = "A23-0-2" + ruleId = "A23-0-2" and + category = "required" or query = // `Query` instance for the `usesValidContainerElementAccess` query @@ -35,7 +37,8 @@ predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `usesValidContainerElementAccess` query "cpp/cert/uses-valid-container-element-access" and - ruleId = "CTR51-CPP" + ruleId = "CTR51-CPP" and + category = "rule" or query = // `Query` instance for the `useValidIteratorRanges` query @@ -43,7 +46,8 @@ predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useValidIteratorRanges` query "cpp/cert/use-valid-iterator-ranges" and - ruleId = "CTR53-CPP" + ruleId = "CTR53-CPP" and + category = "rule" or query = // `Query` instance for the `doNotSubtractIteratorsForDifferentContainers` query @@ -51,7 +55,8 @@ predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotSubtractIteratorsForDifferentContainers` query "cpp/cert/do-not-subtract-iterators-for-different-containers" and - ruleId = "CTR54-CPP" + ruleId = "CTR54-CPP" and + category = "rule" or query = // `Query` instance for the `doNotUseAnAdditiveOperatorOnAnIterator` query @@ -59,7 +64,8 @@ predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotUseAnAdditiveOperatorOnAnIterator` query "cpp/cert/do-not-use-an-additive-operator-on-an-iterator" and - ruleId = "CTR55-CPP" + ruleId = "CTR55-CPP" and + category = "rule" or query = // `Query` instance for the `useValidReferencesForElementsOfString` query @@ -67,7 +73,8 @@ predicate isIteratorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useValidReferencesForElementsOfString` query "cpp/cert/use-valid-references-for-elements-of-string" and - ruleId = "STR52-CPP" + ruleId = "STR52-CPP" and + category = "rule" } module IteratorsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Lambdas.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Lambdas.qll index 7d51a5022d..279b39ba9b 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Lambdas.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Lambdas.qll @@ -16,14 +16,15 @@ newtype LambdasQuery = TReturningLambdaObjectWithCaptureByReferenceQuery() or TEscapingLambdaObjectWithCaptureByReferenceQuery() -predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { +predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `implicitLambdaCapture` query LambdasPackage::implicitLambdaCaptureQuery() and queryId = // `@id` for the `implicitLambdaCapture` query "cpp/autosar/implicit-lambda-capture" and - ruleId = "A5-1-2" + ruleId = "A5-1-2" and + category = "required" or query = // `Query` instance for the `lambdaExpressionWithoutParameterList` query @@ -31,7 +32,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lambdaExpressionWithoutParameterList` query "cpp/autosar/lambda-expression-without-parameter-list" and - ruleId = "A5-1-3" + ruleId = "A5-1-3" and + category = "required" or query = // `Query` instance for the `returnedLambdaObjectOutlivesCaptureByReference` query @@ -39,7 +41,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `returnedLambdaObjectOutlivesCaptureByReference` query "cpp/autosar/returned-lambda-object-outlives-capture-by-reference" and - ruleId = "A5-1-4" + ruleId = "A5-1-4" and + category = "required" or query = // `Query` instance for the `movedLambdaObjectOutlivesCaptureByReference` query @@ -47,7 +50,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `movedLambdaObjectOutlivesCaptureByReference` query "cpp/autosar/moved-lambda-object-outlives-capture-by-reference" and - ruleId = "A5-1-4" + ruleId = "A5-1-4" and + category = "required" or query = // `Query` instance for the `lambdaWithImplicitNonVoidReturnType` query @@ -55,7 +59,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lambdaWithImplicitNonVoidReturnType` query "cpp/autosar/lambda-with-implicit-non-void-return-type" and - ruleId = "A5-1-6" + ruleId = "A5-1-6" and + category = "advisory" or query = // `Query` instance for the `lambdaPassedToDecltype` query @@ -63,7 +68,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lambdaPassedToDecltype` query "cpp/autosar/lambda-passed-to-decltype" and - ruleId = "A5-1-7" + ruleId = "A5-1-7" and + category = "required" or query = // `Query` instance for the `lambdaPassedToTypeid` query @@ -71,7 +77,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lambdaPassedToTypeid` query "cpp/autosar/lambda-passed-to-typeid" and - ruleId = "A5-1-7" + ruleId = "A5-1-7" and + category = "required" or query = // `Query` instance for the `lambdaExpressionInLambdaExpression` query @@ -79,7 +86,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `lambdaExpressionInLambdaExpression` query "cpp/autosar/lambda-expression-in-lambda-expression" and - ruleId = "A5-1-8" + ruleId = "A5-1-8" and + category = "advisory" or query = // `Query` instance for the `identicalLambdaExpressions` query @@ -87,7 +95,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identicalLambdaExpressions` query "cpp/autosar/identical-lambda-expressions" and - ruleId = "A5-1-9" + ruleId = "A5-1-9" and + category = "advisory" or query = // `Query` instance for the `returningLambdaObjectWithCaptureByReference` query @@ -95,7 +104,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `returningLambdaObjectWithCaptureByReference` query "cpp/cert/returning-lambda-object-with-capture-by-reference" and - ruleId = "EXP61-CPP" + ruleId = "EXP61-CPP" and + category = "rule" or query = // `Query` instance for the `escapingLambdaObjectWithCaptureByReference` query @@ -103,7 +113,8 @@ predicate isLambdasQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `escapingLambdaObjectWithCaptureByReference` query "cpp/cert/escaping-lambda-object-with-capture-by-reference" and - ruleId = "EXP61-CPP" + ruleId = "EXP61-CPP" and + category = "rule" } module LambdasPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Literals.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Literals.qll index 1413b0a6fc..16befefd32 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Literals.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Literals.qll @@ -16,14 +16,15 @@ newtype LiteralsQuery = TNullUsedAsIntegerValueQuery() or TLiteralZeroUsedAsNullPointerConstantQuery() -predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `useCorrectIntervalForDigitSequencesSeparators` query LiteralsPackage::useCorrectIntervalForDigitSequencesSeparatorsQuery() and queryId = // `@id` for the `useCorrectIntervalForDigitSequencesSeparators` query "cpp/autosar/use-correct-interval-for-digit-sequences-separators" and - ruleId = "A13-6-1" + ruleId = "A13-6-1" and + category = "required" or query = // `Query` instance for the `escapeSequenceOutsideISO` query @@ -31,7 +32,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `escapeSequenceOutsideISO` query "cpp/autosar/escape-sequence-outside-iso" and - ruleId = "A2-13-1" + ruleId = "A2-13-1" and + category = "required" or query = // `Query` instance for the `hexadecimalConstantsShouldBeUpperCase` query @@ -39,7 +41,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `hexadecimalConstantsShouldBeUpperCase` query "cpp/autosar/hexadecimal-constants-should-be-upper-case" and - ruleId = "A2-13-5" + ruleId = "A2-13-5" and + category = "advisory" or query = // `Query` instance for the `nullPointerConstantNotNullptr` query @@ -47,7 +50,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nullPointerConstantNotNullptr` query "cpp/autosar/null-pointer-constant-not-nullptr" and - ruleId = "A4-10-1" + ruleId = "A4-10-1" and + category = "required" or query = // `Query` instance for the `literalValueUsedOutsideTypeInit` query @@ -55,7 +59,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `literalValueUsedOutsideTypeInit` query "cpp/autosar/literal-value-used-outside-type-init" and - ruleId = "A5-1-1" + ruleId = "A5-1-1" and + category = "required" or query = // `Query` instance for the `useOfNonZeroOctalLiteral` query @@ -63,7 +68,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfNonZeroOctalLiteral` query "cpp/autosar/use-of-non-zero-octal-literal" and - ruleId = "M2-13-2" + ruleId = "M2-13-2" and + category = "required" or query = // `Query` instance for the `useOfNonZeroOctalEscape` query @@ -71,7 +77,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfNonZeroOctalEscape` query "cpp/autosar/use-of-non-zero-octal-escape" and - ruleId = "M2-13-2" + ruleId = "M2-13-2" and + category = "required" or query = // `Query` instance for the `missingUSuffix` query @@ -79,7 +86,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `missingUSuffix` query "cpp/autosar/missing-u-suffix" and - ruleId = "M2-13-3" + ruleId = "M2-13-3" and + category = "required" or query = // `Query` instance for the `literalSuffixNotUpperCase` query @@ -87,7 +95,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `literalSuffixNotUpperCase` query "cpp/autosar/literal-suffix-not-upper-case" and - ruleId = "M2-13-4" + ruleId = "M2-13-4" and + category = "required" or query = // `Query` instance for the `nullUsedAsIntegerValue` query @@ -95,7 +104,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nullUsedAsIntegerValue` query "cpp/autosar/null-used-as-integer-value" and - ruleId = "M4-10-1" + ruleId = "M4-10-1" and + category = "required" or query = // `Query` instance for the `literalZeroUsedAsNullPointerConstant` query @@ -103,7 +113,8 @@ predicate isLiteralsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `literalZeroUsedAsNullPointerConstant` query "cpp/autosar/literal-zero-used-as-null-pointer-constant" and - ruleId = "M4-10-2" + ruleId = "M4-10-2" and + category = "required" } module LiteralsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Loops.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Loops.qll index 65286eed6f..9d9b99989a 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Loops.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Loops.qll @@ -18,14 +18,15 @@ newtype LoopsQuery = TLoopControlVariableModifiedInLoopExpressionQuery() or TNonBooleanLoopControlVariableQuery() -predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `unusedLoopCounterForContainerIteration` query LoopsPackage::unusedLoopCounterForContainerIterationQuery() and queryId = // `@id` for the `unusedLoopCounterForContainerIteration` query "cpp/autosar/unused-loop-counter-for-container-iteration" and - ruleId = "A6-5-1" + ruleId = "A6-5-1" and + category = "required" or query = // `Query` instance for the `multipleLoopCounters` query @@ -33,7 +34,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `multipleLoopCounters` query "cpp/autosar/multiple-loop-counters" and - ruleId = "A6-5-2" + ruleId = "A6-5-2" and + category = "required" or query = // `Query` instance for the `floatingPointLoopCounter` query @@ -41,7 +43,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `floatingPointLoopCounter` query "cpp/autosar/floating-point-loop-counter" and - ruleId = "A6-5-2" + ruleId = "A6-5-2" and + category = "required" or query = // `Query` instance for the `doStatementsShouldNotBeUsed` query @@ -49,7 +52,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doStatementsShouldNotBeUsed` query "cpp/autosar/do-statements-should-not-be-used" and - ruleId = "A6-5-3" + ruleId = "A6-5-3" and + category = "advisory" or query = // `Query` instance for the `forLoopInitializesNonLoopCounter` query @@ -57,7 +61,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `forLoopInitializesNonLoopCounter` query "cpp/autosar/for-loop-initializes-non-loop-counter" and - ruleId = "A6-5-4" + ruleId = "A6-5-4" and + category = "advisory" or query = // `Query` instance for the `forLoopModifiesNonLoopCounter` query @@ -65,7 +70,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `forLoopModifiesNonLoopCounter` query "cpp/autosar/for-loop-modifies-non-loop-counter" and - ruleId = "A6-5-4" + ruleId = "A6-5-4" and + category = "advisory" or query = // `Query` instance for the `notEqualsInLoopCondition` query @@ -73,7 +79,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `notEqualsInLoopCondition` query "cpp/autosar/not-equals-in-loop-condition" and - ruleId = "M6-5-2" + ruleId = "M6-5-2" and + category = "required" or query = // `Query` instance for the `loopCounterModifiedWithinCondition` query @@ -81,7 +88,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `loopCounterModifiedWithinCondition` query "cpp/autosar/loop-counter-modified-within-condition" and - ruleId = "M6-5-3" + ruleId = "M6-5-3" and + category = "required" or query = // `Query` instance for the `loopCounterModifiedWithinStatement` query @@ -89,7 +97,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `loopCounterModifiedWithinStatement` query "cpp/autosar/loop-counter-modified-within-statement" and - ruleId = "M6-5-3" + ruleId = "M6-5-3" and + category = "required" or query = // `Query` instance for the `irregularLoopCounterModification` query @@ -97,7 +106,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `irregularLoopCounterModification` query "cpp/autosar/irregular-loop-counter-modification" and - ruleId = "M6-5-4" + ruleId = "M6-5-4" and + category = "required" or query = // `Query` instance for the `loopControlVariableModifiedInLoopCondition` query @@ -105,7 +115,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `loopControlVariableModifiedInLoopCondition` query "cpp/autosar/loop-control-variable-modified-in-loop-condition" and - ruleId = "M6-5-5" + ruleId = "M6-5-5" and + category = "required" or query = // `Query` instance for the `loopControlVariableModifiedInLoopExpression` query @@ -113,7 +124,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `loopControlVariableModifiedInLoopExpression` query "cpp/autosar/loop-control-variable-modified-in-loop-expression" and - ruleId = "M6-5-5" + ruleId = "M6-5-5" and + category = "required" or query = // `Query` instance for the `nonBooleanLoopControlVariable` query @@ -121,7 +133,8 @@ predicate isLoopsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonBooleanLoopControlVariable` query "cpp/autosar/non-boolean-loop-control-variable" and - ruleId = "M6-5-6" + ruleId = "M6-5-6" and + category = "required" } module LoopsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Macros.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Macros.qll index e9055e8083..0753ffeb48 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Macros.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Macros.qll @@ -17,14 +17,15 @@ newtype MacrosQuery = TMoreThanOneOccurrenceHashOperatorInMacroDefinitionQuery() or THashOperatorsShouldNotBeUsedQuery() -predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { +predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `preProcessorShallOnlyBeUsedForCertainDirectivesPatterns` query MacrosPackage::preProcessorShallOnlyBeUsedForCertainDirectivesPatternsQuery() and queryId = // `@id` for the `preProcessorShallOnlyBeUsedForCertainDirectivesPatterns` query "cpp/autosar/pre-processor-shall-only-be-used-for-certain-directives-patterns" and - ruleId = "A16-0-1" + ruleId = "A16-0-1" and + category = "required" or query = // `Query` instance for the `charactersOccurInHeaderFileNameOrInIncludeDirective` query @@ -32,7 +33,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `charactersOccurInHeaderFileNameOrInIncludeDirective` query "cpp/autosar/characters-occur-in-header-file-name-or-in-include-directive" and - ruleId = "A16-2-1" + ruleId = "A16-2-1" and + category = "required" or query = // `Query` instance for the `errorDirectiveUsed` query @@ -40,7 +42,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `errorDirectiveUsed` query "cpp/autosar/error-directive-used" and - ruleId = "A16-6-1" + ruleId = "A16-6-1" and + category = "required" or query = // `Query` instance for the `pragmaDirectiveUsed` query @@ -48,7 +51,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pragmaDirectiveUsed` query "cpp/autosar/pragma-directive-used" and - ruleId = "A16-7-1" + ruleId = "A16-7-1" and + category = "required" or query = // `Query` instance for the `includeDirectivesNotPrecededByDirectivesOrComments` query @@ -56,7 +60,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `includeDirectivesNotPrecededByDirectivesOrComments` query "cpp/autosar/include-directives-not-preceded-by-directives-or-comments" and - ruleId = "M16-0-1" + ruleId = "M16-0-1" and + category = "required" or query = // `Query` instance for the `macrosShallOnlyBeDefinedOrUndefdInTheGlobalNamespace` query @@ -64,7 +69,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `macrosShallOnlyBeDefinedOrUndefdInTheGlobalNamespace` query "cpp/autosar/macros-shall-only-be-defined-or-undefd-in-the-global-namespace" and - ruleId = "M16-0-2" + ruleId = "M16-0-2" and + category = "required" or query = // `Query` instance for the `functionLikeMacroArgsContainHashToken` query @@ -72,7 +78,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionLikeMacroArgsContainHashToken` query "cpp/autosar/function-like-macro-args-contain-hash-token" and - ruleId = "M16-0-5" + ruleId = "M16-0-5" and + category = "required" or query = // `Query` instance for the `functionLikeMacroParameterNotEnclosedInParentheses` query @@ -80,7 +87,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionLikeMacroParameterNotEnclosedInParentheses` query "cpp/autosar/function-like-macro-parameter-not-enclosed-in-parentheses" and - ruleId = "M16-0-6" + ruleId = "M16-0-6" and + category = "required" or query = // `Query` instance for the `undefinedMacroIdentifiersUsedIn` query @@ -88,7 +96,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `undefinedMacroIdentifiersUsedIn` query "cpp/autosar/undefined-macro-identifiers-used-in" and - ruleId = "M16-0-7" + ruleId = "M16-0-7" and + category = "required" or query = // `Query` instance for the `definedPreProcessorOperatorInOneOfTheTwoStandardForms` query @@ -96,7 +105,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `definedPreProcessorOperatorInOneOfTheTwoStandardForms` query "cpp/autosar/defined-pre-processor-operator-in-one-of-the-two-standard-forms" and - ruleId = "M16-1-1" + ruleId = "M16-1-1" and + category = "required" or query = // `Query` instance for the `moreThanOneOccurrenceHashOperatorInMacroDefinition` query @@ -104,7 +114,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `moreThanOneOccurrenceHashOperatorInMacroDefinition` query "cpp/autosar/more-than-one-occurrence-hash-operator-in-macro-definition" and - ruleId = "M16-3-1" + ruleId = "M16-3-1" and + category = "required" or query = // `Query` instance for the `hashOperatorsShouldNotBeUsed` query @@ -112,7 +123,8 @@ predicate isMacrosQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `hashOperatorsShouldNotBeUsed` query "cpp/autosar/hash-operators-should-not-be-used" and - ruleId = "M16-3-2" + ruleId = "M16-3-2" and + category = "advisory" } module MacrosPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/MoveForward.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/MoveForward.qll index c79931bf58..4d61136610 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/MoveForward.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/MoveForward.qll @@ -12,14 +12,15 @@ newtype MoveForwardQuery = TForwardForwardingReferencesQuery() or TDoNotRelyOnTheValueOfAMovedFromObjectQuery() -predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) { +predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `movedFromObjectReadAccessed` query MoveForwardPackage::movedFromObjectReadAccessedQuery() and queryId = // `@id` for the `movedFromObjectReadAccessed` query "cpp/autosar/moved-from-object-read-accessed" and - ruleId = "A12-8-3" + ruleId = "A12-8-3" and + category = "required" or query = // `Query` instance for the `forwardingValuesToOtherFunctions` query @@ -27,7 +28,8 @@ predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `forwardingValuesToOtherFunctions` query "cpp/autosar/forwarding-values-to-other-functions" and - ruleId = "A18-9-2" + ruleId = "A18-9-2" and + category = "required" or query = // `Query` instance for the `moveUsedOnConstObjects` query @@ -35,7 +37,8 @@ predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `moveUsedOnConstObjects` query "cpp/autosar/move-used-on-const-objects" and - ruleId = "A18-9-3" + ruleId = "A18-9-3" and + category = "required" or query = // `Query` instance for the `argumentToForwardSubsequentlyUsed` query @@ -43,7 +46,8 @@ predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `argumentToForwardSubsequentlyUsed` query "cpp/autosar/argument-to-forward-subsequently-used" and - ruleId = "A18-9-4" + ruleId = "A18-9-4" and + category = "required" or query = // `Query` instance for the `moveFromConsumeParametersRvalRef` query @@ -51,7 +55,8 @@ predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `moveFromConsumeParametersRvalRef` query "cpp/autosar/move-from-consume-parameters-rval-ref" and - ruleId = "A8-4-5" + ruleId = "A8-4-5" and + category = "required" or query = // `Query` instance for the `forwardForwardingReferences` query @@ -59,7 +64,8 @@ predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `forwardForwardingReferences` query "cpp/autosar/forward-forwarding-references" and - ruleId = "A8-4-6" + ruleId = "A8-4-6" and + category = "required" or query = // `Query` instance for the `doNotRelyOnTheValueOfAMovedFromObject` query @@ -67,7 +73,8 @@ predicate isMoveForwardQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotRelyOnTheValueOfAMovedFromObject` query "cpp/cert/do-not-rely-on-the-value-of-a-moved-from-object" and - ruleId = "EXP63-CPP" + ruleId = "EXP63-CPP" and + category = "rule" } module MoveForwardPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Naming.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Naming.qll index 5ab4fc7647..18f03e9c66 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Naming.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Naming.qll @@ -32,14 +32,15 @@ newtype NamingQuery = TFunctionReusesReservedNameQuery() or TEnumeratorReusesReservedNameQuery() -predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { +predicate isNamingQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `userDefinedLiteralOperatorSuffixViolation` query NamingPackage::userDefinedLiteralOperatorSuffixViolationQuery() and queryId = // `@id` for the `userDefinedLiteralOperatorSuffixViolation` query "cpp/autosar/user-defined-literal-operator-suffix-violation" and - ruleId = "A13-1-2" + ruleId = "A13-1-2" and + category = "required" or query = // `Query` instance for the `identifierHiding` query @@ -47,7 +48,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierHiding` query "cpp/autosar/identifier-hiding" and - ruleId = "A2-10-1" + ruleId = "A2-10-1" and + category = "required" or query = // `Query` instance for the `identifierNameOfStaticNonMemberObjectReusedInNamespace` query @@ -55,7 +57,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierNameOfStaticNonMemberObjectReusedInNamespace` query "cpp/autosar/identifier-name-of-static-non-member-object-reused-in-namespace" and - ruleId = "A2-10-4" + ruleId = "A2-10-4" and + category = "required" or query = // `Query` instance for the `identifierNameOfStaticFunctionReusedInNamespace` query @@ -63,7 +66,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierNameOfStaticFunctionReusedInNamespace` query "cpp/autosar/identifier-name-of-static-function-reused-in-namespace" and - ruleId = "A2-10-4" + ruleId = "A2-10-4" and + category = "required" or query = // `Query` instance for the `identifierNameOfAStaticFunctionIsReused` query @@ -71,7 +75,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierNameOfAStaticFunctionIsReused` query "cpp/autosar/identifier-name-of-a-static-function-is-reused" and - ruleId = "A2-10-5" + ruleId = "A2-10-5" and + category = "advisory" or query = // `Query` instance for the `identifierNameOfANonMemberObjectWithExternalOrInternalLinkageIsReused` query @@ -79,7 +84,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierNameOfANonMemberObjectWithExternalOrInternalLinkageIsReused` query "cpp/autosar/identifier-name-of-a-non-member-object-with-external-or-internal-linkage-is-reused" and - ruleId = "A2-10-5" + ruleId = "A2-10-5" and + category = "advisory" or query = // `Query` instance for the `classOrEnumerationNameHiddenByAVariableInTheSameScope` query @@ -87,7 +93,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `classOrEnumerationNameHiddenByAVariableInTheSameScope` query "cpp/autosar/class-or-enumeration-name-hidden-by-a-variable-in-the-same-scope" and - ruleId = "A2-10-6" + ruleId = "A2-10-6" and + category = "required" or query = // `Query` instance for the `classOrEnumerationNameHiddenByAFunctionInTheSameScope` query @@ -95,7 +102,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `classOrEnumerationNameHiddenByAFunctionInTheSameScope` query "cpp/autosar/class-or-enumeration-name-hidden-by-a-function-in-the-same-scope" and - ruleId = "A2-10-6" + ruleId = "A2-10-6" and + category = "required" or query = // `Query` instance for the `classOrEnumerationNameHiddenByAnEnumeratorInTheSameScope` query @@ -103,7 +111,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `classOrEnumerationNameHiddenByAnEnumeratorInTheSameScope` query "cpp/autosar/class-or-enumeration-name-hidden-by-an-enumerator-in-the-same-scope" and - ruleId = "A2-10-6" + ruleId = "A2-10-6" and + category = "required" or query = // `Query` instance for the `universalCharacterNamesUsedOutsideCharacterOrStringLiterals` query @@ -111,7 +120,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `universalCharacterNamesUsedOutsideCharacterOrStringLiterals` query "cpp/autosar/universal-character-names-used-outside-character-or-string-literals" and - ruleId = "A2-13-6" + ruleId = "A2-13-6" and + category = "required" or query = // `Query` instance for the `characterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode` query @@ -119,7 +129,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `characterOutsideTheLanguageStandardBasicSourceCharacterSetUsedInTheSourceCode` query "cpp/autosar/character-outside-the-language-standard-basic-source-character-set-used-in-the-source-code" and - ruleId = "A2-3-1" + ruleId = "A2-3-1" and + category = "required" or query = // `Query` instance for the `invalidCharacterInStringLiteral` query @@ -127,7 +138,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `invalidCharacterInStringLiteral` query "cpp/autosar/invalid-character-in-string-literal" and - ruleId = "A2-3-1" + ruleId = "A2-3-1" and + category = "required" or query = // `Query` instance for the `invalidCharacterInComment` query @@ -135,7 +147,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `invalidCharacterInComment` query "cpp/autosar/invalid-character-in-comment" and - ruleId = "A2-3-1" + ruleId = "A2-3-1" and + category = "required" or query = // `Query` instance for the `nameOfStandardLibraryMacroOrObjectReused` query @@ -143,7 +156,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nameOfStandardLibraryMacroOrObjectReused` query "cpp/autosar/name-of-standard-library-macro-or-object-reused" and - ruleId = "M17-0-2" + ruleId = "M17-0-2" and + category = "required" or query = // `Query` instance for the `nameOfStandardLibraryFunctionIsOverridden` query @@ -151,7 +165,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nameOfStandardLibraryFunctionIsOverridden` query "cpp/autosar/name-of-standard-library-function-is-overridden" and - ruleId = "M17-0-3" + ruleId = "M17-0-3" and + category = "required" or query = // `Query` instance for the `differentIdentifiersNotTypographicallyUnambiguous` query @@ -159,7 +174,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `differentIdentifiersNotTypographicallyUnambiguous` query "cpp/autosar/different-identifiers-not-typographically-unambiguous" and - ruleId = "M2-10-1" + ruleId = "M2-10-1" and + category = "required" or query = // `Query` instance for the `identifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain` query @@ -167,7 +183,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain` query "cpp/autosar/identifier-main-used-for-a-function-other-than-the-global-function-main" and - ruleId = "M7-3-2" + ruleId = "M7-3-2" and + category = "required" or query = // `Query` instance for the `unnamedNamespacesInHeaderFile` query @@ -175,7 +192,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unnamedNamespacesInHeaderFile` query "cpp/autosar/unnamed-namespaces-in-header-file" and - ruleId = "M7-3-3" + ruleId = "M7-3-3" and + category = "required" or query = // `Query` instance for the `nonIdenticalIdentifierUsedForTheParameterInReDeclarationOfAFunction` query @@ -183,7 +201,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonIdenticalIdentifierUsedForTheParameterInReDeclarationOfAFunction` query "cpp/autosar/non-identical-identifier-used-for-the-parameter-in-re-declaration-of-a-function" and - ruleId = "M8-4-2" + ruleId = "M8-4-2" and + category = "required" or query = // `Query` instance for the `redefiningOfStandardLibraryName` query @@ -191,7 +210,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `redefiningOfStandardLibraryName` query "cpp/cert/redefining-of-standard-library-name" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `reuseOfReservedIdentifier` query @@ -199,7 +219,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `reuseOfReservedIdentifier` query "cpp/cert/reuse-of-reserved-identifier" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `useOfSingleUnderscoreReservedPrefix` query @@ -207,7 +228,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfSingleUnderscoreReservedPrefix` query "cpp/cert/use-of-single-underscore-reserved-prefix" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `useOfDoubleUnderscoreReservedPrefix` query @@ -215,7 +237,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfDoubleUnderscoreReservedPrefix` query "cpp/cert/use-of-double-underscore-reserved-prefix" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `useOfReservedLiteralSuffixIdentifier` query @@ -223,7 +246,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfReservedLiteralSuffixIdentifier` query "cpp/cert/use-of-reserved-literal-suffix-identifier" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `objectReusesReservedName` query @@ -231,7 +255,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `objectReusesReservedName` query "cpp/cert/object-reuses-reserved-name" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `functionReusesReservedName` query @@ -239,7 +264,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionReusesReservedName` query "cpp/cert/function-reuses-reserved-name" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" or query = // `Query` instance for the `enumeratorReusesReservedName` query @@ -247,7 +273,8 @@ predicate isNamingQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `enumeratorReusesReservedName` query "cpp/cert/enumerator-reuses-reserved-name" and - ruleId = "DCL51-CPP" + ruleId = "DCL51-CPP" and + category = "rule" } module NamingPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Null.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Null.qll index c0befae569..203bf242a6 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Null.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Null.qll @@ -8,14 +8,15 @@ newtype NullQuery = TParameterNotPassedByReferenceQuery() or TDoNotAttemptToCreateAStringFromANullPointerQuery() -predicate isNullQueryMetadata(Query query, string queryId, string ruleId) { +predicate isNullQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `nullPointersDereferenced` query NullPackage::nullPointersDereferencedQuery() and queryId = // `@id` for the `nullPointersDereferenced` query "cpp/autosar/null-pointers-dereferenced" and - ruleId = "A5-3-2" + ruleId = "A5-3-2" and + category = "required" or query = // `Query` instance for the `parameterNotPassedByReference` query @@ -23,7 +24,8 @@ predicate isNullQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `parameterNotPassedByReference` query "cpp/autosar/parameter-not-passed-by-reference" and - ruleId = "A8-4-10" + ruleId = "A8-4-10" and + category = "required" or query = // `Query` instance for the `doNotAttemptToCreateAStringFromANullPointer` query @@ -31,7 +33,8 @@ predicate isNullQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotAttemptToCreateAStringFromANullPointer` query "cpp/cert/do-not-attempt-to-create-a-string-from-a-null-pointer" and - ruleId = "STR51-CPP" + ruleId = "STR51-CPP" and + category = "rule" } module NullPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/OperatorInvariants.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/OperatorInvariants.qll index 51026ef8b4..186b200ba2 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/OperatorInvariants.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/OperatorInvariants.qll @@ -14,14 +14,17 @@ newtype OperatorInvariantsQuery = TGracefullyHandleSelfCopyAssignmentQuery() or TCopyOperationsMustNotMutateTheSourceObjectQuery() -predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isOperatorInvariantsQueryMetadata( + Query query, string queryId, string ruleId, string category +) { query = // `Query` instance for the `userDefinedCopyAndMoveUseNoThrowSwapFunction` query OperatorInvariantsPackage::userDefinedCopyAndMoveUseNoThrowSwapFunctionQuery() and queryId = // `@id` for the `userDefinedCopyAndMoveUseNoThrowSwapFunction` query "cpp/autosar/user-defined-copy-and-move-use-no-throw-swap-function" and - ruleId = "A12-8-2" + ruleId = "A12-8-2" and + category = "advisory" or query = // `Query` instance for the `copyAssignmentAndAMoveHandleSelfAssignment` query @@ -29,7 +32,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `copyAssignmentAndAMoveHandleSelfAssignment` query "cpp/autosar/copy-assignment-and-a-move-handle-self-assignment" and - ruleId = "A12-8-5" + ruleId = "A12-8-5" and + category = "required" or query = // `Query` instance for the `assignmentOperatorReturnThis` query @@ -37,7 +41,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `assignmentOperatorReturnThis` query "cpp/autosar/assignment-operator-return-this" and - ruleId = "A13-2-1" + ruleId = "A13-2-1" and + category = "required" or query = // `Query` instance for the `binaryOperatorAndBitwiseOperatorReturnAPrvalue` query @@ -45,7 +50,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `binaryOperatorAndBitwiseOperatorReturnAPrvalue` query "cpp/autosar/binary-operator-and-bitwise-operator-return-a-prvalue" and - ruleId = "A13-2-2" + ruleId = "A13-2-2" and + category = "required" or query = // `Query` instance for the `oppositeOperatorsNotDefinedInTermsOfOther` query @@ -53,7 +59,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `oppositeOperatorsNotDefinedInTermsOfOther` query "cpp/autosar/opposite-operators-not-defined-in-terms-of-other" and - ruleId = "A13-5-4" + ruleId = "A13-5-4" and + category = "required" or query = // `Query` instance for the `hashSpecializationsHaveANoexceptFunctionCallOperator` query @@ -61,7 +68,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `hashSpecializationsHaveANoexceptFunctionCallOperator` query "cpp/autosar/hash-specializations-have-a-noexcept-function-call-operator" and - ruleId = "A18-1-6" + ruleId = "A18-1-6" and + category = "required" or query = // `Query` instance for the `semanticEquivalenceBetweenOperatorFormNotPreserved` query @@ -69,7 +77,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `semanticEquivalenceBetweenOperatorFormNotPreserved` query "cpp/autosar/semantic-equivalence-between-operator-form-not-preserved" and - ruleId = "M5-17-1" + ruleId = "M5-17-1" and + category = "required" or query = // `Query` instance for the `gracefullyHandleSelfCopyAssignment` query @@ -77,7 +86,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `gracefullyHandleSelfCopyAssignment` query "cpp/cert/gracefully-handle-self-copy-assignment" and - ruleId = "OOP54-CPP" + ruleId = "OOP54-CPP" and + category = "rule" or query = // `Query` instance for the `copyOperationsMustNotMutateTheSourceObject` query @@ -85,7 +95,8 @@ predicate isOperatorInvariantsQueryMetadata(Query query, string queryId, string queryId = // `@id` for the `copyOperationsMustNotMutateTheSourceObject` query "cpp/cert/copy-operations-must-not-mutate-the-source-object" and - ruleId = "OOP58-CPP" + ruleId = "OOP58-CPP" and + category = "rule" } module OperatorInvariantsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Operators.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Operators.qll index d395d6fd5a..fe71289dbc 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Operators.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Operators.qll @@ -20,14 +20,15 @@ newtype OperatorsQuery = TUnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsignedQuery() or TUnaryOperatorOverloadedQuery() -predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `userDefinedAssignmentOperatorVirtual` query OperatorsPackage::userDefinedAssignmentOperatorVirtualQuery() and queryId = // `@id` for the `userDefinedAssignmentOperatorVirtual` query "cpp/autosar/user-defined-assignment-operator-virtual" and - ruleId = "A10-3-5" + ruleId = "A10-3-5" and + category = "required" or query = // `Query` instance for the `copyAndMoveNotDeclaredProtected` query @@ -35,7 +36,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `copyAndMoveNotDeclaredProtected` query "cpp/autosar/copy-and-move-not-declared-protected" and - ruleId = "A12-8-6" + ruleId = "A12-8-6" and + category = "required" or query = // `Query` instance for the `operatorsShouldBeDeclaredWithTheRefQualifier` query @@ -43,7 +45,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `operatorsShouldBeDeclaredWithTheRefQualifier` query "cpp/autosar/operators-should-be-declared-with-the-ref-qualifier" and - ruleId = "A12-8-7" + ruleId = "A12-8-7" and + category = "advisory" or query = // `Query` instance for the `relationalOperatorShallReturnABooleanValue` query @@ -51,7 +54,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `relationalOperatorShallReturnABooleanValue` query "cpp/autosar/relational-operator-shall-return-a-boolean-value" and - ruleId = "A13-2-3" + ruleId = "A13-2-3" and + category = "required" or query = // `Query` instance for the `functionThatContainsForwardingReferenceAsItsArgumentOverloaded` query @@ -59,7 +63,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `functionThatContainsForwardingReferenceAsItsArgumentOverloaded` query "cpp/autosar/function-that-contains-forwarding-reference-as-its-argument-overloaded" and - ruleId = "A13-3-1" + ruleId = "A13-3-1" and + category = "required" or query = // `Query` instance for the `missingConstOperatorSubscript` query @@ -67,7 +72,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `missingConstOperatorSubscript` query "cpp/autosar/missing-const-operator-subscript" and - ruleId = "A13-5-1" + ruleId = "A13-5-1" and + category = "required" or query = // `Query` instance for the `userDefinedConversionOperatorsNotDefinedExplicit` query @@ -75,7 +81,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `userDefinedConversionOperatorsNotDefinedExplicit` query "cpp/autosar/user-defined-conversion-operators-not-defined-explicit" and - ruleId = "A13-5-2" + ruleId = "A13-5-2" and + category = "required" or query = // `Query` instance for the `userDefinedConversionOperatorsShouldNotBeUsed` query @@ -83,7 +90,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `userDefinedConversionOperatorsShouldNotBeUsed` query "cpp/autosar/user-defined-conversion-operators-should-not-be-used" and - ruleId = "A13-5-3" + ruleId = "A13-5-3" and + category = "advisory" or query = // `Query` instance for the `comparisonOperatorsNotNonMemberFunctionsWithIdenticalParameterTypesAndNoexcept` query @@ -91,7 +99,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `comparisonOperatorsNotNonMemberFunctionsWithIdenticalParameterTypesAndNoexcept` query "cpp/autosar/comparison-operators-not-non-member-functions-with-identical-parameter-types-and-noexcept" and - ruleId = "A13-5-5" + ruleId = "A13-5-5" and + category = "required" or query = // `Query` instance for the `nonMemberGenericOperatorCondition` query @@ -99,7 +108,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nonMemberGenericOperatorCondition` query "cpp/autosar/non-member-generic-operator-condition" and - ruleId = "A14-5-3" + ruleId = "A14-5-3" and + category = "advisory" or query = // `Query` instance for the `unsignedBitwiseOperatorWithoutCast` query @@ -107,7 +117,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unsignedBitwiseOperatorWithoutCast` query "cpp/autosar/unsigned-bitwise-operator-without-cast" and - ruleId = "M5-0-10" + ruleId = "M5-0-10" and + category = "required" or query = // `Query` instance for the `commaOperatorAndOperatorAndTheOperatorOverloaded` query @@ -115,7 +126,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `commaOperatorAndOperatorAndTheOperatorOverloaded` query "cpp/autosar/comma-operator-and-operator-and-the-operator-overloaded" and - ruleId = "M5-2-11" + ruleId = "M5-2-11" and + category = "required" or query = // `Query` instance for the `eachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool` query @@ -123,7 +135,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `eachOperandOfTheOperatorTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool` query "cpp/autosar/each-operand-of-the-operator-the-logical-and-or-the-logical-operators-shall-have-type-bool" and - ruleId = "M5-3-1" + ruleId = "M5-3-1" and + category = "required" or query = // `Query` instance for the `unaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned` query @@ -131,7 +144,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned` query "cpp/autosar/unary-minus-operator-applied-to-an-expression-whose-underlying-type-is-unsigned" and - ruleId = "M5-3-2" + ruleId = "M5-3-2" and + category = "required" or query = // `Query` instance for the `unaryOperatorOverloaded` query @@ -139,7 +153,8 @@ predicate isOperatorsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unaryOperatorOverloaded` query "cpp/autosar/unary-operator-overloaded" and - ruleId = "M5-3-3" + ruleId = "M5-3-3" and + category = "required" } module OperatorsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/OrderOfEvaluation.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/OrderOfEvaluation.qll index 2ac5b428da..9aa62ad377 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/OrderOfEvaluation.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/OrderOfEvaluation.qll @@ -8,17 +8,21 @@ newtype OrderOfEvaluationQuery = TOperandsOfALogicalAndOrNotParenthesizedQuery() or TExplicitConstructionOfUnnamedTemporaryQuery() or TGratuitousUseOfParenthesesQuery() or + TInsufficientUseOfParenthesesQuery() or TIncrementAndDecrementOperatorsMixedWithOtherOperatorsInExpressionQuery() or TAssignmentInSubExpressionQuery() -predicate isOrderOfEvaluationQueryMetadata(Query query, string queryId, string ruleId) { +predicate isOrderOfEvaluationQueryMetadata( + Query query, string queryId, string ruleId, string category +) { query = // `Query` instance for the `expressionShouldNotRelyOnOrderOfEvaluation` query OrderOfEvaluationPackage::expressionShouldNotRelyOnOrderOfEvaluationQuery() and queryId = // `@id` for the `expressionShouldNotRelyOnOrderOfEvaluation` query "cpp/autosar/expression-should-not-rely-on-order-of-evaluation" and - ruleId = "A5-0-1" + ruleId = "A5-0-1" and + category = "required" or query = // `Query` instance for the `operandsOfALogicalAndOrNotParenthesized` query @@ -26,7 +30,8 @@ predicate isOrderOfEvaluationQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `operandsOfALogicalAndOrNotParenthesized` query "cpp/autosar/operands-of-a-logical-and-or-not-parenthesized" and - ruleId = "A5-2-6" + ruleId = "A5-2-6" and + category = "required" or query = // `Query` instance for the `explicitConstructionOfUnnamedTemporary` query @@ -34,7 +39,8 @@ predicate isOrderOfEvaluationQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `explicitConstructionOfUnnamedTemporary` query "cpp/autosar/explicit-construction-of-unnamed-temporary" and - ruleId = "A6-2-2" + ruleId = "A6-2-2" and + category = "required" or query = // `Query` instance for the `gratuitousUseOfParentheses` query @@ -42,7 +48,17 @@ predicate isOrderOfEvaluationQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `gratuitousUseOfParentheses` query "cpp/autosar/gratuitous-use-of-parentheses" and - ruleId = "M5-0-2" + ruleId = "M5-0-2" and + category = "advisory" + or + query = + // `Query` instance for the `insufficientUseOfParentheses` query + OrderOfEvaluationPackage::insufficientUseOfParenthesesQuery() and + queryId = + // `@id` for the `insufficientUseOfParentheses` query + "cpp/autosar/insufficient-use-of-parentheses" and + ruleId = "M5-0-2" and + category = "advisory" or query = // `Query` instance for the `incrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression` query @@ -50,7 +66,8 @@ predicate isOrderOfEvaluationQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `incrementAndDecrementOperatorsMixedWithOtherOperatorsInExpression` query "cpp/autosar/increment-and-decrement-operators-mixed-with-other-operators-in-expression" and - ruleId = "M5-2-10" + ruleId = "M5-2-10" and + category = "required" or query = // `Query` instance for the `assignmentInSubExpression` query @@ -58,7 +75,8 @@ predicate isOrderOfEvaluationQueryMetadata(Query query, string queryId, string r queryId = // `@id` for the `assignmentInSubExpression` query "cpp/autosar/assignment-in-sub-expression" and - ruleId = "M6-2-1" + ruleId = "M6-2-1" and + category = "required" } module OrderOfEvaluationPackage { @@ -90,6 +108,13 @@ module OrderOfEvaluationPackage { TQueryCPP(TOrderOfEvaluationPackageQuery(TGratuitousUseOfParenthesesQuery())) } + Query insufficientUseOfParenthesesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `insufficientUseOfParentheses` query + TQueryCPP(TOrderOfEvaluationPackageQuery(TInsufficientUseOfParenthesesQuery())) + } + Query incrementAndDecrementOperatorsMixedWithOtherOperatorsInExpressionQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/OutOfBounds.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/OutOfBounds.qll index c85fa998e1..d1c3087339 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/OutOfBounds.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/OutOfBounds.qll @@ -9,14 +9,15 @@ newtype OutOfBoundsQuery = TGuaranteeGenericCppLibraryFunctionsDoNotOverflowQuery() or TRangeCheckStringElementAccessQuery() -predicate isOutOfBoundsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isOutOfBoundsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `containerAccessWithoutRangeCheckAutosar` query OutOfBoundsPackage::containerAccessWithoutRangeCheckAutosarQuery() and queryId = // `@id` for the `containerAccessWithoutRangeCheckAutosar` query "cpp/autosar/container-access-without-range-check-autosar" and - ruleId = "A5-2-5" + ruleId = "A5-2-5" and + category = "required" or query = // `Query` instance for the `containerAccessWithoutRangeCheckCert` query @@ -24,7 +25,8 @@ predicate isOutOfBoundsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `containerAccessWithoutRangeCheckCert` query "cpp/cert/container-access-without-range-check-cert" and - ruleId = "CTR50-CPP" + ruleId = "CTR50-CPP" and + category = "rule" or query = // `Query` instance for the `guaranteeGenericCppLibraryFunctionsDoNotOverflow` query @@ -32,7 +34,8 @@ predicate isOutOfBoundsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `guaranteeGenericCppLibraryFunctionsDoNotOverflow` query "cpp/cert/guarantee-generic-cpp-library-functions-do-not-overflow" and - ruleId = "CTR52-CPP" + ruleId = "CTR52-CPP" and + category = "rule" or query = // `Query` instance for the `rangeCheckStringElementAccess` query @@ -40,7 +43,8 @@ predicate isOutOfBoundsQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `rangeCheckStringElementAccess` query "cpp/cert/range-check-string-element-access" and - ruleId = "STR53-CPP" + ruleId = "STR53-CPP" and + category = "rule" } module OutOfBoundsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Pointers.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Pointers.qll index 07e68a5a9c..1dd5bef4c8 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Pointers.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Pointers.qll @@ -28,14 +28,15 @@ newtype PointersQuery = TMemberAccessWithUninitializedStaticPointerToMemberQuery() or TUseOfPointerToMemberToAccessNonexistentMemberQuery() -predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { +predicate isPointersQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `pointerToAnElementOfAnArrayPassedToASmartPointer` query PointersPackage::pointerToAnElementOfAnArrayPassedToASmartPointerQuery() and queryId = // `@id` for the `pointerToAnElementOfAnArrayPassedToASmartPointer` query "cpp/autosar/pointer-to-an-element-of-an-array-passed-to-a-smart-pointer" and - ruleId = "A18-1-4" + ruleId = "A18-1-4" and + category = "required" or query = // `Query` instance for the `declarationContainLessThanTwoLevelsOfIndirection` query @@ -43,7 +44,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `declarationContainLessThanTwoLevelsOfIndirection` query "cpp/autosar/declaration-contain-less-than-two-levels-of-indirection" and - ruleId = "A5-0-3" + ruleId = "A5-0-3" and + category = "required" or query = // `Query` instance for the `pointerArithmeticUsedWithPointersToNonFinalClasses` query @@ -51,7 +53,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerArithmeticUsedWithPointersToNonFinalClasses` query "cpp/autosar/pointer-arithmetic-used-with-pointers-to-non-final-classes" and - ruleId = "A5-0-4" + ruleId = "A5-0-4" and + category = "required" or query = // `Query` instance for the `pointerToMemberVirtualFunctionWithNullPointerConstant` query @@ -59,7 +62,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerToMemberVirtualFunctionWithNullPointerConstant` query "cpp/autosar/pointer-to-member-virtual-function-with-null-pointer-constant" and - ruleId = "A5-10-1" + ruleId = "A5-10-1" and + category = "required" or query = // `Query` instance for the `deletingPointerToIncompleteType` query @@ -67,7 +71,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `deletingPointerToIncompleteType` query "cpp/autosar/deleting-pointer-to-incomplete-type" and - ruleId = "A5-3-3" + ruleId = "A5-3-3" and + category = "required" or query = // `Query` instance for the `pointerToMemberAccessNonExistentClassMembers` query @@ -75,7 +80,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerToMemberAccessNonExistentClassMembers` query "cpp/autosar/pointer-to-member-access-non-existent-class-members" and - ruleId = "A5-5-1" + ruleId = "A5-5-1" and + category = "required" or query = // `Query` instance for the `nullPointerToMemberAccessNonExistentClassMembers` query @@ -83,7 +89,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nullPointerToMemberAccessNonExistentClassMembers` query "cpp/autosar/null-pointer-to-member-access-non-existent-class-members" and - ruleId = "A5-5-1" + ruleId = "A5-5-1" and + category = "required" or query = // `Query` instance for the `uninitializedStaticPointerToMemberUse` query @@ -91,7 +98,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `uninitializedStaticPointerToMemberUse` query "cpp/autosar/uninitialized-static-pointer-to-member-use" and - ruleId = "A5-5-1" + ruleId = "A5-5-1" and + category = "required" or query = // `Query` instance for the `indexingNotTheOnlyFormOfPointerArithmetic` query @@ -99,7 +107,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `indexingNotTheOnlyFormOfPointerArithmetic` query "cpp/autosar/indexing-not-the-only-form-of-pointer-arithmetic" and - ruleId = "M5-0-15" + ruleId = "M5-0-15" and + category = "required" or query = // `Query` instance for the `pointerAndDerivedPointerAccessDifferentArray` query @@ -107,7 +116,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerAndDerivedPointerAccessDifferentArray` query "cpp/autosar/pointer-and-derived-pointer-access-different-array" and - ruleId = "M5-0-16" + ruleId = "M5-0-16" and + category = "required" or query = // `Query` instance for the `pointerSubtractionOnDifferentArrays` query @@ -115,7 +125,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerSubtractionOnDifferentArrays` query "cpp/autosar/pointer-subtraction-on-different-arrays" and - ruleId = "M5-0-17" + ruleId = "M5-0-17" and + category = "required" or query = // `Query` instance for the `appliedToObjectsOfPointerType` query @@ -123,7 +134,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `appliedToObjectsOfPointerType` query "cpp/autosar/applied-to-objects-of-pointer-type" and - ruleId = "M5-0-18" + ruleId = "M5-0-18" and + category = "required" or query = // `Query` instance for the `identifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer` query @@ -131,7 +143,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer` query "cpp/autosar/identifier-with-array-type-passed-as-function-argument-decay-to-a-pointer" and - ruleId = "M5-2-12" + ruleId = "M5-2-12" and + category = "required" or query = // `Query` instance for the `pointerToAVirtualBaseClassCastToAPointer` query @@ -139,7 +152,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `pointerToAVirtualBaseClassCastToAPointer` query "cpp/autosar/pointer-to-a-virtual-base-class-cast-to-a-pointer" and - ruleId = "M5-2-2" + ruleId = "M5-2-2" and + category = "required" or query = // `Query` instance for the `castNotConvertPointerToFunction` query @@ -147,7 +161,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castNotConvertPointerToFunction` query "cpp/autosar/cast-not-convert-pointer-to-function" and - ruleId = "M5-2-6" + ruleId = "M5-2-6" and + category = "required" or query = // `Query` instance for the `integerOrPointerToVoidConvertedToPointerType` query @@ -155,7 +170,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `integerOrPointerToVoidConvertedToPointerType` query "cpp/autosar/integer-or-pointer-to-void-converted-to-pointer-type" and - ruleId = "M5-2-8" + ruleId = "M5-2-8" and + category = "required" or query = // `Query` instance for the `castConvertAPointerTypeToAnIntegralType` query @@ -163,7 +179,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castConvertAPointerTypeToAnIntegralType` query "cpp/autosar/cast-convert-a-pointer-type-to-an-integral-type" and - ruleId = "M5-2-9" + ruleId = "M5-2-9" and + category = "required" or query = // `Query` instance for the `doNotUsePointerArithmeticOnPolymorphicObjects` query @@ -171,7 +188,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotUsePointerArithmeticOnPolymorphicObjects` query "cpp/cert/do-not-use-pointer-arithmetic-on-polymorphic-objects" and - ruleId = "CTR56-CPP" + ruleId = "CTR56-CPP" and + category = "rule" or query = // `Query` instance for the `deletingPointerToIncompleteClass` query @@ -179,7 +197,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `deletingPointerToIncompleteClass` query "cpp/cert/deleting-pointer-to-incomplete-class" and - ruleId = "EXP57-CPP" + ruleId = "EXP57-CPP" and + category = "rule" or query = // `Query` instance for the `castOfPointerToIncompleteClass` query @@ -187,7 +206,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `castOfPointerToIncompleteClass` query "cpp/cert/cast-of-pointer-to-incomplete-class" and - ruleId = "EXP57-CPP" + ruleId = "EXP57-CPP" and + category = "rule" or query = // `Query` instance for the `useOfPointerToMemberToAccessUndefinedMember` query @@ -195,7 +215,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfPointerToMemberToAccessUndefinedMember` query "cpp/cert/use-of-pointer-to-member-to-access-undefined-member" and - ruleId = "OOP55-CPP" + ruleId = "OOP55-CPP" and + category = "rule" or query = // `Query` instance for the `memberAccessWithUninitializedStaticPointerToMember` query @@ -203,7 +224,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `memberAccessWithUninitializedStaticPointerToMember` query "cpp/cert/member-access-with-uninitialized-static-pointer-to-member" and - ruleId = "OOP55-CPP" + ruleId = "OOP55-CPP" and + category = "rule" or query = // `Query` instance for the `useOfPointerToMemberToAccessNonexistentMember` query @@ -211,7 +233,8 @@ predicate isPointersQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `useOfPointerToMemberToAccessNonexistentMember` query "cpp/cert/use-of-pointer-to-member-to-access-nonexistent-member" and - ruleId = "OOP55-CPP" + ruleId = "OOP55-CPP" and + category = "rule" } module PointersPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Representation.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Representation.qll index d9f2850f95..2f92ea89ec 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Representation.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Representation.qll @@ -7,20 +7,22 @@ newtype RepresentationQuery = TBitFieldsShallBeUsedOnlyWhenInterfacingToHardwareOrConformingToCommunicationProtocolsQuery() or TAuditPossibleHardwareInterfaceDueToBitFieldUsageInDataTypeDefinitionQuery() or TObjectAssignedToAnOverlappingObjectQuery() or + TDoNotPassAliasedPointerToParamQuery() or TUnderlyingBitRepresentationsOfFloatingPointValuesUsedQuery() or TNamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBitQuery() or TMemsetUsedToAccessObjectRepresentationQuery() or TMemcmpUsedToAccessObjectRepresentationQuery() or TMemcpyUsedToAccessObjectRepresentationQuery() -predicate isRepresentationQueryMetadata(Query query, string queryId, string ruleId) { +predicate isRepresentationQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `bitFieldsShallBeUsedOnlyWhenInterfacingToHardwareOrConformingToCommunicationProtocols` query RepresentationPackage::bitFieldsShallBeUsedOnlyWhenInterfacingToHardwareOrConformingToCommunicationProtocolsQuery() and queryId = // `@id` for the `bitFieldsShallBeUsedOnlyWhenInterfacingToHardwareOrConformingToCommunicationProtocols` query "cpp/autosar/bit-fields-shall-be-used-only-when-interfacing-to-hardware-or-conforming-to-communication-protocols" and - ruleId = "A9-6-2" + ruleId = "A9-6-2" and + category = "required" or query = // `Query` instance for the `auditPossibleHardwareInterfaceDueToBitFieldUsageInDataTypeDefinition` query @@ -28,7 +30,8 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `auditPossibleHardwareInterfaceDueToBitFieldUsageInDataTypeDefinition` query "cpp/autosar/audit-possible-hardware-interface-due-to-bit-field-usage-in-data-type-definition" and - ruleId = "A9-6-2" + ruleId = "A9-6-2" and + category = "required" or query = // `Query` instance for the `objectAssignedToAnOverlappingObject` query @@ -36,7 +39,17 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `objectAssignedToAnOverlappingObject` query "cpp/autosar/object-assigned-to-an-overlapping-object" and - ruleId = "M0-2-1" + ruleId = "M0-2-1" and + category = "required" + or + query = + // `Query` instance for the `doNotPassAliasedPointerToParam` query + RepresentationPackage::doNotPassAliasedPointerToParamQuery() and + queryId = + // `@id` for the `doNotPassAliasedPointerToParam` query + "cpp/autosar/do-not-pass-aliased-pointer-to-param" and + ruleId = "M0-2-1" and + category = "required" or query = // `Query` instance for the `underlyingBitRepresentationsOfFloatingPointValuesUsed` query @@ -44,7 +57,8 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `underlyingBitRepresentationsOfFloatingPointValuesUsed` query "cpp/autosar/underlying-bit-representations-of-floating-point-values-used" and - ruleId = "M3-9-3" + ruleId = "M3-9-3" and + category = "required" or query = // `Query` instance for the `namedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit` query @@ -52,7 +66,8 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `namedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit` query "cpp/autosar/named-bit-fields-with-signed-integer-type-shall-have-a-length-of-more-than-one-bit" and - ruleId = "M9-6-4" + ruleId = "M9-6-4" and + category = "required" or query = // `Query` instance for the `memsetUsedToAccessObjectRepresentation` query @@ -60,7 +75,8 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `memsetUsedToAccessObjectRepresentation` query "cpp/cert/memset-used-to-access-object-representation" and - ruleId = "EXP62-CPP" + ruleId = "EXP62-CPP" and + category = "rule" or query = // `Query` instance for the `memcmpUsedToAccessObjectRepresentation` query @@ -68,7 +84,8 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `memcmpUsedToAccessObjectRepresentation` query "cpp/cert/memcmp-used-to-access-object-representation" and - ruleId = "EXP62-CPP" + ruleId = "EXP62-CPP" and + category = "rule" or query = // `Query` instance for the `memcpyUsedToAccessObjectRepresentation` query @@ -76,7 +93,8 @@ predicate isRepresentationQueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `memcpyUsedToAccessObjectRepresentation` query "cpp/cert/memcpy-used-to-access-object-representation" and - ruleId = "EXP62-CPP" + ruleId = "EXP62-CPP" and + category = "rule" } module RepresentationPackage { @@ -101,6 +119,13 @@ module RepresentationPackage { TQueryCPP(TRepresentationPackageQuery(TObjectAssignedToAnOverlappingObjectQuery())) } + Query doNotPassAliasedPointerToParamQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotPassAliasedPointerToParam` query + TQueryCPP(TRepresentationPackageQuery(TDoNotPassAliasedPointerToParamQuery())) + } + Query underlyingBitRepresentationsOfFloatingPointValuesUsedQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 89c804c4ca..abd6aeff96 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -18,9 +18,11 @@ import ExceptionSafety import Exceptions1 import Exceptions2 import Expressions +import FloatingPoint import Freed import Functions import IO +import ImportMisra23 import Includes import Inheritance import Initialization @@ -71,9 +73,11 @@ newtype TCPPQuery = TExceptions1PackageQuery(Exceptions1Query q) or TExceptions2PackageQuery(Exceptions2Query q) or TExpressionsPackageQuery(ExpressionsQuery q) or + TFloatingPointPackageQuery(FloatingPointQuery q) or TFreedPackageQuery(FreedQuery q) or TFunctionsPackageQuery(FunctionsQuery q) or TIOPackageQuery(IOQuery q) or + TImportMisra23PackageQuery(ImportMisra23Query q) or TIncludesPackageQuery(IncludesQuery q) or TInheritancePackageQuery(InheritanceQuery q) or TInitializationPackageQuery(InitializationQuery q) or @@ -107,55 +111,57 @@ newtype TCPPQuery = TVirtualFunctionsPackageQuery(VirtualFunctionsQuery q) /** The metadata predicate * */ -predicate isQueryMetadata(Query query, string queryId, string ruleId) { - isAllocationsQueryMetadata(query, queryId, ruleId) or - isBannedFunctionsQueryMetadata(query, queryId, ruleId) or - isBannedLibrariesQueryMetadata(query, queryId, ruleId) or - isBannedSyntaxQueryMetadata(query, queryId, ruleId) or - isBannedTypesQueryMetadata(query, queryId, ruleId) or - isClassesQueryMetadata(query, queryId, ruleId) or - isCommentsQueryMetadata(query, queryId, ruleId) or - isConcurrencyQueryMetadata(query, queryId, ruleId) or - isConditionalsQueryMetadata(query, queryId, ruleId) or - isConstQueryMetadata(query, queryId, ruleId) or - isDeadCodeQueryMetadata(query, queryId, ruleId) or - isDeclarationsQueryMetadata(query, queryId, ruleId) or - isExceptionSafetyQueryMetadata(query, queryId, ruleId) or - isExceptions1QueryMetadata(query, queryId, ruleId) or - isExceptions2QueryMetadata(query, queryId, ruleId) or - isExpressionsQueryMetadata(query, queryId, ruleId) or - isFreedQueryMetadata(query, queryId, ruleId) or - isFunctionsQueryMetadata(query, queryId, ruleId) or - isIOQueryMetadata(query, queryId, ruleId) or - isIncludesQueryMetadata(query, queryId, ruleId) or - isInheritanceQueryMetadata(query, queryId, ruleId) or - isInitializationQueryMetadata(query, queryId, ruleId) or - isIntegerConversionQueryMetadata(query, queryId, ruleId) or - isInvariantsQueryMetadata(query, queryId, ruleId) or - isIteratorsQueryMetadata(query, queryId, ruleId) or - isLambdasQueryMetadata(query, queryId, ruleId) or - isLiteralsQueryMetadata(query, queryId, ruleId) or - isLoopsQueryMetadata(query, queryId, ruleId) or - isMacrosQueryMetadata(query, queryId, ruleId) or - isMoveForwardQueryMetadata(query, queryId, ruleId) or - isNamingQueryMetadata(query, queryId, ruleId) or - isNullQueryMetadata(query, queryId, ruleId) or - isOperatorInvariantsQueryMetadata(query, queryId, ruleId) or - isOperatorsQueryMetadata(query, queryId, ruleId) or - isOrderOfEvaluationQueryMetadata(query, queryId, ruleId) or - isOutOfBoundsQueryMetadata(query, queryId, ruleId) or - isPointersQueryMetadata(query, queryId, ruleId) or - isRepresentationQueryMetadata(query, queryId, ruleId) or - isScopeQueryMetadata(query, queryId, ruleId) or - isSideEffects1QueryMetadata(query, queryId, ruleId) or - isSideEffects2QueryMetadata(query, queryId, ruleId) or - isSmartPointers1QueryMetadata(query, queryId, ruleId) or - isSmartPointers2QueryMetadata(query, queryId, ruleId) or - isStringsQueryMetadata(query, queryId, ruleId) or - isTemplatesQueryMetadata(query, queryId, ruleId) or - isToolchainQueryMetadata(query, queryId, ruleId) or - isTrustBoundariesQueryMetadata(query, queryId, ruleId) or - isTypeRangesQueryMetadata(query, queryId, ruleId) or - isUninitializedQueryMetadata(query, queryId, ruleId) or - isVirtualFunctionsQueryMetadata(query, queryId, ruleId) +predicate isQueryMetadata(Query query, string queryId, string ruleId, string category) { + isAllocationsQueryMetadata(query, queryId, ruleId, category) or + isBannedFunctionsQueryMetadata(query, queryId, ruleId, category) or + isBannedLibrariesQueryMetadata(query, queryId, ruleId, category) or + isBannedSyntaxQueryMetadata(query, queryId, ruleId, category) or + isBannedTypesQueryMetadata(query, queryId, ruleId, category) or + isClassesQueryMetadata(query, queryId, ruleId, category) or + isCommentsQueryMetadata(query, queryId, ruleId, category) or + isConcurrencyQueryMetadata(query, queryId, ruleId, category) or + isConditionalsQueryMetadata(query, queryId, ruleId, category) or + isConstQueryMetadata(query, queryId, ruleId, category) or + isDeadCodeQueryMetadata(query, queryId, ruleId, category) or + isDeclarationsQueryMetadata(query, queryId, ruleId, category) or + isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or + isExceptions1QueryMetadata(query, queryId, ruleId, category) or + isExceptions2QueryMetadata(query, queryId, ruleId, category) or + isExpressionsQueryMetadata(query, queryId, ruleId, category) or + isFloatingPointQueryMetadata(query, queryId, ruleId, category) or + isFreedQueryMetadata(query, queryId, ruleId, category) or + isFunctionsQueryMetadata(query, queryId, ruleId, category) or + isIOQueryMetadata(query, queryId, ruleId, category) or + isImportMisra23QueryMetadata(query, queryId, ruleId, category) or + isIncludesQueryMetadata(query, queryId, ruleId, category) or + isInheritanceQueryMetadata(query, queryId, ruleId, category) or + isInitializationQueryMetadata(query, queryId, ruleId, category) or + isIntegerConversionQueryMetadata(query, queryId, ruleId, category) or + isInvariantsQueryMetadata(query, queryId, ruleId, category) or + isIteratorsQueryMetadata(query, queryId, ruleId, category) or + isLambdasQueryMetadata(query, queryId, ruleId, category) or + isLiteralsQueryMetadata(query, queryId, ruleId, category) or + isLoopsQueryMetadata(query, queryId, ruleId, category) or + isMacrosQueryMetadata(query, queryId, ruleId, category) or + isMoveForwardQueryMetadata(query, queryId, ruleId, category) or + isNamingQueryMetadata(query, queryId, ruleId, category) or + isNullQueryMetadata(query, queryId, ruleId, category) or + isOperatorInvariantsQueryMetadata(query, queryId, ruleId, category) or + isOperatorsQueryMetadata(query, queryId, ruleId, category) or + isOrderOfEvaluationQueryMetadata(query, queryId, ruleId, category) or + isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or + isPointersQueryMetadata(query, queryId, ruleId, category) or + isRepresentationQueryMetadata(query, queryId, ruleId, category) or + isScopeQueryMetadata(query, queryId, ruleId, category) or + isSideEffects1QueryMetadata(query, queryId, ruleId, category) or + isSideEffects2QueryMetadata(query, queryId, ruleId, category) or + isSmartPointers1QueryMetadata(query, queryId, ruleId, category) or + isSmartPointers2QueryMetadata(query, queryId, ruleId, category) or + isStringsQueryMetadata(query, queryId, ruleId, category) or + isTemplatesQueryMetadata(query, queryId, ruleId, category) or + isToolchainQueryMetadata(query, queryId, ruleId, category) or + isTrustBoundariesQueryMetadata(query, queryId, ruleId, category) or + isTypeRangesQueryMetadata(query, queryId, ruleId, category) or + isUninitializedQueryMetadata(query, queryId, ruleId, category) or + isVirtualFunctionsQueryMetadata(query, queryId, ruleId, category) } diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Scope.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Scope.qll index 0157347d3b..c4a21040eb 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Scope.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Scope.qll @@ -22,14 +22,15 @@ newtype ScopeQuery = TUnnamedNamespaceInHeaderFileQuery() or TOneDefinitionRuleNotObeyedQuery() -predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { +predicate isScopeQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `nonStandardEntitiesInStandardNamespaces` query ScopePackage::nonStandardEntitiesInStandardNamespacesQuery() and queryId = // `@id` for the `nonStandardEntitiesInStandardNamespaces` query "cpp/autosar/non-standard-entities-in-standard-namespaces" and - ruleId = "A17-6-1" + ruleId = "A17-6-1" and + category = "required" or query = // `Query` instance for the `externalLinkageArrayWithoutExplicitSize` query @@ -37,7 +38,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `externalLinkageArrayWithoutExplicitSize` query "cpp/autosar/external-linkage-array-without-explicit-size" and - ruleId = "A3-1-4" + ruleId = "A3-1-4" and + category = "required" or query = // `Query` instance for the `hiddenInheritedNonOverridableMemberFunction` query @@ -45,7 +47,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `hiddenInheritedNonOverridableMemberFunction` query "cpp/autosar/hidden-inherited-non-overridable-member-function" and - ruleId = "A7-3-1" + ruleId = "A7-3-1" and + category = "required" or query = // `Query` instance for the `hiddenInheritedOverridableMemberFunction` query @@ -53,7 +56,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `hiddenInheritedOverridableMemberFunction` query "cpp/autosar/hidden-inherited-overridable-member-function" and - ruleId = "A7-3-1" + ruleId = "A7-3-1" and + category = "required" or query = // `Query` instance for the `definitionNotConsideredForUnqualifiedLookup` query @@ -61,7 +65,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `definitionNotConsideredForUnqualifiedLookup` query "cpp/autosar/definition-not-considered-for-unqualified-lookup" and - ruleId = "A7-3-1" + ruleId = "A7-3-1" and + category = "required" or query = // `Query` instance for the `oneDefinitionRuleViolation` query @@ -69,7 +74,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `oneDefinitionRuleViolation` query "cpp/autosar/one-definition-rule-violation" and - ruleId = "M3-2-2" + ruleId = "M3-2-2" and + category = "required" or query = // `Query` instance for the `multipleDeclarationViolation` query @@ -77,7 +83,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `multipleDeclarationViolation` query "cpp/autosar/multiple-declaration-violation" and - ruleId = "M3-2-3" + ruleId = "M3-2-3" and + category = "required" or query = // `Query` instance for the `identifierWithExternalLinkageShallHaveOneDefinition` query @@ -85,7 +92,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `identifierWithExternalLinkageShallHaveOneDefinition` query "cpp/autosar/identifier-with-external-linkage-shall-have-one-definition" and - ruleId = "M3-2-4" + ruleId = "M3-2-4" and + category = "required" or query = // `Query` instance for the `missingStaticSpecifierOnFunctionRedeclaration` query @@ -93,7 +101,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `missingStaticSpecifierOnFunctionRedeclaration` query "cpp/autosar/missing-static-specifier-on-function-redeclaration" and - ruleId = "M3-3-2" + ruleId = "M3-3-2" and + category = "required" or query = // `Query` instance for the `unnecessaryExposedIdentifierDeclaration` query @@ -101,7 +110,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unnecessaryExposedIdentifierDeclaration` query "cpp/autosar/unnecessary-exposed-identifier-declaration" and - ruleId = "M3-4-1" + ruleId = "M3-4-1" and + category = "required" or query = // `Query` instance for the `globalNamespaceMembershipViolation` query @@ -109,7 +119,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `globalNamespaceMembershipViolation` query "cpp/autosar/global-namespace-membership-violation" and - ruleId = "M7-3-1" + ruleId = "M7-3-1" and + category = "required" or query = // `Query` instance for the `localFunctionDeclaration` query @@ -117,7 +128,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `localFunctionDeclaration` query "cpp/cert/local-function-declaration" and - ruleId = "DCL53-CPP" + ruleId = "DCL53-CPP" and + category = "rule" or query = // `Query` instance for the `localConstructorInitializedObjectHidesIdentifier` query @@ -125,7 +137,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `localConstructorInitializedObjectHidesIdentifier` query "cpp/cert/local-constructor-initialized-object-hides-identifier" and - ruleId = "DCL53-CPP" + ruleId = "DCL53-CPP" and + category = "rule" or query = // `Query` instance for the `singularOverloadOfMemoryFunction` query @@ -133,7 +146,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `singularOverloadOfMemoryFunction` query "cpp/cert/singular-overload-of-memory-function" and - ruleId = "DCL54-CPP" + ruleId = "DCL54-CPP" and + category = "rule" or query = // `Query` instance for the `modificationOfTheStandardNamespaces` query @@ -141,7 +155,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `modificationOfTheStandardNamespaces` query "cpp/cert/modification-of-the-standard-namespaces" and - ruleId = "DCL58-CPP" + ruleId = "DCL58-CPP" and + category = "rule" or query = // `Query` instance for the `unnamedNamespaceInHeaderFile` query @@ -149,7 +164,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `unnamedNamespaceInHeaderFile` query "cpp/cert/unnamed-namespace-in-header-file" and - ruleId = "DCL59-CPP" + ruleId = "DCL59-CPP" and + category = "rule" or query = // `Query` instance for the `oneDefinitionRuleNotObeyed` query @@ -157,7 +173,8 @@ predicate isScopeQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `oneDefinitionRuleNotObeyed` query "cpp/cert/one-definition-rule-not-obeyed" and - ruleId = "DCL60-CPP" + ruleId = "DCL60-CPP" and + category = "rule" } module ScopePackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects1.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects1.qll index 03a0ea4f70..37f323a6cb 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects1.qll @@ -15,14 +15,15 @@ newtype SideEffects1Query = TDoNotRelyOnSideEffectsInDeclTypeOperandQuery() or TDoNotRelyOnSideEffectsInDeclValExpressionQuery() -predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `evaluationOfTheOperandToTheTypeidOperatorContainSideEffects` query SideEffects1Package::evaluationOfTheOperandToTheTypeidOperatorContainSideEffectsQuery() and queryId = // `@id` for the `evaluationOfTheOperandToTheTypeidOperatorContainSideEffects` query "cpp/autosar/evaluation-of-the-operand-to-the-typeid-operator-contain-side-effects" and - ruleId = "A5-3-1" + ruleId = "A5-3-1" and + category = "required" or query = // `Query` instance for the `rightHandOperandOfALogicalAndOperatorsContainSideEffects` query @@ -30,7 +31,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `rightHandOperandOfALogicalAndOperatorsContainSideEffects` query "cpp/autosar/right-hand-operand-of-a-logical-and-operators-contain-side-effects" and - ruleId = "M5-14-1" + ruleId = "M5-14-1" and + category = "required" or query = // `Query` instance for the `evaluationOfTheOperandToTheSizeofOperatorContainSideEffects` query @@ -38,7 +40,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `evaluationOfTheOperandToTheSizeofOperatorContainSideEffects` query "cpp/autosar/evaluation-of-the-operand-to-the-sizeof-operator-contain-side-effects" and - ruleId = "M5-3-4" + ruleId = "M5-3-4" and + category = "required" or query = // `Query` instance for the `doNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects` query @@ -46,7 +49,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects` query "cpp/cert/do-not-depend-on-the-order-of-scalar-object-evaluation-for-side-effects" and - ruleId = "EXP50-CPP" + ruleId = "EXP50-CPP" and + category = "rule" or query = // `Query` instance for the `doNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments` query @@ -54,7 +58,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments` query "cpp/cert/do-not-depend-on-the-order-of-evaluation-for-side-effects-in-function-calls-as-function-arguments" and - ruleId = "EXP50-CPP" + ruleId = "EXP50-CPP" and + category = "rule" or query = // `Query` instance for the `doNotRelyOnSideEffectsInSizeOfOperand` query @@ -62,7 +67,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotRelyOnSideEffectsInSizeOfOperand` query "cpp/cert/do-not-rely-on-side-effects-in-size-of-operand" and - ruleId = "EXP52-CPP" + ruleId = "EXP52-CPP" and + category = "rule" or query = // `Query` instance for the `doNotRelyOnSideEffectsInTypeIdOperand` query @@ -70,7 +76,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotRelyOnSideEffectsInTypeIdOperand` query "cpp/cert/do-not-rely-on-side-effects-in-type-id-operand" and - ruleId = "EXP52-CPP" + ruleId = "EXP52-CPP" and + category = "rule" or query = // `Query` instance for the `doNotRelyOnSideEffectsInNoExceptOperand` query @@ -78,7 +85,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotRelyOnSideEffectsInNoExceptOperand` query "cpp/cert/do-not-rely-on-side-effects-in-no-except-operand" and - ruleId = "EXP52-CPP" + ruleId = "EXP52-CPP" and + category = "rule" or query = // `Query` instance for the `doNotRelyOnSideEffectsInDeclTypeOperand` query @@ -86,7 +94,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotRelyOnSideEffectsInDeclTypeOperand` query "cpp/cert/do-not-rely-on-side-effects-in-decl-type-operand" and - ruleId = "EXP52-CPP" + ruleId = "EXP52-CPP" and + category = "rule" or query = // `Query` instance for the `doNotRelyOnSideEffectsInDeclValExpression` query @@ -94,7 +103,8 @@ predicate isSideEffects1QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `doNotRelyOnSideEffectsInDeclValExpression` query "cpp/cert/do-not-rely-on-side-effects-in-decl-val-expression" and - ruleId = "EXP52-CPP" + ruleId = "EXP52-CPP" and + category = "rule" } module SideEffects1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects2.qll index ba930c214c..7ba094b13e 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SideEffects2.qll @@ -14,14 +14,15 @@ newtype SideEffects2Query = TFunctionsWithVoidReturnTypeShallHaveExternalSideEffectsQuery() or TPredicateFunctionObjectsShouldNotBeMutableQuery() -predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `moveConstructorShallOnlyMoveObject` query SideEffects2Package::moveConstructorShallOnlyMoveObjectQuery() and queryId = // `@id` for the `moveConstructorShallOnlyMoveObject` query "cpp/autosar/move-constructor-shall-only-move-object" and - ruleId = "A12-8-1" + ruleId = "A12-8-1" and + category = "required" or query = // `Query` instance for the `copyConstructorShallOnlyCopyObject` query @@ -29,7 +30,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `copyConstructorShallOnlyCopyObject` query "cpp/autosar/copy-constructor-shall-only-copy-object" and - ruleId = "A12-8-1" + ruleId = "A12-8-1" and + category = "required" or query = // `Query` instance for the `userDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters` query @@ -37,7 +39,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `userDefinedLiteralsOperatorsShallOnlyPerformConversionOfPassedParameters` query "cpp/autosar/user-defined-literals-operators-shall-only-perform-conversion-of-passed-parameters" and - ruleId = "A13-1-3" + ruleId = "A13-1-3" and + category = "required" or query = // `Query` instance for the `userDefinedLiteralsOperatorsShallNotHaveSideEffects` query @@ -45,7 +48,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `userDefinedLiteralsOperatorsShallNotHaveSideEffects` query "cpp/autosar/user-defined-literals-operators-shall-not-have-side-effects" and - ruleId = "A13-1-3" + ruleId = "A13-1-3" and + category = "required" or query = // `Query` instance for the `stateRelatedToFunctionObjectIdentityShallNotBeCopied` query @@ -53,7 +57,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `stateRelatedToFunctionObjectIdentityShallNotBeCopied` query "cpp/autosar/state-related-to-function-object-identity-shall-not-be-copied" and - ruleId = "A25-1-1" + ruleId = "A25-1-1" and + category = "required" or query = // `Query` instance for the `moveOperatorShallOnlyMoveObject` query @@ -61,7 +66,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `moveOperatorShallOnlyMoveObject` query "cpp/autosar/move-operator-shall-only-move-object" and - ruleId = "A6-2-1" + ruleId = "A6-2-1" and + category = "required" or query = // `Query` instance for the `copyOperatorShallOnlyCopyObject` query @@ -69,7 +75,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `copyOperatorShallOnlyCopyObject` query "cpp/autosar/copy-operator-shall-only-copy-object" and - ruleId = "A6-2-1" + ruleId = "A6-2-1" and + category = "required" or query = // `Query` instance for the `functionsWithVoidReturnTypeShallHaveExternalSideEffects` query @@ -77,7 +84,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `functionsWithVoidReturnTypeShallHaveExternalSideEffects` query "cpp/autosar/functions-with-void-return-type-shall-have-external-side-effects" and - ruleId = "M0-1-8" + ruleId = "M0-1-8" and + category = "required" or query = // `Query` instance for the `predicateFunctionObjectsShouldNotBeMutable` query @@ -85,7 +93,8 @@ predicate isSideEffects2QueryMetadata(Query query, string queryId, string ruleId queryId = // `@id` for the `predicateFunctionObjectsShouldNotBeMutable` query "cpp/cert/predicate-function-objects-should-not-be-mutable" and - ruleId = "CTR58-CPP" + ruleId = "CTR58-CPP" and + category = "rule" } module SideEffects2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers1.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers1.qll index 7750c2396b..5b11807014 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers1.qll @@ -14,14 +14,15 @@ newtype SmartPointers1Query = TUniquePtrPassedToFunctionWithImproperSemanticsQuery() or TSharedPtrPassedToFunctionWithImproperSemanticsQuery() -predicate isSmartPointers1QueryMetadata(Query query, string queryId, string ruleId) { +predicate isSmartPointers1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `ownedPointerValueStoredInUnrelatedSmartPointerAsar` query SmartPointers1Package::ownedPointerValueStoredInUnrelatedSmartPointerAsarQuery() and queryId = // `@id` for the `ownedPointerValueStoredInUnrelatedSmartPointerAsar` query "cpp/autosar/owned-pointer-value-stored-in-unrelated-smart-pointer-asar" and - ruleId = "A20-8-1" + ruleId = "A20-8-1" and + category = "required" or query = // `Query` instance for the `uniquePtrNotUsedToRepresentExclusiveOwnership` query @@ -29,7 +30,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `uniquePtrNotUsedToRepresentExclusiveOwnership` query "cpp/autosar/unique-ptr-not-used-to-represent-exclusive-ownership" and - ruleId = "A20-8-2" + ruleId = "A20-8-2" and + category = "required" or query = // `Query` instance for the `sharedPtrNotUsedToRepresentSharedOwnership` query @@ -37,7 +39,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `sharedPtrNotUsedToRepresentSharedOwnership` query "cpp/autosar/shared-ptr-not-used-to-represent-shared-ownership" and - ruleId = "A20-8-3" + ruleId = "A20-8-3" and + category = "required" or query = // `Query` instance for the `sharedPointerUsedWithNoOwnershipSharing` query @@ -45,7 +48,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `sharedPointerUsedWithNoOwnershipSharing` query "cpp/autosar/shared-pointer-used-with-no-ownership-sharing" and - ruleId = "A20-8-4" + ruleId = "A20-8-4" and + category = "required" or query = // `Query` instance for the `makeUniqueNotUsedToConstructObjectOwnedByUniquePtr` query @@ -53,7 +57,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `makeUniqueNotUsedToConstructObjectOwnedByUniquePtr` query "cpp/autosar/make-unique-not-used-to-construct-object-owned-by-unique-ptr" and - ruleId = "A20-8-5" + ruleId = "A20-8-5" and + category = "required" or query = // `Query` instance for the `makeSharedNotUsedToConstructObjectOwnedBySharedPtr` query @@ -61,7 +66,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `makeSharedNotUsedToConstructObjectOwnedBySharedPtr` query "cpp/autosar/make-shared-not-used-to-construct-object-owned-by-shared-ptr" and - ruleId = "A20-8-6" + ruleId = "A20-8-6" and + category = "required" or query = // `Query` instance for the `smartPointerAsParameterWithoutLifetimeSemantics` query @@ -69,7 +75,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `smartPointerAsParameterWithoutLifetimeSemantics` query "cpp/autosar/smart-pointer-as-parameter-without-lifetime-semantics" and - ruleId = "A8-4-11" + ruleId = "A8-4-11" and + category = "required" or query = // `Query` instance for the `uniquePtrPassedToFunctionWithImproperSemantics` query @@ -77,7 +84,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `uniquePtrPassedToFunctionWithImproperSemantics` query "cpp/autosar/unique-ptr-passed-to-function-with-improper-semantics" and - ruleId = "A8-4-12" + ruleId = "A8-4-12" and + category = "required" or query = // `Query` instance for the `sharedPtrPassedToFunctionWithImproperSemantics` query @@ -85,7 +93,8 @@ predicate isSmartPointers1QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `sharedPtrPassedToFunctionWithImproperSemantics` query "cpp/autosar/shared-ptr-passed-to-function-with-improper-semantics" and - ruleId = "A8-4-13" + ruleId = "A8-4-13" and + category = "required" } module SmartPointers1Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers2.qll index a64b418f2d..aa0755a024 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/SmartPointers2.qll @@ -7,14 +7,15 @@ newtype SmartPointers2Query = TWeakPtrNotUsedToRepresentTemporarySharedOwnershipQuery() or TOwnedPointerValueStoredInUnrelatedSmartPointerCertQuery() -predicate isSmartPointers2QueryMetadata(Query query, string queryId, string ruleId) { +predicate isSmartPointers2QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `weakPtrNotUsedToRepresentTemporarySharedOwnership` query SmartPointers2Package::weakPtrNotUsedToRepresentTemporarySharedOwnershipQuery() and queryId = // `@id` for the `weakPtrNotUsedToRepresentTemporarySharedOwnership` query "cpp/autosar/weak-ptr-not-used-to-represent-temporary-shared-ownership" and - ruleId = "A20-8-7" + ruleId = "A20-8-7" and + category = "required" or query = // `Query` instance for the `ownedPointerValueStoredInUnrelatedSmartPointerCert` query @@ -22,7 +23,8 @@ predicate isSmartPointers2QueryMetadata(Query query, string queryId, string rule queryId = // `@id` for the `ownedPointerValueStoredInUnrelatedSmartPointerCert` query "cpp/cert/owned-pointer-value-stored-in-unrelated-smart-pointer-cert" and - ruleId = "MEM56-CPP" + ruleId = "MEM56-CPP" and + category = "rule" } module SmartPointers2Package { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Strings.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Strings.qll index 457bac01b7..e40e1e7d7f 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Strings.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Strings.qll @@ -13,14 +13,15 @@ newtype StringsQuery = TBasicStringMayNotBeNullTerminatedCertQuery() or TOperationMayNotNullTerminateCStyleStringCertQuery() -predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isStringsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `stringLiteralsAssignedToNonConstantPointers` query StringsPackage::stringLiteralsAssignedToNonConstantPointersQuery() and queryId = // `@id` for the `stringLiteralsAssignedToNonConstantPointers` query "cpp/autosar/string-literals-assigned-to-non-constant-pointers" and - ruleId = "A2-13-4" + ruleId = "A2-13-4" and + category = "required" or query = // `Query` instance for the `basicStringMayNotBeNullTerminatedAutosar` query @@ -28,7 +29,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `basicStringMayNotBeNullTerminatedAutosar` query "cpp/autosar/basic-string-may-not-be-null-terminated-autosar" and - ruleId = "A27-0-2" + ruleId = "A27-0-2" and + category = "advisory" or query = // `Query` instance for the `operationMayNotNullTerminateCStyleStringAutosar` query @@ -36,7 +38,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `operationMayNotNullTerminateCStyleStringAutosar` query "cpp/autosar/operation-may-not-null-terminate-c-style-string-autosar" and - ruleId = "A27-0-2" + ruleId = "A27-0-2" and + category = "advisory" or query = // `Query` instance for the `cStyleStringsUsed` query @@ -44,7 +47,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `cStyleStringsUsed` query "cpp/autosar/c-style-strings-used" and - ruleId = "A27-0-4" + ruleId = "A27-0-4" and + category = "required" or query = // `Query` instance for the `plainCharTypeShallOnlyBeUsedForTheStorageAndUseOfCharacterValues` query @@ -52,7 +56,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `plainCharTypeShallOnlyBeUsedForTheStorageAndUseOfCharacterValues` query "cpp/autosar/plain-char-type-shall-only-be-used-for-the-storage-and-use-of-character-values" and - ruleId = "M5-0-11" + ruleId = "M5-0-11" and + category = "required" or query = // `Query` instance for the `signedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues` query @@ -60,7 +65,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `signedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues` query "cpp/autosar/signed-char-and-unsigned-char-type-shall-only-be-used-for-the-storage-and-use-of-numeric-values" and - ruleId = "M5-0-12" + ruleId = "M5-0-12" and + category = "required" or query = // `Query` instance for the `basicStringMayNotBeNullTerminatedCert` query @@ -68,7 +74,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `basicStringMayNotBeNullTerminatedCert` query "cpp/cert/basic-string-may-not-be-null-terminated-cert" and - ruleId = "STR50-CPP" + ruleId = "STR50-CPP" and + category = "rule" or query = // `Query` instance for the `operationMayNotNullTerminateCStyleStringCert` query @@ -76,7 +83,8 @@ predicate isStringsQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `operationMayNotNullTerminateCStyleStringCert` query "cpp/cert/operation-may-not-null-terminate-c-style-string-cert" and - ruleId = "STR50-CPP" + ruleId = "STR50-CPP" and + category = "rule" } module StringsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Templates.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Templates.qll index 5ad07851ab..d5eeb959a4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Templates.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Templates.qll @@ -13,14 +13,15 @@ newtype TemplatesQuery = TNameNotReferredUsingAQualifiedIdOrThisQuery() or TNameNotReferredUsingAQualifiedIdOrThisAuditQuery() -predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `templateShouldCheckArg` query TemplatesPackage::templateShouldCheckArgQuery() and queryId = // `@id` for the `templateShouldCheckArg` query "cpp/autosar/template-should-check-arg" and - ruleId = "A14-1-1" + ruleId = "A14-1-1" and + category = "advisory" or query = // `Query` instance for the `templateConstructorOverloadResolution` query @@ -28,7 +29,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `templateConstructorOverloadResolution` query "cpp/autosar/template-constructor-overload-resolution" and - ruleId = "A14-5-1" + ruleId = "A14-5-1" and + category = "required" or query = // `Query` instance for the `typeUsedAsTemplateArgShallProvideAllMembers` query @@ -36,7 +38,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `typeUsedAsTemplateArgShallProvideAllMembers` query "cpp/autosar/type-used-as-template-arg-shall-provide-all-members" and - ruleId = "A14-7-1" + ruleId = "A14-7-1" and + category = "required" or query = // `Query` instance for the `templateSpecializationNotDeclaredInTheSameFile` query @@ -44,7 +47,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `templateSpecializationNotDeclaredInTheSameFile` query "cpp/autosar/template-specialization-not-declared-in-the-same-file" and - ruleId = "A14-7-2" + ruleId = "A14-7-2" and + category = "required" or query = // `Query` instance for the `explicitSpecializationsOfFunctionTemplatesUsed` query @@ -52,7 +56,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `explicitSpecializationsOfFunctionTemplatesUsed` query "cpp/autosar/explicit-specializations-of-function-templates-used" and - ruleId = "A14-8-2" + ruleId = "A14-8-2" and + category = "required" or query = // `Query` instance for the `copyAssignmentOperatorNotDeclared` query @@ -60,7 +65,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `copyAssignmentOperatorNotDeclared` query "cpp/autosar/copy-assignment-operator-not-declared" and - ruleId = "M14-5-3" + ruleId = "M14-5-3" and + category = "required" or query = // `Query` instance for the `nameNotReferredUsingAQualifiedIdOrThis` query @@ -68,7 +74,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nameNotReferredUsingAQualifiedIdOrThis` query "cpp/autosar/name-not-referred-using-a-qualified-id-or-this" and - ruleId = "M14-6-1" + ruleId = "M14-6-1" and + category = "required" or query = // `Query` instance for the `nameNotReferredUsingAQualifiedIdOrThisAudit` query @@ -76,7 +83,8 @@ predicate isTemplatesQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `nameNotReferredUsingAQualifiedIdOrThisAudit` query "cpp/autosar/name-not-referred-using-a-qualified-id-or-this-audit" and - ruleId = "M14-6-1" + ruleId = "M14-6-1" and + category = "required" } module TemplatesPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Toolchain.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Toolchain.qll index 969405c785..7dcf9f523a 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Toolchain.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Toolchain.qll @@ -16,14 +16,15 @@ newtype ToolchainQuery = TCompilerWarningLevelNotInComplianceQuery() or TUncompliantOptimizationOptionMustBeDisabledInCompilerQuery() -predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { +predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `floatingPointImplementationShallComplyWithIeeeStandard` query ToolchainPackage::floatingPointImplementationShallComplyWithIeeeStandardQuery() and queryId = // `@id` for the `floatingPointImplementationShallComplyWithIeeeStandard` query "cpp/autosar/floating-point-implementation-shall-comply-with-ieee-standard" and - ruleId = "A0-4-1" + ruleId = "A0-4-1" and + category = "required" or query = // `Query` instance for the `compilerImplementationShallComplyWithCPP14Standard` query @@ -31,7 +32,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `compilerImplementationShallComplyWithCPP14Standard` query "cpp/autosar/compiler-implementation-shall-comply-with-cpp14standard" and - ruleId = "A0-4-3" + ruleId = "A0-4-3" and + category = "required" or query = // `Query` instance for the `incrementOperatorWithBoolOperandIsDeprecated` query @@ -39,7 +41,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `incrementOperatorWithBoolOperandIsDeprecated` query "cpp/autosar/increment-operator-with-bool-operand-is-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `registerKeywordIsDeprecated` query @@ -47,7 +50,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `registerKeywordIsDeprecated` query "cpp/autosar/register-keyword-is-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `implicitCopyConstructorIsDeprecated` query @@ -55,7 +59,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `implicitCopyConstructorIsDeprecated` query "cpp/autosar/implicit-copy-constructor-is-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `implicitCopyAssignmentOperatorIsDeprecated` query @@ -63,7 +68,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `implicitCopyAssignmentOperatorIsDeprecated` query "cpp/autosar/implicit-copy-assignment-operator-is-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `dynamicExceptionsAreDeprecated` query @@ -71,7 +77,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `dynamicExceptionsAreDeprecated` query "cpp/autosar/dynamic-exceptions-are-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `cStandardLibraryHeadersAreDeprecated` query @@ -79,7 +86,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `cStandardLibraryHeadersAreDeprecated` query "cpp/autosar/c-standard-library-headers-are-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `strstreamTypesAreDeprecated` query @@ -87,7 +95,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `strstreamTypesAreDeprecated` query "cpp/autosar/strstream-types-are-deprecated" and - ruleId = "A1-1-1" + ruleId = "A1-1-1" and + category = "required" or query = // `Query` instance for the `compilerWarningLevelNotInCompliance` query @@ -95,7 +104,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `compilerWarningLevelNotInCompliance` query "cpp/autosar/compiler-warning-level-not-in-compliance" and - ruleId = "A1-1-2" + ruleId = "A1-1-2" and + category = "required" or query = // `Query` instance for the `uncompliantOptimizationOptionMustBeDisabledInCompiler` query @@ -103,7 +113,8 @@ predicate isToolchainQueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `uncompliantOptimizationOptionMustBeDisabledInCompiler` query "cpp/autosar/uncompliant-optimization-option-must-be-disabled-in-compiler" and - ruleId = "A1-1-3" + ruleId = "A1-1-3" and + category = "required" } module ToolchainPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/TrustBoundaries.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/TrustBoundaries.qll index b6135deba8..38014aea5f 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/TrustBoundaries.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/TrustBoundaries.qll @@ -8,14 +8,15 @@ newtype TrustBoundariesQuery = TDoNotThrowAnExceptionAcrossExecutionBoundariesQuery() or TDoNotPassANonstandardObjectAcrossBoundariesQuery() -predicate isTrustBoundariesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isTrustBoundariesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `exceptionsThrownAcrossExecutionBoundaries` query TrustBoundariesPackage::exceptionsThrownAcrossExecutionBoundariesQuery() and queryId = // `@id` for the `exceptionsThrownAcrossExecutionBoundaries` query "cpp/autosar/exceptions-thrown-across-execution-boundaries" and - ruleId = "A15-1-5" + ruleId = "A15-1-5" and + category = "required" or query = // `Query` instance for the `doNotThrowAnExceptionAcrossExecutionBoundaries` query @@ -23,7 +24,8 @@ predicate isTrustBoundariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `doNotThrowAnExceptionAcrossExecutionBoundaries` query "cpp/cert/do-not-throw-an-exception-across-execution-boundaries" and - ruleId = "ERR59-CPP" + ruleId = "ERR59-CPP" and + category = "rule" or query = // `Query` instance for the `doNotPassANonstandardObjectAcrossBoundaries` query @@ -31,7 +33,8 @@ predicate isTrustBoundariesQueryMetadata(Query query, string queryId, string rul queryId = // `@id` for the `doNotPassANonstandardObjectAcrossBoundaries` query "cpp/cert/do-not-pass-a-nonstandard-object-across-boundaries" and - ruleId = "EXP60-CPP" + ruleId = "EXP60-CPP" and + category = "rule" } module TrustBoundariesPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/TypeRanges.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/TypeRanges.qll index 5104a03793..4dce9bbfe7 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/TypeRanges.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/TypeRanges.qll @@ -15,14 +15,15 @@ newtype TypeRangesQuery = TDetectErrorsWhenConvertingAStringToANumberQuery() or TDoNotCastToAnOutOfRangeEnumerationValueQuery() -predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) { +predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `uncheckedRangeDomainPoleErrors` query TypeRangesPackage::uncheckedRangeDomainPoleErrorsQuery() and queryId = // `@id` for the `uncheckedRangeDomainPoleErrors` query "cpp/autosar/unchecked-range-domain-pole-errors" and - ruleId = "A0-4-4" + ruleId = "A0-4-4" and + category = "required" or query = // `Query` instance for the `stringNumberConversionMissingErrorCheck` query @@ -30,7 +31,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `stringNumberConversionMissingErrorCheck` query "cpp/autosar/string-number-conversion-missing-error-check" and - ruleId = "A18-0-2" + ruleId = "A18-0-2" and + category = "required" or query = // `Query` instance for the `useOfUnsafeCStringToNumberConversion` query @@ -38,7 +40,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `useOfUnsafeCStringToNumberConversion` query "cpp/autosar/use-of-unsafe-c-string-to-number-conversion" and - ruleId = "A18-0-2" + ruleId = "A18-0-2" and + category = "required" or query = // `Query` instance for the `signedValPassedToChar` query @@ -46,7 +49,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `signedValPassedToChar` query "cpp/autosar/signed-val-passed-to-char" and - ruleId = "A21-8-1" + ruleId = "A21-8-1" and + category = "required" or query = // `Query` instance for the `inputsFromIndependentComponentsNotValidated` query @@ -54,7 +58,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `inputsFromIndependentComponentsNotValidated` query "cpp/autosar/inputs-from-independent-components-not-validated" and - ruleId = "A27-0-1" + ruleId = "A27-0-1" and + category = "required" or query = // `Query` instance for the `nonEnumeratorEnumValue` query @@ -62,7 +67,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `nonEnumeratorEnumValue` query "cpp/autosar/non-enumerator-enum-value" and - ruleId = "A7-2-1" + ruleId = "A7-2-1" and + category = "required" or query = // `Query` instance for the `useOfEnumForRelatedConstants` query @@ -70,7 +76,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `useOfEnumForRelatedConstants` query "cpp/autosar/use-of-enum-for-related-constants" and - ruleId = "A7-2-5" + ruleId = "A7-2-5" and + category = "advisory" or query = // `Query` instance for the `integerUsedForEnum` query @@ -78,7 +85,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `integerUsedForEnum` query "cpp/autosar/integer-used-for-enum" and - ruleId = "A7-2-5" + ruleId = "A7-2-5" and + category = "advisory" or query = // `Query` instance for the `detectErrorsWhenConvertingAStringToANumber` query @@ -86,7 +94,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `detectErrorsWhenConvertingAStringToANumber` query "cpp/cert/detect-errors-when-converting-a-string-to-a-number" and - ruleId = "ERR62-CPP" + ruleId = "ERR62-CPP" and + category = "rule" or query = // `Query` instance for the `doNotCastToAnOutOfRangeEnumerationValue` query @@ -94,7 +103,8 @@ predicate isTypeRangesQueryMetadata(Query query, string queryId, string ruleId) queryId = // `@id` for the `doNotCastToAnOutOfRangeEnumerationValue` query "cpp/cert/do-not-cast-to-an-out-of-range-enumeration-value" and - ruleId = "INT50-CPP" + ruleId = "INT50-CPP" and + category = "rule" } module TypeRangesPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Uninitialized.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Uninitialized.qll index ce5e0ad2a0..e5eddf1b04 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Uninitialized.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Uninitialized.qll @@ -8,14 +8,15 @@ newtype UninitializedQuery = TInformationLeakageAcrossTrustBoundariesQuery() or TDoNotReadUninitializedMemoryQuery() -predicate isUninitializedQueryMetadata(Query query, string queryId, string ruleId) { +predicate isUninitializedQueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `memoryNotInitializedBeforeItIsRead` query UninitializedPackage::memoryNotInitializedBeforeItIsReadQuery() and queryId = // `@id` for the `memoryNotInitializedBeforeItIsRead` query "cpp/autosar/memory-not-initialized-before-it-is-read" and - ruleId = "A8-5-0" + ruleId = "A8-5-0" and + category = "required" or query = // `Query` instance for the `informationLeakageAcrossTrustBoundaries` query @@ -23,7 +24,8 @@ predicate isUninitializedQueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `informationLeakageAcrossTrustBoundaries` query "cpp/cert/information-leakage-across-trust-boundaries" and - ruleId = "DCL55-CPP" + ruleId = "DCL55-CPP" and + category = "rule" or query = // `Query` instance for the `doNotReadUninitializedMemory` query @@ -31,7 +33,8 @@ predicate isUninitializedQueryMetadata(Query query, string queryId, string ruleI queryId = // `@id` for the `doNotReadUninitializedMemory` query "cpp/cert/do-not-read-uninitialized-memory" and - ruleId = "EXP53-CPP" + ruleId = "EXP53-CPP" and + category = "rule" } module UninitializedPackage { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/VirtualFunctions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/VirtualFunctions.qll index a55c48d4dd..e2c73fc33d 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/VirtualFunctions.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/VirtualFunctions.qll @@ -13,14 +13,17 @@ newtype VirtualFunctionsQuery = TVirtualFunctionOverriddenByAPureVirtualFunctionQuery() or TVirtualFunctionParametersUseTheSameDefaultArgumentsQuery() -predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ruleId) { +predicate isVirtualFunctionsQueryMetadata( + Query query, string queryId, string ruleId, string category +) { query = // `Query` instance for the `nonVirtualPublicOrProtectedFunctionsRedefined` query VirtualFunctionsPackage::nonVirtualPublicOrProtectedFunctionsRedefinedQuery() and queryId = // `@id` for the `nonVirtualPublicOrProtectedFunctionsRedefined` query "cpp/autosar/non-virtual-public-or-protected-functions-redefined" and - ruleId = "A10-2-1" + ruleId = "A10-2-1" and + category = "required" or query = // `Query` instance for the `virtualFunctionsShallContainOneSpecifier` query @@ -28,7 +31,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `virtualFunctionsShallContainOneSpecifier` query "cpp/autosar/virtual-functions-shall-contain-one-specifier" and - ruleId = "A10-3-1" + ruleId = "A10-3-1" and + category = "required" or query = // `Query` instance for the `overridingFunctionNotDeclaredOverrideOrFinal` query @@ -36,7 +40,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `overridingFunctionNotDeclaredOverrideOrFinal` query "cpp/autosar/overriding-function-not-declared-override-or-final" and - ruleId = "A10-3-2" + ruleId = "A10-3-2" and + category = "required" or query = // `Query` instance for the `virtualFunctionsIntroducedInFinalClass` query @@ -44,7 +49,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `virtualFunctionsIntroducedInFinalClass` query "cpp/autosar/virtual-functions-introduced-in-final-class" and - ruleId = "A10-3-3" + ruleId = "A10-3-3" and + category = "required" or query = // `Query` instance for the `destructorOfABaseClassNotPublicVirtual` query @@ -52,7 +58,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `destructorOfABaseClassNotPublicVirtual` query "cpp/autosar/destructor-of-a-base-class-not-public-virtual" and - ruleId = "A12-4-1" + ruleId = "A12-4-1" and + category = "required" or query = // `Query` instance for the `nonVirtualPublicDestructorInNonFinalClass` query @@ -60,7 +67,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `nonVirtualPublicDestructorInNonFinalClass` query "cpp/autosar/non-virtual-public-destructor-in-non-final-class" and - ruleId = "A12-4-2" + ruleId = "A12-4-2" and + category = "advisory" or query = // `Query` instance for the `virtualFunctionOverriddenByAPureVirtualFunction` query @@ -68,7 +76,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `virtualFunctionOverriddenByAPureVirtualFunction` query "cpp/autosar/virtual-function-overridden-by-a-pure-virtual-function" and - ruleId = "M10-3-3" + ruleId = "M10-3-3" and + category = "required" or query = // `Query` instance for the `virtualFunctionParametersUseTheSameDefaultArguments` query @@ -76,7 +85,8 @@ predicate isVirtualFunctionsQueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `virtualFunctionParametersUseTheSameDefaultArguments` query "cpp/autosar/virtual-function-parameters-use-the-same-default-arguments" and - ruleId = "M8-3-1" + ruleId = "M8-3-1" and + category = "required" } module VirtualFunctionsPackage { diff --git a/cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll b/cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll new file mode 100644 index 0000000000..5c46fce075 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll @@ -0,0 +1,59 @@ +import cpp +import codingstandards.cpp.types.FunctionType + +/** + * A class representing an expression that has a function pointer type. This can be a function + * access, a variable access, or any expression that has a function pointer type. + */ +abstract class FunctionExpr extends Expr { + /** Any element that could represent the function, for example, a variable or an expression. */ + abstract Element getFunction(); + + /** A name or string that describes the function. */ + abstract string describe(); + + /** Get calls of this function */ + abstract Call getACall(); +} + +/** + * A function access is an an expression of function type where we know the function. + */ +class SimpleFunctionAccess extends FunctionExpr, FunctionAccess { + override Element getFunction() { result = this.getTarget() } + + override string describe() { result = "Address of function " + this.getTarget().getName() } + + override FunctionCall getACall() { result.getTarget() = this.getTarget() } +} + +/** + * An access of a variable that has a function pointer type is also a function expression, for which + * we can track certain properties of the function. + */ +class FunctionVariableAccess extends FunctionExpr, VariableAccess { + FunctionVariableAccess() { this.getUnderlyingType() instanceof FunctionType } + + override Element getFunction() { result = this.getTarget() } + + override string describe() { result = "Function pointer variable " + this.getTarget().getName() } + + override ExprCall getACall() { result.getExpr().(VariableAccess).getTarget() = this.getTarget() } +} + +/** + * A function typed expression that is not a function access or a variable access. + */ +class FunctionTypedExpr extends FunctionExpr { + FunctionTypedExpr() { + this.getUnderlyingType() instanceof FunctionType and + not this instanceof FunctionAccess and + not this instanceof VariableAccess + } + + override Element getFunction() { result = this } + + override string describe() { result = "Expression with function pointer type" } + + override ExprCall getACall() { result.getExpr() = this } +} diff --git a/cpp/common/src/codingstandards/cpp/exprs/Guards.qll b/cpp/common/src/codingstandards/cpp/exprs/Guards.qll new file mode 100644 index 0000000000..73a35ccc6b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exprs/Guards.qll @@ -0,0 +1,34 @@ +import cpp +import codeql.util.Boolean +import semmle.code.cpp.controlflow.Guards +import codingstandards.cpp.exprs.FunctionExprs + +/** + * A guard of the form: `if (funcPtr) funcPtr();`, e.g., a null check on a function before calling + * that function. + * + * Note this does not consider the above to be a null function call guard if `funcPtr` is a + * function name, as that could only be null via unusual linkage steps, and is not expected to be + * an intentional null check. + */ +class NullFunctionCallGuard extends GuardCondition { + FunctionExpr expr; + + NullFunctionCallGuard() { + exists(BasicBlock block, Call guardedCall | + ( + // Handles 'if (funcPtr != NULL)`: + this.ensuresEq(expr, 0, block, false) + or + // Handles `if (funcPtr)` in C where no implicit conversion to bool exists: + expr = this and + expr.getFunction() instanceof Variable and + this.controls(block, true) + ) and + guardedCall = expr.getACall() and + block.contains(guardedCall) + ) + } + + FunctionExpr getFunctionExpr() { result = expr } +} diff --git a/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll new file mode 100644 index 0000000000..ec5731f1bf --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll @@ -0,0 +1,79 @@ +/** + * A module for identifying guideline recategorizations specified in a `conding-standards.yml` file. + */ + +import cpp +import semmle.code.cpp.XML +import codingstandards.cpp.exclusions.RuleMetadata +import codingstandards.cpp.Config + +/** A container of guideline recategorizations. */ +class GuidelineRecategorizations extends CodingStandardsConfigSection { + GuidelineRecategorizations() { hasName("guideline-recategorizations") } +} + +class GuidelineRecategorization extends XmlElement { + GuidelineRecategorization() { + getParent() instanceof GuidelineRecategorizations and + hasName("guideline-recategorizations-entry") + } + + string getRuleId() { result = getAChild("rule-id").getTextValue() } + + string getCategory() { result = getAChild("category").getTextValue() } + + /** Get a query for which a recategorization is specified. */ + Query getQuery() { result.getRuleId() = getRuleId() } + + private EffectiveCategory getValidEffectiveCategory() { + exists(string category, string recategorization | + category = getQuery().getCategory() and + recategorization = getCategory() + | + result = TMandatory() and + category = ["advisory", "required"] and + recategorization = "mandatory" + or + result = TRequired() and + category = "advisory" and + recategorization = "required" + or + result = TDisapplied() and + category = "advisory" and + recategorization = "disapplied" + ) + } + + private predicate isValidRecategorization(string category, string recategorization) { + category = ["advisory", "required"] and + recategorization = "mandatory" + or + category = "advisory" and + recategorization = "required" + or + category = "advisory" and + recategorization = "disapplied" + } + + string getAnInvalidReason() { + not isValidRecategorization(this.getQuery().getCategory(), this.getCategory()) and + if exists(this.getQuery()) + then + result = + "Invalid recategorization from '" + this.getQuery().getCategory() + "' to '" + + this.getCategory() + "'." + else result = "Unknown rule id '" + this.getRuleId() + "'." + } + + predicate isValid() { not isInvalid() } + + predicate isInvalid() { getEffectiveCategory() = TInvalid(_) } + + EffectiveCategory getEffectiveCategory() { + ( + if exists(getValidEffectiveCategory()) + then result = getValidEffectiveCategory() + else result = TInvalid(getAnInvalidReason()) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/guideline_recategorizations/InvalidGuidelineRecategorizations.ql b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/InvalidGuidelineRecategorizations.ql new file mode 100644 index 0000000000..6df3f3cf57 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/InvalidGuidelineRecategorizations.ql @@ -0,0 +1,13 @@ +/** + * @id cpp/coding-standards/invalid-guideline-recategorizations + * @name Invalid guideline recategorizations + * @description Guideline recategorizations marked as invalid will not be applied. + */ + +import cpp +import GuidelineRecategorizations + +from GuidelineRecategorization gr +select gr, + gr.getFile().getRelativePath() + ": '" + gr.getAnInvalidReason() + "' for rule " + gr.getRuleId() + + "." diff --git a/cpp/common/src/codingstandards/cpp/guideline_recategorizations/ListGuidelineRecategorizations.ql b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/ListGuidelineRecategorizations.ql new file mode 100644 index 0000000000..a51c1b5993 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/ListGuidelineRecategorizations.ql @@ -0,0 +1,12 @@ +/** + * @id cpp/coding-standards/list-guideline-recategorizations + * @kind table + * @name List all guideline recategorizations observed in a database + * @description Lists all the guideline recategorizations that were indexed in the database. + */ + +import cpp +import GuidelineRecategorizations + +from GuidelineRecategorization gr +select gr.getRuleId(), gr.getQuery().getCategory(), gr.getCategory() diff --git a/cpp/common/src/codingstandards/cpp/lifetimes/lifetimeprofile/LifetimeProfile.qll b/cpp/common/src/codingstandards/cpp/lifetimes/lifetimeprofile/LifetimeProfile.qll index bc7338fc97..b02f51380e 100644 --- a/cpp/common/src/codingstandards/cpp/lifetimes/lifetimeprofile/LifetimeProfile.qll +++ b/cpp/common/src/codingstandards/cpp/lifetimes/lifetimeprofile/LifetimeProfile.qll @@ -1,6 +1,4 @@ import cpp -private import semmle.code.cpp.dataflow.DataFlow -private import semmle.code.cpp.dataflow.internal.FlowVar private import semmle.code.cpp.controlflow.Nullness private import codingstandards.cpp.Dereferenced private import codingstandards.cpp.Expr @@ -199,7 +197,7 @@ newtype TPSetEntry = PSetNull(NullReason nr) or /** An invalid pointer, for the given reason. */ PSetInvalid(InvalidReason ir) or - /** An unkown pointer. */ + /** An unknown pointer. */ PSetUnknown() /** diff --git a/cpp/common/src/codingstandards/cpp/resources/ResourceLeakAnalysis.qll b/cpp/common/src/codingstandards/cpp/resources/ResourceLeakAnalysis.qll new file mode 100644 index 0000000000..3dd61e934d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/resources/ResourceLeakAnalysis.qll @@ -0,0 +1,91 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.valuenumbering.HashCons +import semmle.code.cpp.controlflow.Dominance +import codeql.util.Boolean + +/** + * A library for detecting leaked resources. + * + * To use this library, implement `ResourceLeakConfigSig`: + * + * ``` + * class UnjoinedThreadConfig implements ResourceLeakConfigSig { + * predicate isResource(DataFlow::Node node) { + * node.asExpr().isThreadCreate() + * } + * + * predicate isFree(ControlFlowNode node, DataFlow::Node resource) { + * node.asExpr().isThreadJoin(resource.asExpr()) + * } + * } + * ``` + * + * You can now check if a resource is leaked through the module predicate + * `ResourceLeak::isLeaked(resource)`. + * + * The leak analysis finds the exit point of the function in which the resource is is declared, and + * then reverses execution from there using `getAPredecessor()`. When this backwards walk discovers + * a control flow node that frees the resource, that exploration stops. If any exploration reaches + * a resource, that resource may be leaked via that path. + * + * Uses `DataFlow::Node` in order to track aliases of the resource to better detect when the + * resource is freed. + * + * This library by default assumes that resources are expression nodes. To use it with other kinds + * of nodes requires overriding `resourceInitPoint`. + */ +signature module ResourceLeakConfigSig { + predicate isAllocate(ControlFlowNode node, DataFlow::Node resource); + + predicate isFree(ControlFlowNode node, DataFlow::Node resource); + + bindingset[node] + default DataFlow::Node getAnAlias(DataFlow::Node node) { + DataFlow::localFlow(node, result) + or + exists(Expr current, Expr after | + current in [node.asExpr(), node.asDefiningArgument()] and + after in [result.asExpr(), result.asDefiningArgument()] and + hashCons(current) = hashCons(after) and + strictlyDominates(current, after) + ) + } + + /* A point at which a resource is considered to have leaked if it has not been freed. */ + default ControlFlowNode outOfScope(ControlFlowNode allocPoint) { + result = allocPoint.(Expr).getEnclosingFunction().getBlock().getLastStmt() + } +} + +module ResourceLeak { + private newtype TResource = + TJustResource(DataFlow::Node resource, ControlFlowNode cfgNode) { + Config::isAllocate(cfgNode, resource) + } + + private predicate isLeakedAtControlPoint(TResource resource, ControlFlowNode cfgNode) { + // Holds if this control point is where the resource was allocated (and therefore not freed). + resource = TJustResource(_, cfgNode) + or + // Holds if this control point does not free the resource, and is reachable from a point that + // does not free the resource. + isLeakedAtControlPoint(resource, cfgNode.getAPredecessor()) and + not exists(DataFlow::Node freed, DataFlow::Node resourceNode | + Config::isFree(cfgNode, freed) and + freed = Config::getAnAlias(resourceNode) and + resource = TJustResource(resourceNode, _) + ) + } + + /** + * Holds if `resource` is leaked. Use this module predicate to find leaked resources. + */ + ControlFlowNode getALeak(ControlFlowNode allocPoint) { + exists(TResource resourceWrapper, DataFlow::Node resource | + resourceWrapper = TJustResource(resource, allocPoint) and + result = Config::outOfScope(allocPoint) and + isLeakedAtControlPoint(resourceWrapper, result) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/resources/ResourceManagement.qll b/cpp/common/src/codingstandards/cpp/resources/ResourceManagement.qll index db65dd4920..17c30196a0 100644 --- a/cpp/common/src/codingstandards/cpp/resources/ResourceManagement.qll +++ b/cpp/common/src/codingstandards/cpp/resources/ResourceManagement.qll @@ -1,58 +1,45 @@ import cpp import semmle.code.cpp.dataflow.DataFlow +import codingstandards.cpp.resources.ResourceLeakAnalysis -/** - * The `ResourceAcquisitionExpr` abstract class models resource - * acquisition and release expressions - */ -abstract class ResourceAcquisitionExpr extends Expr { - abstract Expr getReleaseExpr(); -} - -// allocation - deallocation -class AllocExpr extends ResourceAcquisitionExpr { - AllocExpr() { this.(AllocationExpr).requiresDealloc() } - - override Expr getReleaseExpr() { - exists(DeallocationExpr d | result = d.getFreedExpr()) and - DataFlow::localFlow(DataFlow::exprNode(this), DataFlow::exprNode(result)) - } -} - -// file open-close -class FstreamAcquisitionExpr extends ResourceAcquisitionExpr { - FstreamAcquisitionExpr() { +module ResourceLeakConfig implements ResourceLeakConfigSig { + predicate isAllocate(ControlFlowNode allocPoint, DataFlow::Node node) { + exists(AllocationExpr alloc | + allocPoint = alloc and + alloc.requiresDealloc() and + node.asExpr() = alloc + ) + or exists(FunctionCall f | - f.getTarget().hasQualifiedName("std", "basic_fstream", "open") and this = f.getQualifier() + f.getTarget().hasQualifiedName("std", "basic_fstream", "open") and + allocPoint = f and + node.asDefiningArgument() = f.getQualifier() ) - } - - override Expr getReleaseExpr() { + or exists(FunctionCall f | - f.getTarget().hasQualifiedName("std", "basic_fstream", "close") and result = f.getQualifier() - ) and - exists(DataFlow::Node def | - def.asDefiningArgument() = this and - DataFlow::localFlow(def, DataFlow::exprNode(result)) + f.getTarget().hasQualifiedName("std", "mutex", "lock") and + allocPoint = f and + node.asDefiningArgument() = f.getQualifier() ) } -} -// mutex lock unlock -class MutexAcquisitionExpr extends ResourceAcquisitionExpr { - MutexAcquisitionExpr() { + predicate isFree(ControlFlowNode node, DataFlow::Node resource) { + exists(DeallocationExpr d, Expr freedExpr | + freedExpr = d.getFreedExpr() and + node = d and + resource.asExpr() = freedExpr + ) + or exists(FunctionCall f | - f.getTarget().hasQualifiedName("std", "mutex", "lock") and this = f.getQualifier() + f.getTarget().hasQualifiedName("std", "basic_fstream", "close") and + node = f and + resource.asExpr() = f.getQualifier() ) - } - - override Expr getReleaseExpr() { + or exists(FunctionCall f | - f.getTarget().hasQualifiedName("std", "mutex", "unlock") and result = f.getQualifier() - ) and - exists(DataFlow::Node def | - def.asDefiningArgument() = this and - DataFlow::localFlow(def, DataFlow::exprNode(result)) + f.getTarget().hasQualifiedName("std", "mutex", "unlock") and + node = f and + resource.asExpr() = f.getQualifier() ) } } diff --git a/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.qll b/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.qll index 98044c3ce1..b213087c5c 100644 --- a/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.qll +++ b/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.qll @@ -7,22 +7,23 @@ import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import codingstandards.cpp.Nullness import codingstandards.cpp.Expr -import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import NullPointerToPointerMemberExpressionFlow::PathGraph abstract class AccessOfUndefinedMemberThroughNullPointerSharedQuery extends Query { } Query getQuery() { result instanceof AccessOfUndefinedMemberThroughNullPointerSharedQuery } query predicate problems( - PointerToMemberExpr pointerToMemberExpr, DataFlow::PathNode source, DataFlow::PathNode sink, - string message, Location sourceLocation, string sourceDescription + PointerToMemberExpr pointerToMemberExpr, + NullPointerToPointerMemberExpressionFlow::PathNode source, + NullPointerToPointerMemberExpressionFlow::PathNode sink, string message, Location sourceLocation, + string sourceDescription ) { not isExcluded(pointerToMemberExpr, getQuery()) and message = "A null pointer-to-member value from $@ is passed as the second operand to a pointer-to-member expression." and sink.getNode().asExpr() = pointerToMemberExpr.getPointerExpr() and - any(NullPointerToPointerMemberExpressionConfig config).hasFlowPath(source, sink) and + NullPointerToPointerMemberExpressionFlow::flowPath(source, sink) and sourceLocation = source.getNode().getLocation() and sourceDescription = "initialization" } diff --git a/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.qll b/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.qll index 7d7bb627ee..0271d7c6e7 100644 --- a/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.qll +++ b/cpp/common/src/codingstandards/cpp/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.qll @@ -44,7 +44,7 @@ newtype TStaticMemberPointerAbstractValue = AssignedNullValue(StaticMemberPointer ptr, Expr val) { // A null value tracked via the data flow graph exists(ControlFlowNode n | - any(NullValueToAssignmentConfig config).hasFlow(_, DataFlow::exprNode(val)) and + NullValueToAssignmentFlow::flow(_, DataFlow::exprNode(val)) and n.(Assignment).getLValue() = ptr.getAnAccess() and n.(Assignment).getRValue() = val ) @@ -63,7 +63,7 @@ newtype TStaticMemberPointerAbstractValue = AssignedNonNullValue(StaticMemberPointer ptr, Expr val) { // A non-null value tracked via the data flow graph exists(ControlFlowNode n | - not any(NullValueToAssignmentConfig config).hasFlow(_, DataFlow::exprNode(val)) and + NullValueToAssignmentFlow::flow(_, DataFlow::exprNode(val)) and n.(Assignment).getLValue() = ptr.getAnAccess() and n.(Assignment).getRValue() = val ) diff --git a/cpp/common/src/codingstandards/cpp/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.qll b/cpp/common/src/codingstandards/cpp/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.qll new file mode 100644 index 0000000000..603a75bd01 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.qll @@ -0,0 +1,17 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The address-of operator shall not be overloaded. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Operator + +abstract class AddressOfOperatorOverloadedSharedQuery extends Query { } + +Query getQuery() { result instanceof AddressOfOperatorOverloadedSharedQuery } + +query predicate problems(UnaryAddressOfOperator e, string message) { + not isExcluded(e, getQuery()) and message = "The unary & operator overloaded." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.qll b/cpp/common/src/codingstandards/cpp/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.qll new file mode 100644 index 0000000000..da17706f54 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.qll @@ -0,0 +1,34 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The argument to a mixed-use macro parameter shall not be subject to further + * expansion. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Macro + +abstract class AMixedUseMacroArgumentSubjectToExpansionSharedQuery extends Query { } + +Query getQuery() { result instanceof AMixedUseMacroArgumentSubjectToExpansionSharedQuery } + +query predicate problems(FunctionLikeMacro m, string message) { + exists(MacroInvocation mi, int i, string expanded, string param | + not isExcluded(m, getQuery()) and + mi = m.getAnInvocation() and + param = m.getParameter(i) and + ( + exists(TokenPastingOperator op | op.getMacro() = m and op.getOperand() = param) + or + exists(StringizingOperator op | op.getMacro() = m and op.getOperand() = param) + ) and + // An expansion that is equal to "" means the expansion is not used and is optimized away by EDG. This happens when the expanded argument is an operand to `#` or `##`. + // This check ensure there is an expansion that is used. + expanded = mi.getExpandedArgument(i) and + not expanded = "" and + not mi.getUnexpandedArgument(i) = mi.getExpandedArgument(i) and + message = + "Macro " + m.getName() + " contains use of parameter " + param + " used in multiple contexts." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.qll b/cpp/common/src/codingstandards/cpp/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.qll new file mode 100644 index 0000000000..b7ec4917bd --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.qll @@ -0,0 +1,46 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * An array passed as a function argument shall not decay to a pointer. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class ArrayPassedAsFunctionArgumentDecayToAPointerSharedQuery extends Query { } + +Query getQuery() { result instanceof ArrayPassedAsFunctionArgumentDecayToAPointerSharedQuery } + +predicate arrayToPointerDecay(Access ae, Parameter p) { + ( + p.getType() instanceof PointerType and + // exclude parameters of void* because then it assumed the caller can pass in dimensions through other means. + // examples are uses in `memset` or `memcpy` + not p.getType() instanceof VoidPointerType + or + p.getType() instanceof ArrayType + ) and + ae.getType() instanceof ArrayType and + // exclude char[] arrays because we assume that we can determine its dimension by looking for a NULL byte. + not ae.getType().(ArrayType).getBaseType() instanceof CharType +} + +query predicate problems( + Element e, string message, Variable array, string array_string, Parameter decayedArray, + string decayedArray_string, Function f, string f_string +) { + exists(FunctionCall fc, VariableAccess arrayAccess, int i | + not isExcluded(e, getQuery()) and + arrayAccess = array.getAnAccess() and + f = fc.getTarget() and + arrayAccess = fc.getArgument(i) and + decayedArray = f.getParameter(i) and + arrayToPointerDecay(arrayAccess, decayedArray) and + not arrayAccess.isAffectedByMacro() and + e = fc.getArgument(i) and + array_string = array.getName() and + decayedArray_string = decayedArray.getName() and + f_string = f.getName() and + message = "The array $@ decays to the pointer $@ when passed as an argument to the function $@." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/asmdeclarationused/AsmDeclarationUsed.qll b/cpp/common/src/codingstandards/cpp/rules/asmdeclarationused/AsmDeclarationUsed.qll new file mode 100644 index 0000000000..c6748683da --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/asmdeclarationused/AsmDeclarationUsed.qll @@ -0,0 +1,16 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The asm declaration shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class AsmDeclarationUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof AsmDeclarationUsedSharedQuery } + +query predicate problems(AsmStmt e, string message) { + not isExcluded(e, getQuery()) and message = "Use of asm declaration" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.qll b/cpp/common/src/codingstandards/cpp/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.qll new file mode 100644 index 0000000000..295e346913 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.qll @@ -0,0 +1,24 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The library functions atof, atoi, atol and atoll from shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +private string atoi() { result = ["atof", "atoi", "atol", "atoll"] } + +abstract class AtofAtoiAtolAndAtollUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof AtofAtoiAtolAndAtollUsedSharedQuery } + +query predicate problems(FunctionCall fc, string message) { + exists(Function f | + not isExcluded(fc, getQuery()) and + f = fc.getTarget() and + f.getName() = atoi() and + f.getFile().getBaseName() = "stdlib.h" and + message = "Call to banned function " + f.getName() + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/backslashcharactermisuse/BackslashCharacterMisuse.qll b/cpp/common/src/codingstandards/cpp/rules/backslashcharactermisuse/BackslashCharacterMisuse.qll new file mode 100644 index 0000000000..34cb93fb39 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/backslashcharactermisuse/BackslashCharacterMisuse.qll @@ -0,0 +1,23 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * In character literals and non-raw string literals, \ shall only be used to form a + * defined escape sequence or universal character name. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class BackslashCharacterMisuseSharedQuery extends Query { } + +Query getQuery() { result instanceof BackslashCharacterMisuseSharedQuery } + +query predicate problems(StringLiteral l, string message) { + exists(string es | + not isExcluded(l, getQuery()) and + es = l.getANonStandardEscapeSequence(_, _) and + // Exclude universal-character-names, which begin with \u or \U + not es.toLowerCase().matches("\\u") and + message = "This literal contains the non-standard escape sequence " + es + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.qll b/cpp/common/src/codingstandards/cpp/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.qll index 3282c75e1e..e27f09fd98 100644 --- a/cpp/common/src/codingstandards/cpp/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.qll +++ b/cpp/common/src/codingstandards/cpp/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.qll @@ -10,7 +10,6 @@ import semmle.code.cpp.security.BufferWrite import semmle.code.cpp.commons.Buffer import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.dataflow.TaintTracking -import semmle.code.cpp.dataflow.internal.TaintTrackingUtil import codingstandards.cpp.PossiblyUnsafeStringOperation abstract class BasicStringMayNotBeNullTerminatedSharedQuery extends Query { } diff --git a/cpp/common/src/codingstandards/cpp/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.qll b/cpp/common/src/codingstandards/cpp/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.qll new file mode 100644 index 0000000000..27048b2d25 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.qll @@ -0,0 +1,41 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A bit-field shall have an appropriate type. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Compiler + +abstract class BitFieldShallHaveAnAppropriateTypeSharedQuery extends Query { } + +Query getQuery() { result instanceof BitFieldShallHaveAnAppropriateTypeSharedQuery } + +Type getSupportedBitFieldType(Compiler compiler) { + compiler instanceof UnsupportedCompiler and + ( + result instanceof IntType and + ( + result.(IntegralType).isExplicitlySigned() or + result.(IntegralType).isExplicitlyUnsigned() + ) + or + result instanceof BoolType + ) + or + (compiler instanceof Gcc or compiler instanceof Clang) and + ( + result instanceof IntegralOrEnumType + or + result instanceof BoolType + ) +} + +query predicate problems(BitField bitField, string message) { + not isExcluded(bitField, getQuery()) and + /* A violation would neither be an appropriate primitive type nor an appropriate typedef. */ + not getSupportedBitFieldType(getCompiler(bitField.getFile())) = + bitField.getType().resolveTypedefs() and + message = "Bit-field '" + bitField + "' is declared on type '" + bitField.getType() + "'." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.qll b/cpp/common/src/codingstandards/cpp/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.qll new file mode 100644 index 0000000000..0e516a43ec --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.qll @@ -0,0 +1,25 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The built-in unary - operator should not be applied to an expression of unsigned + * type. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class BuiltInUnaryOperatorAppliedToUnsignedExpressionSharedQuery extends Query { } + +Query getQuery() { result instanceof BuiltInUnaryOperatorAppliedToUnsignedExpressionSharedQuery } + +query predicate problems(Element e, string message) { + exists(UnaryMinusExpr ex, IntegralType t | + t = ex.getOperand().getExplicitlyConverted().getType().getUnderlyingType() and + t.isUnsigned() and + not ex.isAffectedByMacro() and + e = ex.getOperand() and + not isExcluded(e, getQuery()) and + message = + "The unary minus operator shall not be applied to an expression whose underlying type is unsigned." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.qll b/cpp/common/src/codingstandards/cpp/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.qll new file mode 100644 index 0000000000..66f1006d17 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.qll @@ -0,0 +1,26 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CastCharBeforeConvertingToLargerSizesSharedQuery extends Query { } + +Query getQuery() { result instanceof CastCharBeforeConvertingToLargerSizesSharedQuery } + +query predicate problems(Cast c, string message) { + not isExcluded(c, getQuery()) and + // find cases where there is a conversion happening wherein the + // base type is a char + c.getExpr().getType() instanceof CharType and + not c.getExpr().getType() instanceof UnsignedCharType and + // it's a bigger type + c.getType().getSize() > c.getExpr().getType().getSize() and + // and it's some kind of integer type + c.getType().getUnderlyingType() instanceof IntegralType and + not c.isFromTemplateInstantiation(_) and + message = + "Expression not converted to `unsigned char` before converting to a larger integer type." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.qll b/cpp/common/src/codingstandards/cpp/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.qll new file mode 100644 index 0000000000..48fa1f0c86 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.qll @@ -0,0 +1,20 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Casts shall not be performed between a pointer to function and any other type. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CastsBetweenAPointerToFunctionAndAnyOtherTypeSharedQuery extends Query { } + +Query getQuery() { result instanceof CastsBetweenAPointerToFunctionAndAnyOtherTypeSharedQuery } + +query predicate problems(Cast c, string message) { + not isExcluded(c, getQuery()) and + not c.isImplicit() and + not c.isAffectedByMacro() and + c.getExpr().getType() instanceof FunctionPointerType and + message = "Cast converting a pointer to function." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.qll b/cpp/common/src/codingstandards/cpp/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.qll index 5309f59a34..81fd306d86 100644 --- a/cpp/common/src/codingstandards/cpp/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.qll +++ b/cpp/common/src/codingstandards/cpp/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.qll @@ -6,12 +6,12 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import codingstandards.cpp.exceptions.ExceptionFlow -abstract class CatchExceptionsByLValueReferenceSharedQuery extends Query { } +abstract class CatchExceptionsByLvalueReferenceSharedQuery extends Query { } -Query getQuery() { result instanceof CatchExceptionsByLValueReferenceSharedQuery } +Query getQuery() { result instanceof CatchExceptionsByLvalueReferenceSharedQuery } query predicate problems(Parameter catchParameter, string message) { exists(CatchBlock cb, HandlerType catchType | diff --git a/cpp/common/src/codingstandards/cpp/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.qll b/cpp/common/src/codingstandards/cpp/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.qll new file mode 100644 index 0000000000..676b2d3030 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.qll @@ -0,0 +1,18 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The presence of a nested /* comment can indicate accidentally commented out code. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CharacterSequenceUsedWithinACStyleCommentSharedQuery extends Query { } + +Query getQuery() { result instanceof CharacterSequenceUsedWithinACStyleCommentSharedQuery } + +query predicate problems(CStyleComment c, string message) { + not isExcluded(c, getQuery()) and + exists(c.getContents().regexpFind("./\\*", _, _)) and + message = "C-style /* comment includes nested /*." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.qll b/cpp/common/src/codingstandards/cpp/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.qll new file mode 100644 index 0000000000..3d65ea3662 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/closefilehandlewhennolongerneededshared/CloseFileHandleWhenNoLongerNeededShared.qll @@ -0,0 +1,176 @@ +/** + * Provides a library which includes a `problems` predicate for reporting + * file handles which are open but not closed before they go out of scope. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import semmle.code.cpp.controlflow.StackVariableReachability +import codingstandards.cpp.standardlibrary.FileAccess +import codingstandards.cpp.Allocations + +/** + * 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 allocation call + opened(e) and + // We are only interested in allocation calls that are + // actually freed somehow, as MemoryNeverFreed + // 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) + ) + } +} + +/** + * 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() +} + +abstract class CloseFileHandleWhenNoLongerNeededSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof CloseFileHandleWhenNoLongerNeededSharedSharedQuery } + +query predicate problems(ControlFlowNode def, string message, Stmt ret, string retMsg) { + not isExcluded(def, getQuery()) and + message = "The file opened here may not be closed at $@." and + retMsg = "this location" 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() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/commaoperatorused/CommaOperatorUsed.qll b/cpp/common/src/codingstandards/cpp/rules/commaoperatorused/CommaOperatorUsed.qll index a6a80969b8..6985f7fc1e 100644 --- a/cpp/common/src/codingstandards/cpp/rules/commaoperatorused/CommaOperatorUsed.qll +++ b/cpp/common/src/codingstandards/cpp/rules/commaoperatorused/CommaOperatorUsed.qll @@ -1,5 +1,6 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * The comma operator shall not be used. */ import cpp diff --git a/cpp/common/src/codingstandards/cpp/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.qll b/cpp/common/src/codingstandards/cpp/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.qll new file mode 100644 index 0000000000..71b06a4662 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.qll @@ -0,0 +1,38 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unsigned integer + * wraparound related to constant expressions. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Macro +import codingstandards.cpp.Exclusions +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +abstract class ConstantUnsignedIntegerExpressionsWrapAroundSharedQuery extends Query { } + +Query getQuery() { result instanceof ConstantUnsignedIntegerExpressionsWrapAroundSharedQuery } + +query predicate problems(BinaryArithmeticOperation bao, string message) { + not isExcluded(bao, getQuery()) and + bao.isConstant() and + bao.getUnderlyingType().(IntegralType).isUnsigned() and + convertedExprMightOverflow(bao) and + // Exclude expressions generated from macro invocations of argument-less macros in third party + // code. This is because these are not under the control of the developer. Macros with arguments + // are not excluded, so that we can report cases where the argument provided by the developer + // wraps around (this may also report cases where the macro itself contains a wrapping expression, + // but we cannot distinguish these cases because we don't know which generated expressions are + // affected by which arguments). + // + // This addresses a false positive in the test cases on UULONG_MAX, which is reported in MUSL + // because it is defined as (2ULL*LLONG_MAX+1), which is a constant integer expression, and + // although it doesn't wrap in practice, our range analysis loses precision at the top end of the + // unsigned long long range so incorrectly assumes it can wrap. + not exists(LibraryMacro m, MacroInvocation mi | + mi = m.getAnInvocation() and + mi.getAnExpandedElement() = bao and + not exists(mi.getUnexpandedArgument(_)) + ) and + message = "Use of a constant, unsigned, integer expression that over- or under-flows." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/constlikereturnvalue/ConstLikeReturnValue.qll b/cpp/common/src/codingstandards/cpp/rules/constlikereturnvalue/ConstLikeReturnValue.qll index dde44214b8..a366991714 100644 --- a/cpp/common/src/codingstandards/cpp/rules/constlikereturnvalue/ConstLikeReturnValue.qll +++ b/cpp/common/src/codingstandards/cpp/rules/constlikereturnvalue/ConstLikeReturnValue.qll @@ -1,12 +1,15 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * The pointers returned by the Standard Library functions localeconv, getenv, + * setlocale or, strerror shall only be used as if they have pointer to + * const-qualified type. */ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import DFFlow::PathGraph abstract class ConstLikeReturnValueSharedQuery extends Query { } @@ -41,27 +44,20 @@ class ObjectWrite extends Expr { /** * DF configuration for flows from a `NotModifiableCall` to a object modifications. */ -class DFConf extends DataFlow::Configuration { - DFConf() { this = "DFConf" } +module DFConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NotModifiableCall } - override predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof NotModifiableCall - } - - override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ObjectWrite } + predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ObjectWrite } } -query predicate problems( - Element e, string message, DataFlow::PathNode source, string sourcetext, DataFlow::PathNode sink, - string sinktext -) { +module DFFlow = DataFlow::Global; + +query predicate problems(Element e, DFFlow::PathNode source, DFFlow::PathNode sink, string message) { not isExcluded(e, getQuery()) and // the modified object comes from a call to one of the ENV functions - any(DFConf d).hasFlowPath(source, sink) and + DFFlow::flowPath(source, sink) and e = sink.getNode().asExpr() and message = "The object returned by the function " + - source.getNode().asExpr().(FunctionCall).getTarget().getName() + " should not be modified." and - sourcetext = source.toString() and - sinktext = sink.toString() + source.getNode().asExpr().(FunctionCall).getTarget().getName() + " should not be modified." } diff --git a/cpp/common/src/codingstandards/cpp/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.qll b/cpp/common/src/codingstandards/cpp/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.qll index 657a000caf..fcf20afbc0 100644 --- a/cpp/common/src/codingstandards/cpp/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.qll +++ b/cpp/common/src/codingstandards/cpp/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.qll @@ -85,6 +85,26 @@ class ContainerEmptyCall extends FunctionCall { } } +/** + * A call to either `begin()` on a container. + */ +class ContainerBeginCall extends FunctionCall { + ContainerBeginCall() { + getTarget().getDeclaringType() instanceof ContainerType and + getTarget().getName() = "begin" + } +} + +/** + * A call to either `end()` on a container. + */ +class ContainerEndCall extends FunctionCall { + ContainerEndCall() { + getTarget().getDeclaringType() instanceof ContainerType and + getTarget().getName() = "end" + } +} + /** * A call to either `size()` or `length()` on a container. */ @@ -179,7 +199,21 @@ class StringContainerConstructorCall extends ContainerConstructorCall { c.getNumberOfParameters() = 0 and result = 0 or - // from c-string constructor + // from c-string constructors + c.getNumberOfParameters() = 1 and + c.getParameter(0).getType() = stringInstantiation.getConstCharTPointer() and + result = getArgument(0).getValue().length() + or + c.getNumberOfParameters() = 2 and + c.getParameter(0).getType() = stringInstantiation.getConstCharTPointer() and + c.getParameter(1).getType() = stringInstantiation.getSizeType() and + result = getArgument(1).getValue().toFloat() + or + c.getNumberOfParameters() = 2 and + c.getParameter(0).getType() = stringInstantiation.getSizeType() and + c.getParameter(1).getType() = stringInstantiation.getCharT() and + result = getArgument(0).getValue().toFloat() + or c.getNumberOfParameters() = 2 and c.getParameter(0).getType() = stringInstantiation.getConstCharTPointer() and c.getParameter(1).getType() = stringInstantiation.getConstAllocatorReferenceType() and diff --git a/cpp/common/src/codingstandards/cpp/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.qll b/cpp/common/src/codingstandards/cpp/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.qll new file mode 100644 index 0000000000..ae87176517 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.qll @@ -0,0 +1,53 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * User-provided copy assignment operators and move assignment operators shall handle + * self-assignment. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Operator + +abstract class CopyAndMoveAssignmentsShallHandleSelfAssignmentSharedQuery extends Query { } + +Query getQuery() { result instanceof CopyAndMoveAssignmentsShallHandleSelfAssignmentSharedQuery } + +predicate isUserCopyOrUserMove(Operator o) { + o instanceof UserCopyOperator or + o instanceof UserMoveOperator +} + +predicate callsStdSwap(Function f) { + exists(FunctionCall fc | + fc.getTarget().hasGlobalOrStdName("swap") and + fc.getEnclosingFunction() = f + ) +} + +predicate callsNoExceptSwap(Operator o) { + exists(Function f, FunctionCall fc | + callsStdSwap(f) and + fc.getEnclosingFunction() = o and + fc.getTarget() = f + ) +} + +predicate checksForSelfAssignment(Operator o) { + exists(IfStmt i, ComparisonOperation c | + i.getEnclosingFunction() = o and + i.getCondition() = c and + ( + c.getLeftOperand().toString() = "this" or + c.getRightOperand().toString() = "this" + ) + ) +} + +query predicate problems(Operator o, string message) { + not isExcluded(o, getQuery()) and + isUserCopyOrUserMove(o) and + not callsNoExceptSwap(o) and + not checksForSelfAssignment(o) and + message = "User defined copy or user defined move does not handle self-assignment correctly." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/csignalfunctionsused/CsignalFunctionsUsed.qll b/cpp/common/src/codingstandards/cpp/rules/csignalfunctionsused/CsignalFunctionsUsed.qll new file mode 100644 index 0000000000..15c71018f9 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/csignalfunctionsused/CsignalFunctionsUsed.qll @@ -0,0 +1,21 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Signal handling contains implementation-defined and undefined behaviour. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CsignalFunctionsUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof CsignalFunctionsUsedSharedQuery } + +query predicate problems(FunctionCall fc, string message) { + exists(Function f | + not isExcluded(fc, getQuery()) and + f = fc.getTarget() and + f.hasGlobalOrStdName(["signal", "raise"]) and + message = "Use of function '" + f.getQualifiedName() + "'." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/csignaltypesused/CsignalTypesUsed.qll b/cpp/common/src/codingstandards/cpp/rules/csignaltypesused/CsignalTypesUsed.qll new file mode 100644 index 0000000000..21de1066f6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/csignaltypesused/CsignalTypesUsed.qll @@ -0,0 +1,21 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Signal handling contains implementation-defined and undefined behaviour. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CsignalTypesUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof CsignalTypesUsedSharedQuery } + +query predicate problems(TypeMention tm, string message) { + exists(UserType ut | + not isExcluded(tm, getQuery()) and + ut = tm.getMentionedType() and + ut.hasGlobalOrStdName("sig_atomic_t") and + message = "Use of type '" + ut.getQualifiedName() + "'." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/cstdiofunctionsused/CstdioFunctionsUsed.qll b/cpp/common/src/codingstandards/cpp/rules/cstdiofunctionsused/CstdioFunctionsUsed.qll new file mode 100644 index 0000000000..284997dc19 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/cstdiofunctionsused/CstdioFunctionsUsed.qll @@ -0,0 +1,38 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Streams and file I/O have a large number of unspecified, undefined, and + * implementation-defined behaviours associated with them. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CstdioFunctionsUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof CstdioFunctionsUsedSharedQuery } + +query predicate problems(FunctionCall fc, string message) { + exists(Function f | + not isExcluded(fc, getQuery()) and + f = fc.getTarget() and + f.hasGlobalOrStdName([ + "remove", "rename", "tmpfile", "tmpnam", + // File access + "fclose", "fflush", "fopen", "freopen", "setbuf", "setvbuf", + // Formatted input/output + "fprintf", "fscanf", "printf", "scanf", "snprintf", "sprintf", "sscanf", "vfprintf", + "vfscanf", "vprintf", "vscanf", "vsnprintf", "vsprintf", "vsscanf", + // Character input/output + "fgetc", "fgets", "fputc", "fputs", "getc", "getchar", "gets", "putc", "putchar", "puts", + "ungetc", + // Direct input/output + "fread", "fwrite", + // File positioning + "fgetpos", "fseek", "fsetpos", "ftell", "rewind", + // Error handling + "clearerr", "feof", "ferror", "perror" + ]) and + message = "Use of function '" + f.getQualifiedName() + "'." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/cstdiomacrosused/CstdioMacrosUsed.qll b/cpp/common/src/codingstandards/cpp/rules/cstdiomacrosused/CstdioMacrosUsed.qll new file mode 100644 index 0000000000..d610b6a166 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/cstdiomacrosused/CstdioMacrosUsed.qll @@ -0,0 +1,22 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Streams and file I/O have a large number of unspecified, undefined, and + * implementation-defined behaviours associated with them. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CstdioMacrosUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof CstdioMacrosUsedSharedQuery } + +query predicate problems(MacroInvocation mi, string message) { + not isExcluded(mi, getQuery()) and + mi.getMacroName() in [ + "BUFSIZ", "EOF", "FILENAME_MAX", "FOPEN_MAX", "L_tmpnam", "TMP_MAX", "_IOFBF", "IOLBF", + "_IONBF", "SEEK_CUR", "SEEK_END", "SEEK_SET" + ] and + message = "Use of macro '" + mi.getMacroName() + "'." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/cstdiotypesused/CstdioTypesUsed.qll b/cpp/common/src/codingstandards/cpp/rules/cstdiotypesused/CstdioTypesUsed.qll new file mode 100644 index 0000000000..d517d78c8b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/cstdiotypesused/CstdioTypesUsed.qll @@ -0,0 +1,27 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Streams and file I/O have a large number of unspecified, undefined, and + * implementation-defined behaviours associated with them. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class CstdioTypesUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof CstdioTypesUsedSharedQuery } + +query predicate problems(TypeMention tm, string message) { + exists(UserType ut | + not isExcluded(tm, getQuery()) and + ut = tm.getMentionedType() and + ut.hasGlobalOrStdName(["FILE", "fpos_t"]) and + // Not in the standard library + exists(tm.getFile().getRelativePath()) and + // Not in our tests copy of the standard library + not tm.getFile().getRelativePath() = + ["includes/standard-library/stddef.h", "includes/standard-library/stdio.h"] and + message = "Use of type '" + ut.getQualifiedName() + "'." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/deadcode/DeadCode.qll b/cpp/common/src/codingstandards/cpp/rules/deadcode/DeadCode.qll new file mode 100644 index 0000000000..fb0bd772a2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/deadcode/DeadCode.qll @@ -0,0 +1,137 @@ +/** + * Provides a library which includes a `problems` predicate for reporting DeadCode. + * + * This problems predicate finds the following kinds of dead code statement: + * - A declaration of a non-static stack variable whose initializing expression is pure and that is never subsequently accessed in live code. + * - A block that contain only dead statements. + * - A do loop whose condition is pure, and whose body contains only dead statements. + * - An if statement whose condition is pure, and whose then and else clauses (where they exist) only contain dead statements. + * - A while loop whose condition is pure, and whose body contains only dead statements. + * - Expression statements whose expressions are pure. + * - Writes to a non-static stack variable that is never subsequently read in live code. + */ + +import cpp +import codingstandards.cpp.alertreporting.HoldsForAllCopies +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.deadcode.UselessAssignments +import codingstandards.cpp.deadcode.UnreachableCode +import codingstandards.cpp.deadcode.UnusedVariables + +abstract class DeadCodeSharedQuery extends Query { } + +Query getQuery() { result instanceof DeadCodeSharedQuery } + +/** + * Holds if the `Stmt` `s` is either dead or unreachable. + */ +predicate isDeadOrUnreachableStmt(Stmt s) { + isDeadStmt(s) + or + s.getBasicBlock() = any(UnreachableBasicBlock ubb).getABasicBlock() +} + +predicate isDeadStmt(Stmt s) { + // A `DeclStmt` is dead code if: + // - All the declarations are variable declarations + // - None of those variables are ever accessed in non-dead code + // - The initializers for each of the variables are pure + // - It isn't constexpr and used to declare an array size + exists(DeclStmt ds | + ds = s and + // Use forex so that we don't flag "fake" generated `DeclStmt`s (e.g. those generated by the + // extractor for static_asserts) with no actual declarations + forex(Declaration d | d = ds.getADeclaration() | + exists(LocalScopeVariable v | + d = v and + v.getInitializer().getExpr().isPure() and + not exists(VariableAccess va | + va.getTarget() = v and + not isDeadOrUnreachableStmt(va.getEnclosingStmt()) + ) and + not countUsesInLocalArraySize(v) > 0 + ) + ) + ) + or + // A block that only contains dead statements. + exists(BlockStmt b | + b = s and + forall(Stmt child | child = b.getAStmt() | isDeadStmt(child)) and + // If this is a catch block, we should only report it as dead if it is the last catch block. + not exists(TryStmt ts, int i | + ts.getCatchClause(i) = b and + i < (ts.getNumberOfCatchClauses() - 1) + ) + ) + or + // A do statement whose condition is pure, and whose body contains only dead statements. + exists(DoStmt ds | + ds = s and + ds.getCondition().isPure() and + isDeadOrUnreachableStmt(ds.getStmt()) + ) + or + // An if statement whose condition is pure, and whose then and else clauses (where they exist) are dead or unreachable + exists(IfStmt is | + is = s and + is.getCondition().isPure() and + // Then part is either dead or unreachable + isDeadOrUnreachableStmt(is.getThen()) and + (exists(is.getElse()) implies isDeadOrUnreachableStmt(is.getElse())) + ) + or + // A while statement whose condition is pure, and whose body is a dead or unreachable statement + exists(WhileStmt ws | + ws = s and + ws.getCondition().isPure() and + isDeadOrUnreachableStmt(ws.getStmt()) + ) + or + // An expression statement which is pure + s.(ExprStmt).getExpr().isPure() + or + exists(SsaDefinition sd, LocalScopeVariable v | + // A useless definition + isUselessSsaDefinition(sd, v) and + s.(ExprStmt).getExpr() = sd.getDefinition() and + // The defining value is pure + sd.getDefiningValue(v).isPure() + ) + or + // Any TryStmt with a dead body is dead. We ignore the catch blocks, because if the body is dead, + // no exception can be thrown, and so the catch blocks are unreachable + exists(TryStmt ts | s = ts and isDeadStmt(ts.getStmt())) +} + +/** + * Holds if the `Stmt` `s` is dead, i.e. could be executed, but its removal would not meaningfully + * affect the program. + */ +class DeadStmtInstance extends Stmt { + DeadStmtInstance() { + isDeadStmt(this) and + // Exclude compiler generated statements + not this.isCompilerGenerated() and + // Exclude code fully generated by macros, because the code may be "live" in other expansions + isNotWithinMacroExpansion(this) and + // MISRA defines dead code as an "_executed_ statement whose removal would not affect the program + // output". We therefore exclude unreachable statements as they are, by definition, not executed. + not this.getBasicBlock() = any(UnreachableBasicBlock ubb).getABasicBlock() + } +} + +class DeadStmt = HoldsForAllCopies::LogicalResultElement; + +query predicate problems(DeadStmt s, string message) { + not isExcluded(s.getAnElementInstance(), getQuery()) and + message = "This statement is dead code." and + // Report only the highest level dead statement, to avoid over reporting + not exists(DeadStmt parent | + // All instances must share a dead statement parent for us to report the parent instead + forall(Stmt instance | instance = s.getAnElementInstance() | + parent.getAnElementInstance() = instance.getParentStmt() + ) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.qll b/cpp/common/src/codingstandards/cpp/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.qll new file mode 100644 index 0000000000..c2b857d600 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.qll @@ -0,0 +1,70 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A using declaration that makes a symbol available for unqualified lookup does not + * included definitions defined after the using declaration which can result in + * unexpected behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class DefinitionNotConsideredForUnqualifiedLookupSharedQuery extends Query { } + +Query getQuery() { result instanceof DefinitionNotConsideredForUnqualifiedLookupSharedQuery } + +/** + * Holds if `functionDecl` is a possible intended target of the `usingDecl`. + */ +pragma[noinline] +predicate isPossibleIntendedTarget( + FunctionDeclarationEntry functionDecl, UsingDeclarationEntry usingDecl +) { + // Extracted to improve the join order. With this approach, we first compute a set of using + // declarations and a set of possible intended targets + functionDecl.getDeclaration().isTopLevel() and + functionDecl.getDeclaration().getQualifiedName() = usingDecl.getDeclaration().getQualifiedName() and + functionDecl.getDeclaration().getNamespace().getParentNamespace*() = usingDecl.getParentScope() +} + +/** + * Holds if `functionDecl` is a possible intended target of the `usingDecl`, and they exist at the + * given locations. + */ +pragma[noinline] +predicate isPossibleIntendedTargetLocation( + FunctionDeclarationEntry functionDecl, UsingDeclarationEntry usingDecl, File usingsFile, + File unavailableFile, int usingsStartLine, int unavailableStartLine +) { + // Extracted to improve the join order. With this approach, we take the set of possible intended + // targets computed in isPossibleIntendedTargets, and compute the files and start lines. + // This helps avoid the join order preferred by the optimiser if this is all written directly in + // the from-where-select, where it will eagerly join: + // + // usingDeclarationEntries -> enclosing files -> all other elements in those files + // + // which is expensive when there are a lot of files with using declarations + isPossibleIntendedTarget(functionDecl, usingDecl) and + usingsFile = usingDecl.getFile() and + unavailableFile = functionDecl.getFile() and + usingsStartLine = usingDecl.getLocation().getStartLine() and + unavailableStartLine = functionDecl.getLocation().getStartLine() +} + +query predicate problems( + FunctionDeclarationEntry unavailableDecl, string message, UsingDeclarationEntry usingDecl, + string usingDecl_string +) { + not isExcluded(unavailableDecl, getQuery()) and + exists(File usingsFile, File unavailableFile, int usingsStartLine, int unavailableStartLine | + isPossibleIntendedTargetLocation(unavailableDecl, usingDecl, usingsFile, unavailableFile, + usingsStartLine, unavailableStartLine) and + // An approximation of order where we want the using to preceed the new declaration. + usingsFile = unavailableFile and + usingsStartLine < unavailableStartLine + ) and + message = + "Definition for '" + unavailableDecl.getName() + + "' is not available for unqualified lookup because it is declared after $@" and + usingDecl_string = "using-declaration" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/dereferenceofnullpointer/DereferenceOfNullPointer.qll b/cpp/common/src/codingstandards/cpp/rules/dereferenceofnullpointer/DereferenceOfNullPointer.qll new file mode 100644 index 0000000000..950b14df3d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/dereferenceofnullpointer/DereferenceOfNullPointer.qll @@ -0,0 +1,24 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Dereferencing a NULL pointer leads to undefined behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.lifetimes.lifetimeprofile.LifetimeProfile + +abstract class DereferenceOfNullPointerSharedQuery extends Query { } + +Query getQuery() { result instanceof DereferenceOfNullPointerSharedQuery } + +query predicate problems( + NullDereference nd, string message, Element explanation, string explanationDesc +) { + not isExcluded(nd, getQuery()) and + exists(NullReason nr, string nullMessage | + nr = nd.getAnInvalidReason() and + nr.hasMessage(nullMessage, explanation, explanationDesc) and + message = "Null may be dereferenced here " + nullMessage + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll b/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll index 87a4580ab3..5c7475883e 100644 --- a/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll +++ b/cpp/common/src/codingstandards/cpp/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.qll @@ -46,16 +46,32 @@ string step1(string s) { string step2(string s) { s = "m_" and result = "rn" } -predicate violation(UserVariable v1, UserVariable v2) { - v2 = getPotentialScopeOfVariable(v1) and +class VariableName extends string { + VariableName() { exists(UserVariable uv | uv.getName() = this) } + + string getCanon() { + result = + this.toLowerCase() + .replaceAll("_", "") + .regexpReplaceAll("[il]", "1") + .replaceAll("s", "5") + .replaceAll("z", "2") + .replaceAll("b", "8") + .replaceAll("h", "n") + .replaceAll("m", "rn") + .replaceAll("o", "0") + } +} + +predicate isConflictingName(VariableName name1, VariableName name2) { exists(string s1, string s2 | // over-approximate a match, because it is cheaper to compute - getCanon(v1) = getCanon(v2) and - v1 != v2 and - not v1.getName() = v2.getName() and + name1.getCanon() = name2.getCanon() and + // Exclude identical names + not name1 = name2 and // expand 'm' to 'm_' to match either 'm_' or 'rn' - s1 = v1.getName().replaceAll("_", "").replaceAll("m", "m_") and - s2 = v2.getName().replaceAll("_", "").replaceAll("m", "m_") and + s1 = name1.replaceAll("_", "").replaceAll("m", "m_") and + s2 = name2.replaceAll("_", "").replaceAll("m", "m_") and // at this point the strings must have equal length, the substitutions do not expand nor contract the string s1.length() = s2.length() and forall(int i | i in [0 .. s1.length() - 1] | @@ -87,6 +103,23 @@ predicate violation(UserVariable v1, UserVariable v2) { ) } +predicate violation(UserVariable v1, UserVariable v2) { + exists(string name1, string name2 | + isConflictingName(name1, name2) and + exists(Scope parentScope, Scope childScope | + parentScope.getVariable(name1) = v1 and + childScope.getVariable(name2) = v2 + | + childScope.getStrictParent+() = parentScope + or + // Disambiguate names in the same scope by name order + childScope = parentScope and + name1 < name2 + ) and + inSameTranslationUnitLate(v1.getFile(), v2.getFile()) + ) +} + query predicate problems( UserVariable v, string message, UserVariable v1, string v1Description, UserVariable v2, string v2Description diff --git a/cpp/common/src/codingstandards/cpp/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.qll b/cpp/common/src/codingstandards/cpp/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.qll index bea25d6ffa..83266ed524 100644 --- a/cpp/common/src/codingstandards/cpp/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.qll +++ b/cpp/common/src/codingstandards/cpp/rules/donotaccessaclosedfile/DoNotAccessAClosedFile.qll @@ -27,7 +27,7 @@ predicate accessSameVariable(VariableAccess va1, VariableAccess va2) { va1.getTarget() = va2.getTarget() } -SubBasicBlock followsFileClose(SubBasicBlock source, VariableAccess closedFile) { +SubBasicBlock followsFileClose(SubBasicBlock source, Expr closedFile) { result = source or exists(SubBasicBlock mid | diff --git a/cpp/common/src/codingstandards/cpp/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.qll b/cpp/common/src/codingstandards/cpp/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.qll new file mode 100644 index 0000000000..79eda7714d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.qll @@ -0,0 +1,193 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.types.Pointers +import codingstandards.cpp.Variable +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.pointsto.PointsTo +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * A function that has a parameter with a restrict-qualified pointer type. + */ +class FunctionWithRestrictParameters extends Function { + Parameter restrictPtrParam; + + FunctionWithRestrictParameters() { + restrictPtrParam.getUnspecifiedType() instanceof PointerOrArrayType and + ( + restrictPtrParam.getType().hasSpecifier(["restrict"]) and + restrictPtrParam = this.getAParameter() + or + this.hasGlobalName(["strcpy", "strncpy", "strcat", "strncat", "memcpy"]) and + restrictPtrParam = this.getParameter([0, 1]) + or + this.hasGlobalName(["strcpy_s", "strncpy_s", "strcat_s", "strncat_s", "memcpy_s"]) and + restrictPtrParam = this.getParameter([0, 2]) + or + this.hasGlobalName(["strtok_s"]) and + restrictPtrParam = this.getAParameter() + or + this.hasGlobalName(["printf", "printf_s", "scanf", "scanf_s"]) and + restrictPtrParam = this.getParameter(0) + or + this.hasGlobalName(["sprintf", "sprintf_s", "snprintf", "snprintf_s"]) and + restrictPtrParam = this.getParameter(3) + ) + } + + Parameter getARestrictPtrParam() { result = restrictPtrParam } +} + +/** + * A call to a function that has a parameter with a restrict-qualified pointer type. + */ +class CallToFunctionWithRestrictParameters extends FunctionCall { + CallToFunctionWithRestrictParameters() { + this.getTarget() instanceof FunctionWithRestrictParameters + } + + Expr getARestrictPtrArg() { + result = + this.getArgument(this.getTarget() + .(FunctionWithRestrictParameters) + .getARestrictPtrParam() + .getIndex()) + } + + Expr getAPtrArg(int index) { + result = this.getArgument(index) and + pointerValue(result) + } + + Expr getAPossibleSizeArg() { + exists(Parameter param | + param = this.getTarget().(FunctionWithRestrictParameters).getAParameter() and + param.getUnderlyingType() instanceof IntegralType and + // exclude __builtin_object_size + not result.(FunctionCall).getTarget() instanceof BuiltInFunction and + result = this.getArgument(param.getIndex()) + ) + } +} + +/** + * A `PointsToExpr` that is an argument of a pointer-type in a `CallToFunctionWithRestrictParameters` + */ +class CallToFunctionWithRestrictParametersArgExpr extends Expr { + int paramIndex; + + CallToFunctionWithRestrictParametersArgExpr() { + this = any(CallToFunctionWithRestrictParameters call).getAPtrArg(paramIndex) + } + + int getParamIndex() { result = paramIndex } +} + +int getStatedValue(Expr e) { + // `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful + // result in this case we pick the minimum value obtainable from dataflow and range analysis. + result = + upperBound(e) + .minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())) +} + +int getPointerArithmeticOperandStatedValue(CallToFunctionWithRestrictParametersArgExpr expr) { + result = getStatedValue(expr.(PointerArithmeticExpr).getOperand()) + or + // edge-case: &(array[index]) expressions + result = getStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand()) + or + // fall-back if `expr` is not a pointer arithmetic expression + not expr instanceof PointerArithmeticExpr and + not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and + result = 0 +} + +module PointerValueToRestrictArgConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { pointerValue(source.asExpr()) } + + predicate isSink(DataFlow::Node sink) { + exists(CallToFunctionWithRestrictParameters call | + sink.asExpr() = call.getAPtrArg(_).getAChild*() + ) + } + + predicate isBarrierIn(DataFlow::Node node) { + exists(AddressOfExpr a | node.asExpr() = a.getOperand().getAChild*()) + } +} + +module PointerValueToRestrictArgFlow = DataFlow::Global; + +abstract class DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery extends Query { } + +Query getQuery() { + result instanceof DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery +} + +query predicate problems( + CallToFunctionWithRestrictParameters call, string message, + CallToFunctionWithRestrictParametersArgExpr arg2, string arg2message, + CallToFunctionWithRestrictParametersArgExpr arg1, string arg1message, Expr source1, + string sourceMessage2, Expr source2, string lastMessage2 +) { + not isExcluded(call, getQuery()) and + exists(int argOffset1, int argOffset2, string sourceMessage1 | + arg1 = call.getARestrictPtrArg() and + arg2 = call.getAPtrArg(_) and + // enforce ordering to remove permutations if multiple restrict-qualified args exist + (not arg2 = call.getARestrictPtrArg() or arg2.getParamIndex() > arg1.getParamIndex()) and + ( + // check if two pointers address the same object + PointerValueToRestrictArgFlow::flow(DataFlow::exprNode(source1), + DataFlow::exprNode(arg1.getAChild*())) and + ( + // one pointer value flows to both args + PointerValueToRestrictArgFlow::flow(DataFlow::exprNode(source1), + DataFlow::exprNode(arg2.getAChild*())) and + sourceMessage1 = "$@" and + sourceMessage2 = "source" and + source1 = source2 + or + // there are two separate values that flow from an AddressOfExpr of the same target + getAddressOfExprTargetBase(source1) = getAddressOfExprTargetBase(source2) and + PointerValueToRestrictArgFlow::flow(DataFlow::exprNode(source2), + DataFlow::exprNode(arg2.getAChild*())) and + sourceMessage1 = "a pair of address-of expressions ($@, $@)" and + sourceMessage2 = "addressof1" and + not source1 = source2 + ) + ) and + // get the offset of the pointer arithmetic operand (or '0' if there is none) + argOffset1 = getPointerArithmeticOperandStatedValue(arg1) and + argOffset2 = getPointerArithmeticOperandStatedValue(arg2) and + ( + // case 1: the pointer args are the same. + // (definite aliasing) + argOffset1 = argOffset2 + or + // case 2: the pointer args are different, a size arg exists, + // and the size arg is greater than the difference between the offsets. + // (potential aliasing) + exists(Expr sizeArg | + sizeArg = call.getAPossibleSizeArg() and + getStatedValue(sizeArg) > (argOffset1 - argOffset2).abs() + ) + or + // case 3: the pointer args are different, and a size arg does not exist + // (potential aliasing) + not exists(call.getAPossibleSizeArg()) + ) and + lastMessage2 = "addressof2" and + arg2message = "aliased pointer" and + arg1message = "restrict-qualified parameter" and + message = + "Call to '" + call.getTarget().getName() + + "' passes an $@ to a $@ (pointer value derived from " + sourceMessage1 + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.qll b/cpp/common/src/codingstandards/cpp/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.qll index 40dac2d027..adb9785814 100644 --- a/cpp/common/src/codingstandards/cpp/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.qll +++ b/cpp/common/src/codingstandards/cpp/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.qll @@ -7,48 +7,47 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import ArrayToPointerDiffOperandFlow::PathGraph -class ArrayToPointerDiffOperandConfig extends DataFlow::Configuration { - ArrayToPointerDiffOperandConfig() { this = "ArrayToPointerDiffOperandConfig" } - - override predicate isSource(DataFlow::Node source) { +module ArrayToPointerDiffOperandConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr().(VariableAccess).getType() instanceof ArrayType or // Consider array to pointer decay for parameters. source.asExpr().(VariableAccess).getTarget().(Parameter).getType() instanceof ArrayType } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(PointerDiffExpr e | e.getAnOperand() = sink.asExpr()) } - override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { // Add a flow step from the base to the array expression to track pointers to elements of the array. exists(ArrayExpr e | e.getArrayBase() = pred.asExpr() and e = succ.asExpr()) } } +module ArrayToPointerDiffOperandFlow = DataFlow::Global; + abstract class DoNotSubtractPointersAddressingDifferentArraysSharedQuery extends Query { } Query getQuery() { result instanceof DoNotSubtractPointersAddressingDifferentArraysSharedQuery } query predicate problems( - DataFlow::Node sinkNode, DataFlow::PathNode source, DataFlow::PathNode sink, string message, - Variable currentOperandPointee, string currentOperandPointeeName, Variable otherOperandPointee, - string otherOperandPointeeName + DataFlow::Node sinkNode, ArrayToPointerDiffOperandFlow::PathNode source, + ArrayToPointerDiffOperandFlow::PathNode sink, string message, Variable currentOperandPointee, + string currentOperandPointeeName, Variable otherOperandPointee, string otherOperandPointeeName ) { exists( - PointerDiffExpr pointerSubtraction, string side, ArrayToPointerDiffOperandConfig c, - Variable sourceLeft, Variable sourceRight + PointerDiffExpr pointerSubtraction, string side, Variable sourceLeft, Variable sourceRight | not isExcluded(pointerSubtraction, getQuery()) and - c.hasFlow(DataFlow::exprNode(sourceLeft.getAnAccess()), + ArrayToPointerDiffOperandFlow::flow(DataFlow::exprNode(sourceLeft.getAnAccess()), DataFlow::exprNode(pointerSubtraction.getLeftOperand())) and - c.hasFlow(DataFlow::exprNode(sourceRight.getAnAccess()), + ArrayToPointerDiffOperandFlow::flow(DataFlow::exprNode(sourceRight.getAnAccess()), DataFlow::exprNode(pointerSubtraction.getRightOperand())) and not sourceLeft = sourceRight and - c.hasFlowPath(source, sink) and + ArrayToPointerDiffOperandFlow::flowPath(source, sink) and ( source.getNode().asExpr() = sourceLeft.getAnAccess() and sink.getNode().asExpr() = pointerSubtraction.getLeftOperand() and diff --git a/cpp/common/src/codingstandards/cpp/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.qll b/cpp/common/src/codingstandards/cpp/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.qll index 758dcc0157..5d3a7e1cda 100644 --- a/cpp/common/src/codingstandards/cpp/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.qll +++ b/cpp/common/src/codingstandards/cpp/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.qll @@ -7,55 +7,173 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.dataflow.new.DataFlow import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils +import codeql.util.Boolean abstract class DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery extends Query { } Query getQuery() { result instanceof DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery } +/** + * A `VariableAccess` of a variable that is an array, or a pointer type casted to a byte pointer. + */ +abstract class ArrayLikeAccess extends Expr { + abstract Element getElement(); + + abstract string getName(); + + abstract int getSize(); + + abstract DataFlow::Node getNode(); +} + +/** + * A `VariableAccess` of a variable that is an array. + */ +class ArrayVariableAccess extends ArrayLikeAccess, VariableAccess { + int size; + + ArrayVariableAccess() { size = getType().(ArrayType).getArraySize() } + + override Variable getElement() { result = getTarget() } + + override string getName() { result = getElement().getName() } + + override int getSize() { result = size } + + override DataFlow::Node getNode() { result.asExpr() = this } +} + +/** + * Get the size of the object pointed to by a type (pointer or array). + * + * Depth of type unwrapping depends on the type. Pointer will be dereferenced only once: the element + * size of `T*` is `sizeof(T)` while the element size of `T**` is `sizeof(T*)`. However, array types + * will be deeply unwrapped, as the pointed to size of `T[][]` is `sizeof(T)`. These processes + * interact, so the element size of a pointer to an array of `T` has an element size of `sizeof(T)` + * and not `sizeof(T[length])`. + */ +int elementSize(Type type, Boolean deref) { + if type instanceof ArrayType + then result = elementSize(type.(ArrayType).getBaseType(), false) + else + if deref = true and type instanceof PointerType + then result = elementSize(type.(PointerType).getBaseType(), false) + else result = type.getSize() +} + +/** + * A pointer type casted to a byte pointer, which is effectively a pointer to a byte array whose + * length depends on `elementSize()` of the original pointed-to type. + */ +class CastedToBytePointer extends ArrayLikeAccess, Conversion { + /** The sizeof() the pointed-to type */ + int size; + + CastedToBytePointer() { + getType().(PointerType).getBaseType().getSize() = 1 and + size = elementSize(getExpr().getType(), true) and + size > 1 + } + + override Element getElement() { result = this } + + override string getName() { result = "cast to byte pointer" } + + override int getSize() { result = size } + + override DataFlow::Node getNode() { + // Carefully avoid use-use flow, which would mean any later usage of the original pointer value + // after the cast would be considered a usage of the byte pointer value. + // + // To fix this, we currently assume the value is assigned to a variable, and find that variable + // with `.asDefinition()` like so: + exists(DataFlow::Node conversion | + conversion.asConvertedExpr() = this and + result.asDefinition() = conversion.asExpr() + ) + } +} + +predicate pointerRecastBarrier(DataFlow::Node barrier) { + // Casting to a differently sized pointer + exists(CStyleCast cast, Expr casted | + cast.getExpr() = casted and casted = barrier.asConvertedExpr() + | + not casted.getType().(PointerType).getBaseType().getSize() = + cast.getType().(PointerType).getBaseType().getSize() + ) +} + /** * A data-flow configuration that tracks access to an array to type to an array index expression. * This is used to determine possible pointer to array creations. */ -class ArrayToArrayExprConfig extends DataFlow::Configuration { - ArrayToArrayExprConfig() { this = "ArrayToArrayIndexConfig" } +module ByteArrayToArrayExprConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(CastedToBytePointer a | a.getNode() = source) } - override predicate isSource(DataFlow::Node source) { - source.asExpr().(VariableAccess).getType() instanceof ArrayType + predicate isBarrier(DataFlow::Node barrier) { + // Casting to a differently sized pointer invalidates this analysis. + pointerRecastBarrier(barrier) } - override predicate isSink(DataFlow::Node sink) { - exists(ArrayExpr c | c.getArrayBase() = sink.asExpr()) + predicate isSink(DataFlow::Node sink) { exists(ArrayExpr c | c.getArrayBase() = sink.asExpr()) } +} + +module BytePointerToArrayExprFlow = DataFlow::Global; + +/** + * A data-flow configuration that tracks access to an array to type to an array index expression. + * This is used to determine possible pointer to array creations. + */ +module ArrayToArrayExprConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(ArrayVariableAccess a | a.getNode() = source) } + + predicate isBarrier(DataFlow::Node barrier) { + // Casting to a differently sized pointer invalidates this analysis. + pointerRecastBarrier(barrier) } + + predicate isSink(DataFlow::Node sink) { exists(ArrayExpr c | c.getArrayBase() = sink.asExpr()) } } -/** Holds if the address taken expression `addressOf` takes the address of an array element at `index` of `array` with size `arraySize`. */ -predicate pointerOperandCreation(AddressOfExpr addressOf, Variable array, int arraySize, int index) { - arraySize = array.getType().(ArrayType).getArraySize() and - exists(ArrayExpr ae | - any(ArrayToArrayExprConfig cfg) - .hasFlow(DataFlow::exprNode(array.getAnAccess()), DataFlow::exprNode(ae.getArrayBase())) and - index = lowerBound(ae.getArrayOffset().getFullyConverted()) and +module ArrayToArrayExprFlow = DataFlow::Global; + +/** Holds if the address taken expression `addressOf` takes the address of an array element at `index` of `array`. */ +predicate pointerOperandCreation(AddressOfExpr addressOf, ArrayLikeAccess array, int index) { + exists(ArrayExpr ae, Expr arrayOffset | + ( + ArrayToArrayExprFlow::flow(array.getNode(), DataFlow::exprNode(ae.getArrayBase())) and + array instanceof ArrayVariableAccess + or + // Since casts can occur in the middle of flow, barriers are not perfect for modeling the + // desired behavior. Handle casts to byte pointers as sources in a separate flow analysis. + BytePointerToArrayExprFlow::flow(array.getNode(), DataFlow::exprNode(ae.getArrayBase())) and + // flow() may hold for `ArrayVariableAccess` in the above, even though they aren't sources + array instanceof CastedToBytePointer + ) and + arrayOffset = ae.getArrayOffset().getFullyConverted() and + index = lowerBound(arrayOffset) and + // This case typically indicates range analysis has gone wrong: + not index = exprMaxVal(arrayOffset) and addressOf.getOperand() = ae ) } /** A variable that points to an element of an array. */ class PointerOperand extends Variable { - Variable array; - int arraySize; + ArrayLikeAccess array; int index; AddressOfExpr source; PointerOperand() { - pointerOperandCreation(source, array, arraySize, index) and + pointerOperandCreation(source, array, index) and this.getAnAssignedValue() = source } - Variable getArray() { result = array } - - int getArraySize() { result = arraySize } + ArrayLikeAccess getArray() { result = array } int getIndex() { result = index } @@ -113,9 +231,7 @@ class DerivedArrayPointer extends Variable { DerivedArrayPointer() { derivedPointer(this, source, operand, index) } - Variable getArray() { result = operand.getArray() } - - int getArraySize() { result = operand.getArraySize() } + ArrayLikeAccess getArray() { result = operand.getArray() } int getIndex() { result = index } @@ -133,15 +249,10 @@ class DerivedArrayPointerOrPointerOperand extends Variable { this instanceof PointerOperand } - Variable getArray() { + ArrayLikeAccess getArray() { result = this.(DerivedArrayPointer).getArray() or result = this.(PointerOperand).getArray() } - int getArraySize() { - result = this.(DerivedArrayPointer).getArraySize() or - result = this.(PointerOperand).getArraySize() - } - int getIndex() { result = this.(DerivedArrayPointer).getIndex() or result = this.(PointerOperand).getIndex() } @@ -151,14 +262,16 @@ class DerivedArrayPointerOrPointerOperand extends Variable { } } -query predicate problems(Expr arrayPointerCreation, string message, Variable array, string arrayName) { +query predicate problems(Expr arrayPointerCreation, string message, Element array, string arrayName) { not isExcluded(arrayPointerCreation, getQuery()) and exists( DerivedArrayPointerOrPointerOperand derivedArrayPointerOrPointerOperand, int index, - int arraySize, int difference, string denomination + ArrayLikeAccess arrayAccess, int arraySize, int difference, string denomination | - array = derivedArrayPointerOrPointerOperand.getArray() and - arraySize = derivedArrayPointerOrPointerOperand.getArraySize() and + arrayAccess = derivedArrayPointerOrPointerOperand.getArray() and + array = arrayAccess.getElement() and + arrayName = arrayAccess.getName() and + arraySize = arrayAccess.getSize() and index = derivedArrayPointerOrPointerOperand.getIndex() and arrayPointerCreation = derivedArrayPointerOrPointerOperand.getSource() and difference = index - arraySize and @@ -175,7 +288,6 @@ query predicate problems(Expr arrayPointerCreation, string message, Variable arr ) and message = "Array pointer " + derivedArrayPointerOrPointerOperand.getName() + " points " + - (index - arraySize).toString() + " " + denomination + " passed the end of $@." - ) and - arrayName = array.getName() + difference.toString() + " " + denomination + " past the end of $@." + ) } diff --git a/cpp/common/src/codingstandards/cpp/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.qll b/cpp/common/src/codingstandards/cpp/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.qll index 15f1fa4057..aa8fa29bfd 100644 --- a/cpp/common/src/codingstandards/cpp/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.qll +++ b/cpp/common/src/codingstandards/cpp/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.qll @@ -8,7 +8,7 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import ArrayToRelationalOperationOperandFlow::PathGraph abstract class DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery extends Query { } @@ -32,21 +32,22 @@ class DecayedArrayAccess extends ArraySource { } } -class ArrayToRelationalOperationOperandConfig extends DataFlow::Configuration { - ArrayToRelationalOperationOperandConfig() { this = "ArrayToRelationalOperationOperandConfig" } +module ArrayToRelationalOperationOperandConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof ArraySource } - override predicate isSource(DataFlow::Node source) { source instanceof ArraySource } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(RelationalOperation op | op.getAnOperand() = sink.asExpr()) } - override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { // Add a flow step from the base to the array expression to track pointers to elements of the array. exists(ArrayExpr e | e.getArrayBase() = pred.asExpr() and e = succ.asExpr()) } } +module ArrayToRelationalOperationOperandFlow = + DataFlow::Global; + predicate isComparingPointers(RelationalOperation op) { forall(Expr operand | operand = op.getAnOperand() | operand.getType() instanceof PointerType or operand.getType() instanceof ArrayType @@ -54,22 +55,20 @@ predicate isComparingPointers(RelationalOperation op) { } query predicate problems( - RelationalOperation compare, DataFlow::PathNode source, DataFlow::PathNode sink, string message, + RelationalOperation compare, ArrayToRelationalOperationOperandFlow::PathNode source, + ArrayToRelationalOperationOperandFlow::PathNode sink, string message, Variable selectedOperandPointee, string selectedOperandPointeeName, Variable otherOperandPointee, string otherOperandPointeeName ) { not isExcluded(compare, getQuery()) and - exists( - ArrayToRelationalOperationOperandConfig c, Variable sourceLeft, Variable sourceRight, - string side - | - c.hasFlow(DataFlow::exprNode(sourceLeft.getAnAccess()), + exists(Variable sourceLeft, Variable sourceRight, string side | + ArrayToRelationalOperationOperandFlow::flow(DataFlow::exprNode(sourceLeft.getAnAccess()), DataFlow::exprNode(compare.getLeftOperand())) and - c.hasFlow(DataFlow::exprNode(sourceRight.getAnAccess()), + ArrayToRelationalOperationOperandFlow::flow(DataFlow::exprNode(sourceRight.getAnAccess()), DataFlow::exprNode(compare.getRightOperand())) and not sourceLeft = sourceRight and isComparingPointers(compare) and - c.hasFlowPath(source, sink) and + ArrayToRelationalOperationOperandFlow::flowPath(source, sink) and ( source.getNode().asExpr() = sourceLeft.getAnAccess() and sink.getNode().asExpr() = compare.getLeftOperand() and diff --git a/cpp/common/src/codingstandards/cpp/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.qll b/cpp/common/src/codingstandards/cpp/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.qll new file mode 100644 index 0000000000..1441b6ec97 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.qll @@ -0,0 +1,31 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The macro setjmp and function longjmp shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class DoNotUseSetjmpOrLongjmpSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof DoNotUseSetjmpOrLongjmpSharedSharedQuery } + +predicate isLongJumpCall(Locatable fc) { + fc.(FunctionCall).getTarget().hasGlobalOrStdName("longjmp") or + fc.(MacroInvocation).getMacroName() = "longjmp" +} + +predicate isSetJumpCall(MacroInvocation mi) { mi.getMacroName() = "setjmp" } + +query predicate problems(Element jmp, string message) { + exists(string callType | + not isExcluded(jmp, getQuery()) and + message = "Use of banned " + callType + "." and + ( + isLongJumpCall(jmp) and callType = "longjmp function" + or + isSetJumpCall(jmp) and callType = "setjmp macro" + ) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.qll b/cpp/common/src/codingstandards/cpp/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.qll new file mode 100644 index 0000000000..3dba1a3aa3 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.qll @@ -0,0 +1,19 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Empty throws with no currently handled exception can cause abrupt program + * termination. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class EmptyThrowOnlyWithinACatchHandlerSharedQuery extends Query { } + +Query getQuery() { result instanceof EmptyThrowOnlyWithinACatchHandlerSharedQuery } + +query predicate problems(ReThrowExpr re, string message) { + not isExcluded(re, getQuery()) and + not re.getEnclosingElement+() instanceof CatchBlock and + message = "Rethrow outside catch block" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.qll b/cpp/common/src/codingstandards/cpp/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.qll new file mode 100644 index 0000000000..d014e7be86 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.qll @@ -0,0 +1,18 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Although scoped enum will implicitly define an underlying type of int, the underlying base type of enumeration should always be explicitly defined with a type that will be large enough to store all enumerators. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class EnumerationNotDefinedWithAnExplicitUnderlyingTypeSharedQuery extends Query { } + +Query getQuery() { result instanceof EnumerationNotDefinedWithAnExplicitUnderlyingTypeSharedQuery } + +query predicate problems(Enum e, string message) { + not isExcluded(e, getQuery()) and + not e.hasExplicitUnderlyingType() and + message = "Base type of enumeration is not explicitly specified." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.qll b/cpp/common/src/codingstandards/cpp/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.qll new file mode 100644 index 0000000000..1989afbc8b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.qll @@ -0,0 +1,20 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Throwing an exception of pointer type can lead to use-after-free or memory leak + * issues. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class ExceptionObjectHavePointerTypeSharedQuery extends Query { } + +Query getQuery() { result instanceof ExceptionObjectHavePointerTypeSharedQuery } + +query predicate problems(Expr thrownExpr, string message) { + not isExcluded(thrownExpr, getQuery()) and + thrownExpr = any(ThrowExpr te).getExpr() and + thrownExpr.getType().getUnspecifiedType() instanceof PointerType and + message = "Exception object with pointer type " + thrownExpr.getType() + " is thrown here." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.qll b/cpp/common/src/codingstandards/cpp/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.qll index 16f86f78be..5a712dd522 100644 --- a/cpp/common/src/codingstandards/cpp/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.qll +++ b/cpp/common/src/codingstandards/cpp/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.qll @@ -4,43 +4,17 @@ */ import cpp -import semmle.code.cpp.controlflow.SubBasicBlocks import codingstandards.cpp.Exclusions import codingstandards.cpp.Customizations import codingstandards.cpp.exceptions.ExceptionFlow import codingstandards.cpp.ExceptionSafety import codingstandards.cpp.resources.ResourceManagement +import codingstandards.cpp.resources.ResourceLeakAnalysis abstract class ExceptionSafetyValidStateSharedQuery extends Query { } Query getQuery() { result instanceof ExceptionSafetyValidStateSharedQuery } -/** - * Ensures that `UncaughtThrowExpr` and `Expr` appear at the start of a `SubBasicBlock`. - */ -class SafetyValidStateSubBasicBlock extends SubBasicBlockCutNode { - SafetyValidStateSubBasicBlock() { - this instanceof ResourceAcquisitionExpr or - this = any(ResourceAcquisitionExpr rae).getReleaseExpr() or - this instanceof UncaughtThrowExpr - } -} - -/** - * Execution continues from an allocation expression - * without releasing the resource - */ -SubBasicBlock followsInitialized(ResourceAcquisitionExpr src) { - result = src - or - exists(SubBasicBlock mid | - mid = followsInitialized(src) and - result = mid.getASuccessor() and - //stop recursion on resource release - not result = src.getReleaseExpr() - ) -} - /** * `UncaughtThrowExpr` models a `throw` expression that is not handled */ @@ -48,14 +22,25 @@ class UncaughtThrowExpr extends ThrowExpr { UncaughtThrowExpr() { getASuccessor() = getEnclosingFunction() } } -query predicate problems( - UncaughtThrowExpr te, string message, ResourceAcquisitionExpr e, string eDescription -) { +module ThrowLeakConfig implements ResourceLeakConfigSig { + predicate isAllocate(ControlFlowNode node, DataFlow::Node resource) { + ResourceLeakConfig::isAllocate(node, resource) + } + + predicate isFree(ControlFlowNode node, DataFlow::Node resource) { + ResourceLeakConfig::isFree(node, resource) + } + + ControlFlowNode outOfScope(ControlFlowNode allocPoint) { + result.(UncaughtThrowExpr).getEnclosingFunction() = allocPoint.(Expr).getEnclosingFunction() + } + + DataFlow::Node getAnAlias(DataFlow::Node node) { DataFlow::localFlow(node, result) } +} + +query predicate problems(UncaughtThrowExpr te, string message, Element e, string eDescription) { not isExcluded(te, getQuery()) and - exists(SubBasicBlock sbb | - sbb.getANode() = e and - te = followsInitialized(sbb) - ) and + te = ResourceLeak::getALeak(e) and message = "The $@ is not released explicitly before throwing an exception." and eDescription = "allocated resource" } diff --git a/cpp/common/src/codingstandards/cpp/rules/exithandlerthrowsexception/ExitHandlerThrowsException.qll b/cpp/common/src/codingstandards/cpp/rules/exithandlerthrowsexception/ExitHandlerThrowsException.qll index 6d145bbf0a..3f1efdc18e 100644 --- a/cpp/common/src/codingstandards/cpp/rules/exithandlerthrowsexception/ExitHandlerThrowsException.qll +++ b/cpp/common/src/codingstandards/cpp/rules/exithandlerthrowsexception/ExitHandlerThrowsException.qll @@ -17,7 +17,7 @@ class ExitHandler extends ExceptionThrowingFunction { Call c; ExitHandler() { - c.getTarget().hasQualifiedName("std", ["atexit", "at_quick_exit"]) and + c.getTarget().hasGlobalOrStdName(["atexit", "at_quick_exit"]) and c.getArgument(0) = this.getAnAccess() } diff --git a/cpp/common/src/codingstandards/cpp/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.qll b/cpp/common/src/codingstandards/cpp/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.qll new file mode 100644 index 0000000000..960b4ba2b6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.qll @@ -0,0 +1,28 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Forwarding references and std::forward shall be used together. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.standardlibrary.Utility + +abstract class ForwardingReferencesAndForwardNotUsedTogetherSharedQuery extends Query { } + +Query getQuery() { result instanceof ForwardingReferencesAndForwardNotUsedTogetherSharedQuery } + +query predicate problems(FunctionCall c, string message, Parameter a, string a_string) { + not isExcluded(c, getQuery()) and + a_string = a.getName() and + a.getAnAccess() = c.getAnArgument() and + ( + c instanceof StdMoveCall and + a instanceof ForwardParameter and + message = "Function `std::forward` should be used for forwarding the forward reference $@." + or + c instanceof StdForwardCall and + a instanceof ConsumeParameter and + message = "Function `std::move` should be used for forwarding rvalue reference $@." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.qll b/cpp/common/src/codingstandards/cpp/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.qll new file mode 100644 index 0000000000..035c0a1f4a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/freememorywhennolongerneededshared/FreeMemoryWhenNoLongerNeededShared.qll @@ -0,0 +1,176 @@ +/** + * Provides a library which includes a `problems` predicate for reporting + * memory allocations which are allocated but not freed before they go out of scope. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import semmle.code.cpp.controlflow.StackVariableReachability +import codingstandards.cpp.Allocations +import semmle.code.cpp.pointsto.PointsTo + +predicate allocated(FunctionCall fc) { allocExpr(fc, _) } + +/** An expression for which there exists a function call that might free it. */ +class FreedExpr extends PointsToExpr { + FreedExpr() { freeExprOrIndirect(_, this, _) } + + override predicate interesting() { freeExprOrIndirect(_, this, _) } +} + +/** + * Holds if `fc` is a call to a function that allocates memory that might be freed. + */ +predicate mallocCallMayBeFreed(FunctionCall fc) { allocated(fc) and anythingPointsTo(fc) } + +/** + * '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 allocCallOrIndirect(Expr e) { + // direct memory allocation call + allocated(e) and + // We are only interested in memory allocation calls that are + // actually freed somehow, as MemoryNeverFreed + // will catch those that aren't. + mallocCallMayBeFreed(e) + or + exists(ReturnStmt rtn | + // indirect memory allocation call + mayCallFunction(e, rtn.getEnclosingFunction()) and + ( + // return memory allocation + allocCallOrIndirect(rtn.getExpr()) + or + // return variable assigned with allocated memory + exists(Variable v | + v = rtn.getExpr().(VariableAccess).getTarget() and + allocCallOrIndirect(v.getAnAssignedValue()) and + not assignedToFieldOrGlobal(v, _) + ) + ) + ) +} + +predicate allocDefinition(StackVariable v, ControlFlowNode def) { + exists(Expr expr | exprDefinition(v, def, expr) and allocCallOrIndirect(expr)) +} + +class MallocVariableReachability extends StackVariableReachabilityWithReassignment { + MallocVariableReachability() { this = "MallocVariableReachability" } + + override predicate isSourceActual(ControlFlowNode node, StackVariable v) { + allocDefinition(v, node) + } + + override predicate isSinkActual(ControlFlowNode node, StackVariable v) { + // node may be used in allocReaches + exists(node.(AnalysedExpr).getNullSuccessor(v)) or + freeExprOrIndirect(node, v.getAnAccess(), _) 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 malloc at `def` is still held in Variable `v` upon entering `node`. + */ +predicate mallocVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) { + exists(MallocVariableReachability r | + // reachability + r.reachesTo(def, _, node, v) + or + // accept def node itself + r.isSource(def, v) and + node = def + ) +} + +class MallocReachability extends StackVariableReachabilityExt { + MallocReachability() { this = "MallocReachability" } + + override predicate isSource(ControlFlowNode node, StackVariable v) { allocDefinition(v, node) } + + override predicate isSink(ControlFlowNode node, StackVariable v) { + v.getFunction() = node.(ReturnStmt).getEnclosingFunction() and + // exclude return statements that call a function and pass the pointer as an argument + not exists(Expr arg | + arg = node.(ReturnStmt).getExpr().(FunctionCall).getAnArgument() and + arg = v.getAnAccess() and + not dereferenced(arg) + ) + } + + override predicate isBarrier( + ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v + ) { + isSource(source, v) and + next = node.getASuccessor() and + // the memory (stored in any variable `v0`) allocated at `source` is freed or + // assigned to a global at node, or NULL checked on the edge node -> next. + exists(StackVariable v0 | mallocVariableReaches(v0, source, node) | + node.(AnalysedExpr).getNullSuccessor(v0) = next or + freeExprOrIndirect(node, v0.getAnAccess(), _) or + assignedToFieldOrGlobal(v0, node) + ) + } +} + +/** + * The value returned by alloc `def` has not been freed, confirmed to be null, + * or potentially leaked globally upon reaching `node` (regardless of what variable + * it's still held in, if any). + */ +predicate mallocReaches(ControlFlowNode def, ControlFlowNode node) { + exists(MallocReachability 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() +} + +abstract class FreeMemoryWhenNoLongerNeededSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof FreeMemoryWhenNoLongerNeededSharedSharedQuery } + +// note: this query is based on CloseFileHandleWhenNoLongerNeededShared.qll +query predicate problems(ControlFlowNode def, string message, Stmt ret, string retMsg) { + not isExcluded(def, getQuery()) and + message = "The memory allocated here may not be freed at $@." and + retMsg = "this location" and + ( + mallocReaches(def, ret) and + not exists(StackVariable v | + mallocVariableReaches(v, def, ret) and + ret.getAChild*() = v.getAnAccess() + ) + or + allocated(def) and + not mallocCallMayBeFreed(def) and + ret = def.getControlFlowScope().getEntryPoint() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.qll b/cpp/common/src/codingstandards/cpp/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.qll new file mode 100644 index 0000000000..93177e4f46 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.qll @@ -0,0 +1,62 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unchecked error values. + */ + +import cpp +import codingstandards.cpp.Customizations +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.controlflow.Guards +import codingstandards.cpp.Exclusions + +abstract class FunctionErroneousReturnValueNotTestedSharedQuery extends Query { } + +Query getQuery() { result instanceof FunctionErroneousReturnValueNotTestedSharedQuery } + +query predicate problems(FunctionCall fc, string message) { + not isExcluded(fc, getQuery()) and + fc.getTarget() + .hasGlobalOrStdName([ + // fcntl.h + "open", "openat", "fcntl", "creat", + // locale.h + "setlocale", + // stdlib.h + "system", "getenv", "getenv_s", + // signal.h + "signal", "raise", + // setjmp.h + "setjmp", + // stdio.h + "fopen", "fopen_s", "freopen", "freopen_s", "fclose", "fcloseall", "fflush", "setvbuf", + "fgetc", "getc", "fgets", "fputc", "getchar", "gets", "gets_s", "putchar", "puts", + "ungetc", "scanf", "fscanf", "sscanf", "scanf_s", "fscanf_s", "sscanf_s", "vscanf", + "vfscanf", "vsscanf", "vscanf_s", "vfscanf_s", "vsscanf_s", "printf", "fprintf", + "sprintf", "snprintf", "printf_s", "fprintf_s", "sprintf_s", "snprintf_s", "vprintf", + "vfprintf", "vsprintf", "vsnprintf", "vprintf_s", "vfprintf_s", "vsprintf_s", + "vsnprintf_s", "ftell", "fgetpos", "fseek", "fsetpos", "remove", "rename", "tmpfile", + "tmpfile_s", "tmpnam", "tmpnam_s", + // string.h + "strcpy_s", "strncpy_s", "strcat_s", "strncat_s", "memset_s", "memcpy_s", "memmove_s", + "strerror_s", + // threads.h + "thrd_create", "thrd_sleep", "thrd_detach", "thrd_join", "mtx_init", "mtx_lock", + "mtx_timedlock", "mtx_trylock", "mtx_unlock", "cnd_init", "cnd_signal", "cnd_broadcast", + "cnd_wait", "cnd_timedwait", "tss_create", "tss_get", "tss_set", + // time.h + "time", "clock", "timespec_get", "asctime_s", "ctime_s", "gmtime", "gmtime_s", + "localtime", "localtime_s", + // unistd.h + "write", "read", "close", "unlink", + // wchar.h + "fgetwc", "getwc", "fgetws", "fputwc", "putwc", "fputws", "getwchar", "putwchar", + "ungetwc", "wscanf", "fwscanf", "swscanf", "wscanf_s", "fwscanf_s", "swscanf_s", + "vwscanf", "vfwscanf", "vswscanf", "vwscanf_s", "vfwscanf_s", "vswscanf_s", "wprintf", + "fwprintf", "swprintf", "wprintf_s", "fwprintf_s", "swprintf_s", "snwprintf_s", + "vwprintf", "vfwprintf", "vswprintf", "vwprintf_s", "vfwprintf_s", "vswprintf_s", + "vsnwprintf_s" + ]) and + not exists(GuardCondition gc | + DataFlow::localFlow(DataFlow::exprNode(fc), DataFlow::exprNode(gc.getAChild*())) + ) and + message = "Return value from " + fc.getTarget().getName() + " is not tested for errors." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.qll b/cpp/common/src/codingstandards/cpp/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.qll new file mode 100644 index 0000000000..73e4181640 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.qll @@ -0,0 +1,31 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Function-like macros shall not be defined. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.IrreplaceableFunctionLikeMacro + +abstract class FunctionLikeMacrosDefinedSharedQuery extends Query { } + +Query getQuery() { result instanceof FunctionLikeMacrosDefinedSharedQuery } + +predicate partOfConstantExpr(MacroInvocation i) { + exists(Expr e | + e.isConstant() and + not i.getExpr() = e and + i.getExpr().getParent+() = e + ) +} + +query predicate problems(FunctionLikeMacro m, string message) { + not isExcluded(m, getQuery()) and + not m instanceof IrreplaceableFunctionLikeMacro and + //macros can have empty body + not m.getBody().length() = 0 and + //function call not allowed in a constant expression (where constant expr is parent) + forall(MacroInvocation i | i = m.getAnInvocation() | not partOfConstantExpr(i)) and + message = "Macro used instead of a function." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.qll b/cpp/common/src/codingstandards/cpp/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.qll index 2b910612cb..bb54a31df6 100644 --- a/cpp/common/src/codingstandards/cpp/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.qll +++ b/cpp/common/src/codingstandards/cpp/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.qll @@ -5,20 +5,30 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions +import codingstandards.cpp.Noreturn abstract class FunctionNoReturnAttributeConditionSharedQuery extends Query { } Query getQuery() { result instanceof FunctionNoReturnAttributeConditionSharedQuery } +/** + * `noreturn` functions are declared differently in c/c++. Attempt to match + * the description to the file; low risk if it chooses incorrectly. + */ +string describeNoreturn(Function f) { + if f.getFile().getExtension() = ["c", "C", "h", "H"] + then result = "_Noreturn" + else result = "[[noreturn]]" +} + /** * This checks that the return statement is reachable from the function entry point */ -query predicate problems(Function f, string message) { +query predicate problems(NoreturnFunction f, string message) { not isExcluded(f, getQuery()) and - f.getAnAttribute().getName() = "noreturn" and - exists(ReturnStmt s | - f = s.getEnclosingFunction() and - s.getBasicBlock().isReachable() - ) and - message = "The function " + f.getName() + " declared with attribute [[noreturn]] returns a value." + mayReturn(f) and + not f.isCompilerGenerated() and + message = + "The function " + f.getName() + " declared with attribute " + describeNoreturn(f) + + " returns a value." } diff --git a/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll b/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll new file mode 100644 index 0000000000..e54e4378e9 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll @@ -0,0 +1,35 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Using recursive functions can lead to stack overflows and limit scalability and + * portability of the program. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class FunctionsCallThemselvesEitherDirectlyOrIndirectlySharedQuery extends Query { } + +Query getQuery() { result instanceof FunctionsCallThemselvesEitherDirectlyOrIndirectlySharedQuery } + +class RecursiveCall extends FunctionCall { + RecursiveCall() { + this.getTarget().calls*(this.getEnclosingFunction()) and + not this.getTarget().hasSpecifier("is_constexpr") + } +} + +class RecursiveFunction extends Function { + RecursiveFunction() { exists(RecursiveCall fc | fc.getEnclosingFunction() = this) } +} + +query predicate problems(FunctionCall fc, string message, RecursiveFunction f, string functionName) { + not isExcluded(fc, getQuery()) and + f = fc.getTarget() and + functionName = f.getName() and + if f = fc.getEnclosingFunction() + then message = "This call directly invokes its containing function $@." + else + message = + "The function " + fc.getEnclosingFunction() + " is indirectly recursive via this call to $@." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.qll b/cpp/common/src/codingstandards/cpp/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.qll new file mode 100644 index 0000000000..d0f98d233e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.qll @@ -0,0 +1,21 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Function templates shall not be explicitly specialized. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class FunctionTemplatesExplicitlySpecializedSharedQuery extends Query { } + +Query getQuery() { result instanceof FunctionTemplatesExplicitlySpecializedSharedQuery } + +query predicate problems( + FunctionTemplateSpecialization f, string message, TemplateFunction tf, string tf_string +) { + not isExcluded(f, getQuery()) and + tf = f.getPrimaryTemplate() and + tf_string = f.getPrimaryTemplate().getFile().getBaseName() and + message = "Specialization of function template from primary template located in $@." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.qll b/cpp/common/src/codingstandards/cpp/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.qll new file mode 100644 index 0000000000..3f6ce2786a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/functiontypesnotinprototypeformshared/FunctionTypesNotInPrototypeFormShared.qll @@ -0,0 +1,53 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The use of non-prototype format parameter type declarators is an obsolescent + * language feature. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Identifiers + +abstract class FunctionTypesNotInPrototypeFormSharedSharedQuery extends Query { } + +/** + * `Parameter`s without names + */ +class UnnamedParameter extends Parameter { + UnnamedParameter() { not this.isNamed() } +} + +/* + * This is a copy of the private `hasZeroParamDecl` predicate from the standard set of + * queries as of the `codeql-cli/2.11.2` tag in `github/codeql`. + */ + +predicate hasZeroParamDecl(Function f) { + exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() | + not fde.isImplicit() and + not fde.hasVoidParamList() and + fde.getNumberOfParameters() = 0 and + not fde.isDefinition() + ) +} + +Query getQuery() { result instanceof FunctionTypesNotInPrototypeFormSharedSharedQuery } + +query predicate problems(Function f, string msg) { + not isExcluded(f, getQuery()) and + f instanceof InterestingIdentifiers and + ( + f.getAParameter() instanceof UnnamedParameter and + msg = "Function " + f + " declares parameter that is unnamed." + or + hasZeroParamDecl(f) and + msg = "Function " + f + " does not specify void for no parameters present." + or + //parameters declared in declaration list (not in function signature) + //have no prototype + not f.isPrototyped() and + not hasZeroParamDecl(f) and + msg = "Function " + f + " declares parameter in unsupported declaration list." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.qll b/cpp/common/src/codingstandards/cpp/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.qll new file mode 100644 index 0000000000..285ccd909a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.qll @@ -0,0 +1,23 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The only declarations in the global namespace should be main, namespace declarations + * and extern "C" declarations. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class GlobalNamespaceDeclarationsSharedQuery extends Query { } + +Query getQuery() { result instanceof GlobalNamespaceDeclarationsSharedQuery } + +query predicate problems(DeclarationEntry e, string message) { + not isExcluded(e, getQuery()) and + e.getDeclaration().getNamespace() instanceof GlobalNamespace and + e.getDeclaration().isTopLevel() and + not exists(Function f | f = e.getDeclaration() | f.hasGlobalName("main") or f.hasCLinkage()) and + message = + "Declaration " + e.getName() + + " is in the global namespace and is not a main, a namespace, or an extern \"C\" declaration." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.qll b/cpp/common/src/codingstandards/cpp/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.qll new file mode 100644 index 0000000000..c445c06253 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.qll @@ -0,0 +1,24 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * If a project has the unsized version of operator 'delete' globally defined, then the + * sized version shall be defined. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.OperatorDelete + +abstract class GlobalSizedOperatorDeleteNotDefinedSharedQuery extends Query { } + +Query getQuery() { result instanceof GlobalSizedOperatorDeleteNotDefinedSharedQuery } + +query predicate problems(OperatorDelete unsized_delete, string message) { + not isExcluded(unsized_delete, getQuery()) and + not unsized_delete.isSizeDelete() and + not exists(OperatorDelete od | unsized_delete.isNoThrowDelete() = od.isNoThrowDelete() | + od.isSizeDelete() + ) and + message = + "Unsized function '" + unsized_delete.getName() + "' defined globally without sized version." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.qll b/cpp/common/src/codingstandards/cpp/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.qll new file mode 100644 index 0000000000..b99887ee03 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.qll @@ -0,0 +1,24 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * If a project has the sized version of operator 'delete' globally defined, then the + * unsized version shall be defined. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.OperatorDelete + +abstract class GlobalUnsizedOperatorDeleteNotDefinedSharedQuery extends Query { } + +Query getQuery() { result instanceof GlobalUnsizedOperatorDeleteNotDefinedSharedQuery } + +query predicate problems(OperatorDelete sized_delete, string message) { + not isExcluded(sized_delete, getQuery()) and + sized_delete.isSizeDelete() and + not exists(OperatorDelete od | sized_delete.isNoThrowDelete() = od.isNoThrowDelete() | + not od.isSizeDelete() + ) and + message = + "Sized function '" + sized_delete.getName() + "' defined globally without unsized version." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.qll b/cpp/common/src/codingstandards/cpp/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.qll new file mode 100644 index 0000000000..f329fff12d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.qll @@ -0,0 +1,60 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A goto statement shall reference a label in a surrounding block. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class GotoReferenceALabelInSurroundingBlockSharedQuery extends Query { } + +Query getQuery() { result instanceof GotoReferenceALabelInSurroundingBlockSharedQuery } + +predicate isPartOfSwitch(Stmt goto) { + exists(SwitchStmt switch | switch.getStmt() = goto.getParent()) +} + +SwitchCase getSwitchCase(Stmt stmt) { + exists(int index, SwitchStmt switch | + getStmtInSwitch(switch, stmt, index) and getStmtInSwitch(switch, result, index - 1) + ) + or + exists(int index, SwitchStmt switch, Stmt other | + getStmtInSwitch(switch, stmt, index) and + getStmtInSwitch(switch, other, index - 1) and + not other instanceof SwitchCase and + result = getSwitchCase(other) + ) +} + +predicate getStmtInSwitch(SwitchStmt switch, Stmt s, int index) { + switch.getStmt().(BlockStmt).getStmt(index) = s +} + +int statementDepth(Stmt statement) { + statement.getParent() = statement.getEnclosingFunction().getBlock() and result = 1 + or + statementDepth(statement.getParent()) + 1 = result +} + +query predicate problems(GotoStmt goto, string message, Stmt target, string target_string) { + not isExcluded(goto, getQuery()) and + exists(int gotoDepth, int targetDepth | + goto.getTarget() = target and + gotoDepth = statementDepth(goto) and + targetDepth = statementDepth(target) and + targetDepth >= gotoDepth and + ( + targetDepth = gotoDepth + implies + ( + not isPartOfSwitch(goto) and not goto.getParent() = target.getParent() + or + isPartOfSwitch(goto) and not getSwitchCase(goto) = getSwitchCase(target) + ) + ) and + target_string = "label" and + message = "The goto statement and its $@ are not declared or enclosed in the same block." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/gotostatementcondition/GotoStatementCondition.qll b/cpp/common/src/codingstandards/cpp/rules/gotostatementcondition/GotoStatementCondition.qll new file mode 100644 index 0000000000..74c6abbade --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/gotostatementcondition/GotoStatementCondition.qll @@ -0,0 +1,36 @@ +/** + * Provides a library which includes a `problems` predicate for reporting goto statements that jump to labels + * declared later in the same funciton. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class GotoStatementConditionSharedQuery extends Query { } + +Query getQuery() { result instanceof GotoStatementConditionSharedQuery } + +query predicate problems( + GotoStmt goto, string message, GotoStmt gotoLocation, string gotoLabel, Stmt target, + string targetLabel +) { + not isExcluded(goto, getQuery()) and + target = goto.getTarget() and + exists(Location targetLoc, Location gotoLoc | + targetLoc = target.getLocation() and + gotoLoc = goto.getLocation() and + targetLoc.getFile() = gotoLoc.getFile() + | + // Starts on a previous line + targetLoc.getStartLine() < gotoLoc.getEndLine() + or + // Starts on the same line, but an earlier column + targetLoc.getStartLine() = gotoLoc.getEndLine() and + targetLoc.getEndColumn() < gotoLoc.getStartColumn() + ) and + goto = gotoLocation and + message = "The $@ statement jumps to a $@ that is not declared later in the same function." and + gotoLabel = goto.getName() and + targetLabel = target.toString() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.qll b/cpp/common/src/codingstandards/cpp/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.qll new file mode 100644 index 0000000000..6a13ea083c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.qll @@ -0,0 +1,18 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The goto statement shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class GotoStatementShouldNotBeUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof GotoStatementShouldNotBeUsedSharedQuery } + +query predicate problems(Stmt s, string message) { + not isExcluded(s, getQuery()) and + (s instanceof GotoStmt or s instanceof ComputedGotoStmt) and + message = "Use of goto." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.qll b/cpp/common/src/codingstandards/cpp/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.qll index ccc0a23460..8d859f726d 100644 --- a/cpp/common/src/codingstandards/cpp/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.qll +++ b/cpp/common/src/codingstandards/cpp/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.qll @@ -1,5 +1,6 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * Exceptions thrown before main begins executing cannot be caught. */ import cpp diff --git a/cpp/common/src/codingstandards/cpp/rules/hashoperatorsused/HashOperatorsUsed.qll b/cpp/common/src/codingstandards/cpp/rules/hashoperatorsused/HashOperatorsUsed.qll index ae825e926f..d9f63887a6 100644 --- a/cpp/common/src/codingstandards/cpp/rules/hashoperatorsused/HashOperatorsUsed.qll +++ b/cpp/common/src/codingstandards/cpp/rules/hashoperatorsused/HashOperatorsUsed.qll @@ -2,9 +2,9 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -abstract class HashOperatorsUsedQuery extends Query { } +abstract class HashOperatorsUsedSharedQuery extends Query { } -Query getQuery() { result instanceof HashOperatorsUsedQuery } +Query getQuery() { result instanceof HashOperatorsUsedSharedQuery } query predicate problems(Macro m, string message) { exists(string body | diff --git a/cpp/common/src/codingstandards/cpp/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.qll b/cpp/common/src/codingstandards/cpp/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.qll new file mode 100644 index 0000000000..1c371da20c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.qll @@ -0,0 +1,58 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A non-overriding member function definition that hides an inherited member function + * can result in unexpected behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Class + +abstract class HiddenInheritedNonOverridableMemberFunctionSharedQuery extends Query { } + +Query getQuery() { result instanceof HiddenInheritedNonOverridableMemberFunctionSharedQuery } + +/** + * Holds if the class has a non-virtual member function with the given name. + */ +pragma[noinline, nomagic] +predicate hasNonVirtualMemberFunction(Class clazz, MemberFunction mf, string name) { + mf.getDeclaringType() = clazz and + mf.getName() = name and + not mf.isVirtual() and + // Exclude private member functions, which cannot be inherited. + not mf.isPrivate() +} + +/** + * Holds if the member function is in a class with the given base class, and has the given name. + */ +pragma[noinline, nomagic] +predicate hasDeclarationBaseClass(MemberFunction mf, Class baseClass, string functionName) { + baseClass = mf.getDeclaringType().getABaseClass() and + functionName = mf.getName() +} + +query predicate problems( + MemberFunction overridingDecl, string message, MemberFunction hiddenDecl, string hiddenDecl_string +) { + exists(Class baseClass, string name | + not isExcluded(overridingDecl, getQuery()) and // Check if we are overriding a non-virtual inherited member function + hasNonVirtualMemberFunction(baseClass, hiddenDecl, name) and + hasDeclarationBaseClass(overridingDecl, baseClass, name) and + // Where the hidden member function isn't explicitly brought in scope through a using declaration. + not exists(UsingDeclarationEntry ude | + ude.getDeclaration() = hiddenDecl and + ude.getEnclosingElement() = overridingDecl.getDeclaringType() + ) and + // Exclude compiler generated member functions which include things like copy constructor that hide base class + // copy constructors. + not overridingDecl.isCompilerGenerated() and + // Exclude special member functions, which cannot be inherited. + not overridingDecl instanceof SpecialMemberFunction and + message = + "Declaration for member '" + name + "' hides non-overridable inherited member function $@" and + hiddenDecl_string = hiddenDecl.getName() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.qll b/cpp/common/src/codingstandards/cpp/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.qll new file mode 100644 index 0000000000..ef99e01973 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.qll @@ -0,0 +1,56 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * An overriding member function definition thats hides an overload of the overridden + * inherited member function can result in unexpected behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class HiddenInheritedOverridableMemberFunctionSharedQuery extends Query { } + +Query getQuery() { result instanceof HiddenInheritedOverridableMemberFunctionSharedQuery } + +query predicate problems( + FunctionDeclarationEntry overridingDecl, string message, FunctionDeclarationEntry hiddenDecl, + string hiddenDecl_string +) { + not isExcluded(overridingDecl, getQuery()) and + // Check if we are overriding a virtual inherited member function + hiddenDecl.getDeclaration().isVirtual() and + // Exclude private member functions, which cannot be inherited. + not hiddenDecl.getDeclaration().(MemberFunction).isPrivate() and + // The overriding declaration hides the hidden declaration if: + ( + // 1. the overriding declaration overrides a function in a base class that is an overload of the hidden declaration + // and the hidden declaration isn't overriden in the same class. + exists(FunctionDeclarationEntry overridenDecl | + overridingDecl.getDeclaration().(MemberFunction).overrides(overridenDecl.getDeclaration()) and + overridenDecl.getDeclaration().getAnOverload() = hiddenDecl.getDeclaration() and + not exists(MemberFunction overridingFunc | + hiddenDecl.getDeclaration().(MemberFunction).getAnOverridingFunction() = overridingFunc and + overridingFunc.getDeclaringType() = overridingDecl.getDeclaration().getDeclaringType() + ) + ) and + // and the hidden declaration isn't explicitly brought in scope through a using declaration. + not exists(UsingDeclarationEntry ude | + ude.getDeclaration() = hiddenDecl.getDeclaration() and + ude.getEnclosingElement() = overridingDecl.getDeclaration().getDeclaringType() + ) + or + // 2. if the overriding declaration doesn't override a base member function but has the same name + // as the hidden declaration + not overridingDecl.getDeclaration().(MemberFunction).overrides(_) and + overridingDecl.getName() = hiddenDecl.getName() and + overridingDecl.getDeclaration().getDeclaringType().getABaseClass() = + hiddenDecl.getDeclaration().getDeclaringType() + ) and + // Limit the results to the declarations and not the definitions, if any. + (overridingDecl.getDeclaration().hasDefinition() implies not overridingDecl.isDefinition()) and + (hiddenDecl.getDeclaration().hasDefinition() implies not hiddenDecl.isDefinition()) and + message = + "Declaration for member '" + overridingDecl.getName() + + "' hides overridable inherited member function $@" and + hiddenDecl_string = hiddenDecl.getName() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll b/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll index b3cadd6d2a..39d24299b8 100644 --- a/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll +++ b/cpp/common/src/codingstandards/cpp/rules/identifierhidden/IdentifierHidden.qll @@ -1,5 +1,8 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * Use of an identifier declared in an inner scope with an identical name to an + * identifier in an outer scope can lead to inadvertent errors if the incorrect + * identifier is modified. */ import cpp @@ -11,10 +14,16 @@ abstract class IdentifierHiddenSharedQuery extends Query { } Query getQuery() { result instanceof IdentifierHiddenSharedQuery } -query predicate problems(UserVariable v2, string message, UserVariable v1, string varName) { - not isExcluded(v1, getQuery()) and - not isExcluded(v2, getQuery()) and - hides(v1, v2) and - varName = v1.getName() and +query predicate problems( + UserVariable innerDecl, string message, UserVariable outerDecl, string varName +) { + not isExcluded(outerDecl, getQuery()) and + not isExcluded(innerDecl, getQuery()) and + //ignore template variables for this rule + not outerDecl instanceof TemplateVariable and + not innerDecl instanceof TemplateVariable and + hidesStrict(outerDecl, innerDecl) and + not excludedViaNestedNamespaces(outerDecl, innerDecl) and + varName = outerDecl.getName() and message = "Variable is hiding variable $@." } diff --git a/cpp/common/src/codingstandards/cpp/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.qll b/cpp/common/src/codingstandards/cpp/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.qll new file mode 100644 index 0000000000..806315b43c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.qll @@ -0,0 +1,39 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * An identifier with multiple definitions in different translation units + * leads to undefined behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Linkage + +abstract class IdentifierWithExternalLinkageOneDefinitionSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof IdentifierWithExternalLinkageOneDefinitionSharedSharedQuery } + +query predicate problems(DeclarationEntry de1, string message, DeclarationEntry de2, string de2Str) { + exists(Declaration d | + not isExcluded(de1, getQuery()) and + hasExternalLinkage(d) and + d.isTopLevel() and + d = de1.getDeclaration() and + d = de2.getDeclaration() and + de1 != de2 and + de1.isDefinition() and + de2.isDefinition() and + // exceptions + not d instanceof TemplateClass and + (d instanceof Function implies not d.(Function).isInline()) and + // Apply an ordering based on location to enforce that (de1, de2) = (de2, de1) and we only report (de1, de2). + ( + de1.getFile().getAbsolutePath() < de2.getFile().getAbsolutePath() + or + de1.getFile().getAbsolutePath() = de2.getFile().getAbsolutePath() and + de1.getLocation().getStartLine() < de2.getLocation().getStartLine() + ) and + message = "The identifier " + de1.getName() + " has external linkage and is redefined $@." and + de2Str = "here" + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/ifelseterminationconstruct/IfElseTerminationConstruct.qll b/cpp/common/src/codingstandards/cpp/rules/ifelseterminationconstruct/IfElseTerminationConstruct.qll new file mode 100644 index 0000000000..3c6f25e151 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/ifelseterminationconstruct/IfElseTerminationConstruct.qll @@ -0,0 +1,23 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The final else statement is a defensive programming technique. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class IfElseTerminationConstructSharedQuery extends Query { } + +Query getQuery() { result instanceof IfElseTerminationConstructSharedQuery } + +query predicate problems(IfStmt ifStmt, string message, IfStmt ifLocation, string ifElseString) { + not isExcluded(ifStmt, getQuery()) and + exists(IfStmt ifElse | + ifStmt.getElse() = ifElse and + not ifElse.hasElse() + ) and + ifLocation = ifStmt and + message = "The $@ construct does not terminate with else statement." and + ifElseString = "`if...else`" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.qll b/cpp/common/src/codingstandards/cpp/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.qll new file mode 100644 index 0000000000..16d8fd47ec --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.qll @@ -0,0 +1,17 @@ +/** + * Provides a library which includes a `problems` predicate for reporting potential information leakage across trust boundaries, relating to uninitialized memory in structs. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.trustboundary.UninitializedField + +abstract class InformationLeakageAcrossBoundariesSharedQuery extends Query { } + +Query getQuery() { result instanceof InformationLeakageAcrossBoundariesSharedQuery } + +query predicate problems(Element e, string message) { + uninitializedFieldQuery(e, message) and + not isExcluded(e, getQuery()) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.qll b/cpp/common/src/codingstandards/cpp/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.qll new file mode 100644 index 0000000000..ffb08283cd --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.qll @@ -0,0 +1,48 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * All constructors of a class should explicitly initialize all of its virtual base + * classes and immediate base classes. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Constructor + +abstract class InitializeAllVirtualBaseClassesSharedQuery extends Query { } + +Query getQuery() { result instanceof InitializeAllVirtualBaseClassesSharedQuery } + +query predicate problems( + Constructor c, string message, Class declaringType, string declaringType_string, Class baseClass, + string baseClass_string +) { + exists(string type | + not isExcluded(c, getQuery()) and + declaringType = c.getDeclaringType() and + ( + declaringType.getABaseClass() = baseClass and type = "" + or + baseClass.(VirtualBaseClass).getAVirtuallyDerivedClass().getADerivedClass+() = declaringType and + type = " virtual" + ) and + // There is not an initializer on the constructor for this particular base class + not exists(ConstructorBaseClassInit init | + c.getAnInitializer() = init and + init.getInitializedClass() = baseClass and + not init.isCompilerGenerated() + ) and + // Must be a defined constructor + c.hasDefinition() and + // Not a compiler-generated constructor + not c.isCompilerGenerated() and + // Not a defaulted constructor + not c.isDefaulted() and + // Not a deleted constructor + not c.isDeleted() and + declaringType_string = declaringType.getSimpleName() and + baseClass_string = baseClass.getSimpleName() and + message = + "Constructor for $@ does not explicitly call constructor for" + type + " base class $@." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.qll b/cpp/common/src/codingstandards/cpp/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.qll new file mode 100644 index 0000000000..e9579fcfba --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.qll @@ -0,0 +1,65 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A class shall only define an initializer-list constructor when it is the only + * constructor. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class InitializerListConstructorIsTheOnlyConstructorSharedQuery extends Query { } + +Query getQuery() { result instanceof InitializerListConstructorIsTheOnlyConstructorSharedQuery } + +class StdInitializerList extends Class { + StdInitializerList() { hasQualifiedName("std", "initializer_list") } +} + +/** + * An _initializer-list constructor_ according to `[dcl.init.list]`. + * + * A `Constructor` where the first parameter refers to `std::initializer_list`, and any remaining + * parameters have default arguments. + */ +class InitializerListConstructor extends Constructor { + InitializerListConstructor() { + // The first parameter is a `std::intializer_list` parameter + exists(Type firstParamType | firstParamType = getParameter(0).getType() | + // Either directly `std::initializer_list` + firstParamType instanceof StdInitializerList + or + //A reference to `std::initializer_list` + firstParamType.(ReferenceType).getBaseType().getUnspecifiedType() instanceof + StdInitializerList + ) and + // All parameters other than the fi + forall(Parameter other | other = getParameter([1 .. (getNumberOfParameters() - 1)]) | + exists(other.getInitializer()) + ) + } +} + +query predicate problems( + Constructor c, string message, InitializerListConstructor stdInitializerConstructor, + string stdInitializerConstructor_string +) { + exists(string paramList | + not isExcluded(c, getQuery()) and + // Not an initializer-list constructor + not c instanceof InitializerListConstructor and + // Constructor is not a special member function constructor + not c instanceof CopyConstructor and + not c instanceof MoveConstructor and + not c.getNumberOfParameters() = 0 and // default constructor + // And there is an initalizer-list constructor + stdInitializerConstructor = c.getDeclaringType().getAConstructor() and + // Determine the parameter type list of the constructor + paramList = + concat(string parameter | parameter = c.getAParameter().getType().getName() | parameter, ",") and + message = + "The constructor " + c.getQualifiedName() + "(" + paramList + + ") may be ignored in favour of $@ when using braced initialization." and + stdInitializerConstructor_string = "the constructor accepting std::initializer_list" + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.qll b/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.qll index 3949ff50a8..50b27d819d 100644 --- a/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.qll +++ b/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.qll @@ -45,6 +45,12 @@ predicate incompatibleFunctions(GetenvFunction f1, GetenvFunction f2) { or f1.getName() = ["setlocale", "localeconv"] and f2.getName() = ["setlocale", "localeconv"] + or + f1.getName() = ["asctime", "ctime"] and + f2.getName() = ["asctime", "ctime"] + or + f1.getName() = ["gmtime", "localtime"] and + f2.getName() = ["gmtime", "localtime"] } query predicate problems( @@ -63,10 +69,10 @@ query predicate problems( // The two calls are incompatible fc1 != fc2 and incompatibleFunctions(fc1.getTarget(), fc2.getFunction()) and - // The pointer returned by fc1 accessed in `e` afer the second `GetenvFunctionCall` + // The pointer returned by fc1 accessed in `e` after the second `GetenvFunctionCall` DataFlow::localExprFlow(fc1, e) and e = fc2.getASuccessor+() and - message = "This pointer was returned by a $@ and may have been overwritten by the susequent $@." and + message = "This pointer was returned by a $@ and may have been overwritten by the subsequent $@." and fc1text = fc1.toString() and fc2text = fc2.toString() } diff --git a/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.qll b/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.qll index 8bc1b0c920..759bc08deb 100644 --- a/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.qll +++ b/cpp/common/src/codingstandards/cpp/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.qll @@ -6,7 +6,6 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.rules.invalidatedenvstringpointers.InvalidatedEnvStringPointers as EnvString abstract class InvalidatedEnvStringPointersWarnSharedQuery extends Query { } diff --git a/cpp/common/src/codingstandards/cpp/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.qll b/cpp/common/src/codingstandards/cpp/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.qll index 89f847c5aa..b26421c72c 100644 --- a/cpp/common/src/codingstandards/cpp/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.qll +++ b/cpp/common/src/codingstandards/cpp/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.qll @@ -51,23 +51,26 @@ class WriteFunctionCall extends ReadWriteCall { } } -pragma[inline] +bindingset[a, b] +pragma[inline_late] predicate sameSource(FunctionCall a, FunctionCall b) { sameStreamSource(a, b) or sameFileSource(a, b) } +bindingset[a, b] predicate sameAccessDirection(ReadWriteCall a, ReadWriteCall b) { a.getAccessDirection() = b.getAccessDirection() } +bindingset[a, b] predicate oppositeAccessDirection(ReadWriteCall a, ReadWriteCall b) { not sameAccessDirection(a, b) } /** * A write operation reaching a read and vice versa - * without intervening filepositioning + * without intervening file positioning calls. */ ControlFlowNode reachesInExOperator(ReadWriteCall op) { result = op diff --git a/cpp/common/src/codingstandards/cpp/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.qll b/cpp/common/src/codingstandards/cpp/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.qll new file mode 100644 index 0000000000..5ccbe83c72 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/joinordetachthreadonlyonce/JoinOrDetachThreadOnlyOnce.qll @@ -0,0 +1,49 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Joining or detaching a previously joined or detached thread can lead to undefined + * program behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Concurrency + +abstract class JoinOrDetachThreadOnlyOnceSharedQuery extends Query { } + +Query getQuery() { result instanceof JoinOrDetachThreadOnlyOnceSharedQuery } + +// 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 +query predicate problems(C11ThreadCreateCall tcc, string message) { + not isExcluded(tcc, getQuery()) and + message = "Thread may call join or detach after the thread is joined or detached." 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 + ) + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/linesplicingusedincomments/LineSplicingUsedInComments.qll b/cpp/common/src/codingstandards/cpp/rules/linesplicingusedincomments/LineSplicingUsedInComments.qll new file mode 100644 index 0000000000..52dcf9a3f1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/linesplicingusedincomments/LineSplicingUsedInComments.qll @@ -0,0 +1,19 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Entering a newline following a '\\' character can erroneously commenting out + * regions of code. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class LineSplicingUsedInCommentsSharedQuery extends Query { } + +Query getQuery() { result instanceof LineSplicingUsedInCommentsSharedQuery } + +query predicate problems(CppStyleComment c, string message) { + not isExcluded(c, getQuery()) and + exists(c.getContents().regexpFind("\\\n", _, _)) and + message = "C++ comment includes \\ as the last character of a line" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/loopcompoundcondition/LoopCompoundCondition.qll b/cpp/common/src/codingstandards/cpp/rules/loopcompoundcondition/LoopCompoundCondition.qll new file mode 100644 index 0000000000..b71c193c8e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/loopcompoundcondition/LoopCompoundCondition.qll @@ -0,0 +1,19 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * If the body of a loop is not enclosed in braces, then this can lead to incorrect + * execution, and hard for developers to maintain. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class LoopCompoundConditionSharedQuery extends Query { } + +Query getQuery() { result instanceof LoopCompoundConditionSharedQuery } + +query predicate problems(Loop loop, string message) { + not isExcluded(loop, getQuery()) and + not loop.getStmt() instanceof BlockStmt and + message = "Loop body not enclosed within braces." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.qll b/cpp/common/src/codingstandards/cpp/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.qll new file mode 100644 index 0000000000..b12cddba0b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.qll @@ -0,0 +1,20 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The lowercase form of L shall not be used as the first character in a literal + * suffix. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Literals + +abstract class LowercaseLStartsInLiteralSuffixSharedQuery extends Query { } + +Query getQuery() { result instanceof LowercaseLStartsInLiteralSuffixSharedQuery } + +query predicate problems(IntegerLiteral l, string message) { + not isExcluded(l, getQuery()) and + exists(l.getValueText().indexOf("l")) and + message = "Lowercase 'l' used as a literal suffix." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/macrooffsetofused/MacroOffsetofUsed.qll b/cpp/common/src/codingstandards/cpp/rules/macrooffsetofused/MacroOffsetofUsed.qll new file mode 100644 index 0000000000..b475dc655e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/macrooffsetofused/MacroOffsetofUsed.qll @@ -0,0 +1,18 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The macro offsetof shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class MacroOffsetofUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof MacroOffsetofUsedSharedQuery } + +query predicate problems(MacroInvocation mi, string message) { + not isExcluded(mi, getQuery()) and + mi.getMacroName() = "offsetof" and + message = "Use of banned macro " + mi.getMacroName() + "." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/macroparameterfollowinghash/MacroParameterFollowingHash.qll b/cpp/common/src/codingstandards/cpp/rules/macroparameterfollowinghash/MacroParameterFollowingHash.qll new file mode 100644 index 0000000000..ae20368e67 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/macroparameterfollowinghash/MacroParameterFollowingHash.qll @@ -0,0 +1,24 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A macro parameter immediately following a # operator shall not be immediately + * followed by a ## operator. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Macro + +abstract class MacroParameterFollowingHashSharedQuery extends Query { } + +Query getQuery() { result instanceof MacroParameterFollowingHashSharedQuery } + +query predicate problems(Macro m, string message) { + not isExcluded(m, getQuery()) and + exists(StringizingOperator one, TokenPastingOperator two | + one.getMacro() = m and + two.getMacro() = m and + one.getOffset() < two.getOffset() + ) and + message = "Macro definition uses an # operator followed by a ## operator." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.qll b/cpp/common/src/codingstandards/cpp/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.qll index 693ae36906..314d1dbe4c 100644 --- a/cpp/common/src/codingstandards/cpp/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.qll +++ b/cpp/common/src/codingstandards/cpp/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.qll @@ -1,5 +1,8 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * In the definition of a function-like macro, each instance of a parameter + * shall be enclosed in parentheses, otherwise the result of preprocessor macro + * substitition may not be as expected. */ import cpp diff --git a/cpp/common/src/codingstandards/cpp/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.qll b/cpp/common/src/codingstandards/cpp/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.qll new file mode 100644 index 0000000000..8017308fda --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.qll @@ -0,0 +1,27 @@ +/** + * Provides a library which includes a `problems` predicate for reporting + * instances of memcmp being used to access bits of an object representation + * that are not part of the object's value representation. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import semmle.code.cpp.padding.Padding +import semmle.code.cpp.security.BufferAccess + +abstract class MemcmpUsedToComparePaddingDataSharedQuery extends Query { } + +Query getQuery() { result instanceof MemcmpUsedToComparePaddingDataSharedQuery } + +query predicate problems(MemcmpBA cmp, string message) { + not isExcluded(cmp, getQuery()) and + cmp.getBuffer(_, _) + .getUnconverted() + .getUnspecifiedType() + .(PointerType) + .getBaseType() + .getUnspecifiedType() instanceof PaddedType and + message = + cmp.getName() + " accesses bits which are not part of the object's value representation." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.qll b/cpp/common/src/codingstandards/cpp/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.qll new file mode 100644 index 0000000000..43c1821e2e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.qll @@ -0,0 +1,25 @@ +/** + * Provides a library which includes a `problems` predicate for reporting missing static specifiers for redeclarations of functions with internal linkage. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery } + +query predicate problems( + FunctionDeclarationEntry redeclaration, string message, FunctionDeclarationEntry fde, + string msgpiece +) { + not isExcluded(redeclaration, getQuery()) and + fde.hasSpecifier("static") and + fde.getDeclaration().isTopLevel() and + redeclaration.getDeclaration() = fde.getDeclaration() and + not redeclaration.hasSpecifier("static") and + fde != redeclaration and + message = "The redeclaration of $@ with internal linkage misses the static specifier." and + msgpiece = "function" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.qll b/cpp/common/src/codingstandards/cpp/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.qll new file mode 100644 index 0000000000..90f28e6cc8 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/missingstaticspecifierobjectredeclarationshared/MissingStaticSpecifierObjectRedeclarationShared.qll @@ -0,0 +1,27 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Declaring an identifier with internal linkage without the static storage class + * specifier is an obselescent feature. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class MissingStaticSpecifierObjectRedeclarationSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof MissingStaticSpecifierObjectRedeclarationSharedSharedQuery } + +query predicate problems( + VariableDeclarationEntry redeclaration, string message, VariableDeclarationEntry de, + string deString +) { + not isExcluded(redeclaration, getQuery()) and + //following implies de != redeclaration + de.hasSpecifier("static") and + not redeclaration.hasSpecifier("static") and + de.getDeclaration().isTopLevel() and + redeclaration.getDeclaration() = de.getDeclaration() and + message = "The redeclaration of $@ with internal linkage misses the static specifier." and + deString = de.getName() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.qll b/cpp/common/src/codingstandards/cpp/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.qll new file mode 100644 index 0000000000..eecd349ad7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.qll @@ -0,0 +1,141 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Possible misuse of a generate infinite floating point value. + */ + +import cpp +import codeql.util.Boolean +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.RestrictedRangeAnalysis +import codingstandards.cpp.FloatingPoint +import codingstandards.cpp.AlertReporting +import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.dataflow.new.DataFlow +import semmle.code.cpp.dataflow.new.TaintTracking +import semmle.code.cpp.controlflow.Dominance + +module InvalidInfinityUsage implements DataFlow::ConfigSig { + /** + * An operation which does not have Infinity as an input, but may produce Infinity, according + * to the `RestrictedRangeAnalysis` module. + */ + predicate isSource(DataFlow::Node node) { + potentialSource(node) and + not exists(DataFlow::Node prior | + isAdditionalFlowStep(prior, node) and + potentialSource(prior) + ) + } + + /** + * An operation which may produce Infinity acconding to the `RestrictedRangeAnalysis` module. + */ + additional predicate potentialSource(DataFlow::Node node) { + node.asExpr() instanceof Operation and + exprMayEqualInfinity(node.asExpr(), _) + } + + predicate isBarrierOut(DataFlow::Node node) { + guardedNotFPClass(node.asExpr(), TInfinite()) + or + exists(Expr e | + e.getType() instanceof IntegralType and + e = node.asConvertedExpr() + ) + } + + /** + * An additional flow step to handle operations which propagate Infinity. + * + * This double checks that an Infinity may propagate by checking the `RestrictedRangeAnalysis` + * value estimate. This is conservative, since `RestrictedRangeAnalysis` is performed locally + * in scope with unanalyzable values in a finite range. However, this conservative approach + * leverages analysis of guards and other local conditions to avoid false positives. + */ + predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node sink) { + exists(Operation o | + o.getAnOperand() = source.asExpr() and + o = sink.asExpr() and + potentialSource(sink) + ) + } + + predicate isSink(DataFlow::Node node) { + node instanceof InvalidInfinityUsage and + ( + // Require that range analysis finds this value potentially infinite, to avoid false positives + // in the presence of guards. This may induce false negatives. + exprMayEqualInfinity(node.asExpr(), _) + or + // Unanalyzable expressions are not checked against range analysis, which assumes a finite + // range. + not RestrictedRangeAnalysis::canBoundExpr(node.asExpr()) + or + node.asExpr().(VariableAccess).getTarget() instanceof Parameter + ) + } +} + +class InvalidInfinityUsage extends DataFlow::Node { + string description; + + InvalidInfinityUsage() { + // Case 2: NaNs and infinities shall not be cast to integers + exists(Conversion c | + asExpr() = c.getUnconverted() and + c.getExpr().getType() instanceof FloatingPointType and + c.getType() instanceof IntegralType and + description = "cast to integer." + ) + or + // Case 3: Infinities shall not underflow or otherwise produce finite values + exists(BinaryOperation op | + asExpr() = op.getRightOperand() and + op.getOperator() = "/" and + description = "divisor, which would silently underflow and produce zero." + ) + } + + string getDescription() { result = description } +} + +module InvalidInfinityFlow = DataFlow::Global; + +import InvalidInfinityFlow::PathGraph + +abstract class MisuseOfInfiniteFloatingPointValueSharedQuery extends Query { } + +Query getQuery() { result instanceof MisuseOfInfiniteFloatingPointValueSharedQuery } + +query predicate problems( + Element elem, InvalidInfinityFlow::PathNode source, InvalidInfinityFlow::PathNode sink, + string message, Expr sourceExpr, string sourceString, Function function, string functionName +) { + not isExcluded(elem, getQuery()) and + exists(InvalidInfinityUsage usage, string computedInFunction | + elem = MacroUnwrapper::unwrapElement(sink.getNode().asExpr()) and + not InvalidInfinityFlow::PathGraph::edges(_, source, _, _) and + not InvalidInfinityFlow::PathGraph::edges(sink, _, _, _) and + not sourceExpr.isFromTemplateInstantiation(_) and + not usage.asExpr().isFromTemplateInstantiation(_) and + usage = sink.getNode() and + sourceExpr = source.getNode().asExpr() and + function = sourceExpr.getEnclosingFunction() and + InvalidInfinityFlow::flow(source.getNode(), usage) and + ( + if not sourceExpr.getEnclosingFunction() = usage.asExpr().getEnclosingFunction() + then computedInFunction = "computed in function $@ " + else computedInFunction = "" + ) and + ( + if sourceExpr instanceof DivExpr + then sourceString = "from division by zero" + else sourceString = sourceExpr.toString() + ) and + message = + "Possibly infinite float value $@ " + computedInFunction + "flows to " + + usage.getDescription() and + functionName = function.getName() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.qll b/cpp/common/src/codingstandards/cpp/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.qll new file mode 100644 index 0000000000..19ec4e1986 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.qll @@ -0,0 +1,201 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Possible mishandling of an undetected NaN value produced by a floating point + * operation. + */ + +import cpp +import codeql.util.Boolean +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.RestrictedRangeAnalysis +import codingstandards.cpp.FloatingPoint +import codingstandards.cpp.AlertReporting +import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.dataflow.new.DataFlow +import semmle.code.cpp.dataflow.new.TaintTracking +import semmle.code.cpp.controlflow.Dominance + +abstract class PotentiallyNaNExpr extends Expr { + abstract string getReason(); +} + +class DomainErrorFunctionCall extends FunctionCall, PotentiallyNaNExpr { + string reason; + + DomainErrorFunctionCall() { RestrictedDomainError::hasDomainError(this, reason) } + + override string getReason() { result = reason } +} + +// IEEE 754-1985 Section 7.1 invalid operations +class InvalidOperationExpr extends BinaryOperation, PotentiallyNaNExpr { + string reason; + + InvalidOperationExpr() { + // Usual arithmetic conversions in both languages mean that if either operand is a floating + // type, the other one is converted to a floating type as well. + getAnOperand().getFullyConverted().getType() instanceof FloatingPointType and + ( + // 7.1.1 propagates signaling NaNs, we rely on flow analysis and/or assume quiet NaNs, so we + // intentionally do not cover this case. + // 7.1.2: magnitude subtraction of infinities, such as +Inf + -Inf + getOperator() = "+" and + exists(Boolean sign | + exprMayEqualInfinity(getLeftOperand(), sign) and + exprMayEqualInfinity(getRightOperand(), sign.booleanNot()) + ) and + reason = "from addition of infinity and negative infinity" + or + // 7.1.2 continued + getOperator() = "-" and + exists(Boolean sign | + exprMayEqualInfinity(getLeftOperand(), sign) and + exprMayEqualInfinity(getRightOperand(), sign) + ) and + reason = "from subtraction of an infinity from itself" + or + // 7.1.3: multiplication of zero by infinity + getOperator() = "*" and + exists(Expr zeroOp, Expr infinityOp | + zeroOp = getAnOperand() and + infinityOp = getAnOperand() and + not zeroOp = infinityOp and + exprMayEqualZero(zeroOp) and + exprMayEqualInfinity(infinityOp, _) + ) and + reason = "from multiplication of zero by infinity" + or + // 7.1.4: Division of zero by zero, or infinity by infinity + getOperator() = "/" and + exprMayEqualZero(getLeftOperand()) and + exprMayEqualZero(getRightOperand()) and + reason = "from division of zero by zero" + or + // 7.1.4 continued + getOperator() = "/" and + exprMayEqualInfinity(getLeftOperand(), _) and + exprMayEqualInfinity(getRightOperand(), _) and + reason = "from division of infinity by infinity" + or + // 7.1.5: x % y where y is zero or x is infinite + getOperator() = "%" and + exprMayEqualInfinity(getLeftOperand(), _) and + reason = "from modulus of infinity" + or + // 7.1.5 continued + getOperator() = "%" and + exprMayEqualZero(getRightOperand()) and + reason = "from modulus by zero" + // 7.1.6 handles the sqrt function, not covered here. + // 7.1.7 declares exceptions during invalid conversions, which we catch as sinks in our flow + // analysis. + // 7.1.8 declares exceptions for unordered comparisons, which we catch as sinks in our flow + // analysis. + ) + } + + override string getReason() { result = reason } +} + +module InvalidNaNUsage implements DataFlow::ConfigSig { + /** + * An expression which has non-NaN inputs and may produce a NaN output. + */ + predicate isSource(DataFlow::Node node) { + potentialSource(node) and + not exists(DataFlow::Node prior | + isAdditionalFlowStep(prior, node) and + potentialSource(prior) + ) + } + + /** + * An expression which may produce a NaN output. + */ + additional predicate potentialSource(DataFlow::Node node) { + node.asExpr() instanceof PotentiallyNaNExpr + } + + predicate isBarrierOut(DataFlow::Node node) { + guardedNotFPClass(node.asExpr(), TNaN()) + or + exists(Expr e | + e.getType() instanceof IntegralType and + e = node.asConvertedExpr() + ) + } + + /** + * Add an additional flow step to handle NaN propagation through floating point operations. + */ + predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node sink) { + exists(Operation o | + o.getAnOperand() = source.asExpr() and + o = sink.asExpr() and + o.getType() instanceof FloatingPointType + ) + } + + predicate isSink(DataFlow::Node node) { + not guardedNotFPClass(node.asExpr(), TNaN()) and + node instanceof InvalidNaNUsage + } +} + +class InvalidNaNUsage extends DataFlow::Node { + string description; + + InvalidNaNUsage() { + // Case 1: NaNs shall not be compared, except to themselves + exists(ComparisonOperation cmp | + this.asExpr() = cmp.getAnOperand() and + not hashCons(cmp.getLeftOperand()) = hashCons(cmp.getRightOperand()) and + description = "comparison, which would always evaluate to false." + ) + or + // Case 2: NaNs and infinities shall not be cast to integers + exists(Conversion c | + this.asExpr() = c.getUnconverted() and + c.getExpr().getType() instanceof FloatingPointType and + c.getType() instanceof IntegralType and + description = "a cast to integer." + ) + } + + string getDescription() { result = description } +} + +module InvalidNaNFlow = DataFlow::Global; + +import InvalidNaNFlow::PathGraph + +abstract class MisuseOfNaNFloatingPointValueSharedQuery extends Query { } + +Query getQuery() { result instanceof MisuseOfNaNFloatingPointValueSharedQuery } + +query predicate problems( + Element elem, InvalidNaNFlow::PathNode source, InvalidNaNFlow::PathNode sink, string message, + Expr sourceExpr, string sourceString, Function function, string functionName +) { + not isExcluded(elem, getQuery()) and + exists(InvalidNaNUsage usage, string computedInFunction | + not InvalidNaNFlow::PathGraph::edges(_, source, _, _) and + not InvalidNaNFlow::PathGraph::edges(sink, _, _, _) and + not sourceExpr.isFromTemplateInstantiation(_) and + not usage.asExpr().isFromTemplateInstantiation(_) and + elem = MacroUnwrapper::unwrapElement(sink.getNode().asExpr()) and + usage = sink.getNode() and + sourceExpr = source.getNode().asExpr() and + sourceString = source.getNode().asExpr().(PotentiallyNaNExpr).getReason() and + function = sourceExpr.getEnclosingFunction() and + InvalidNaNFlow::flow(source.getNode(), usage) and + ( + if not sourceExpr.getEnclosingFunction() = usage.asExpr().getEnclosingFunction() + then computedInFunction = "computed in function $@ " + else computedInFunction = "" + ) and + message = "Possible NaN value $@ " + computedInFunction + "flows to " + usage.getDescription() and + functionName = function.getName() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.qll b/cpp/common/src/codingstandards/cpp/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.qll new file mode 100644 index 0000000000..05821d7270 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.qll @@ -0,0 +1,66 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A declaration should not declare more than one variable or member variable. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class MultipleGlobalOrMemberDeclaratorsSharedQuery extends Query { } + +Query getQuery() { result instanceof MultipleGlobalOrMemberDeclaratorsSharedQuery } + +/* + * Unfortunately, we do not have an equivalent of `DeclStmt` for non-local declarations, so we + * cannot determine whether a declaration was declared with another declaration. + * + * However, we can use location trickery to figure out if the declaration occurs close enough to + * another declaration that it _must_ have been declared within the same declaration sequence. + * + * We do this by requiring that the end location of a previous declaration is within a certain + * number of characters of the start location of the current declaration. + */ + +/** + * A `Declaration` which is not in a local scope, and is written directly by the user. + * + * These act as "candidates" for declarations that could plausibly occur in a declaration sequence + * with other candidates. + */ +class NonLocalUserDeclaration extends Declaration { + NonLocalUserDeclaration() { + not this instanceof StackVariable and + not this instanceof TemplateParameter and + not this instanceof EnumConstant and + not this instanceof TypedefType and + not any(LambdaCapture lc).getField() = this and + not this.(Function).isCompilerGenerated() and + not this.(Variable).isCompilerGenerated() and + not this.(Parameter).getFunction().isCompilerGenerated() and + not this.isInMacroExpansion() and + not exists(Struct s, TypedefType t | + s.isAnonymous() and + t.getBaseType() = s and + this = s.getAMemberVariable() + ) + } +} + +/** + * Holds if `d1` is followed directly by `d2`. + */ +predicate isFollowingDeclaration(NonLocalUserDeclaration d1, NonLocalUserDeclaration d2) { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + d1.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and + d2.getLocation().hasLocationInfo(filepath, startline, endcolumn + [2 .. 3], endline, _) + ) and + not d1.(UserType).stripType() = d2.(Variable).getType().stripType() +} + +query predicate problems(NonLocalUserDeclaration d1, string message) { + not isExcluded(d1, getQuery()) and + isFollowingDeclaration(d1, _) and + not isFollowingDeclaration(_, d1) and + message = "Multiple declarations after " + d1.getName() + " in this declaration sequence." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/multiplelocaldeclarators/MultipleLocalDeclarators.qll b/cpp/common/src/codingstandards/cpp/rules/multiplelocaldeclarators/MultipleLocalDeclarators.qll new file mode 100644 index 0000000000..2269f36d97 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/multiplelocaldeclarators/MultipleLocalDeclarators.qll @@ -0,0 +1,20 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A declaration should not declare more than one variable or member variable. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class MultipleLocalDeclaratorsSharedQuery extends Query { } + +Query getQuery() { result instanceof MultipleLocalDeclaratorsSharedQuery } + +query predicate problems(DeclStmt ds, string message) { + not isExcluded(ds, getQuery()) and + count(Declaration d | d = ds.getADeclaration()) > 1 and + // Not a compiler generated `DeclStmt`, such as in the range-based for loop + not ds.isCompilerGenerated() and + message = "Declaration list contains more than one declaration." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.qll b/cpp/common/src/codingstandards/cpp/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.qll new file mode 100644 index 0000000000..326886dda6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.qll @@ -0,0 +1,20 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A named bit-field with signed integer type shall not have a length of one bit. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NamedBitFieldsWithSignedIntegerTypeSharedQuery extends Query { } + +Query getQuery() { result instanceof NamedBitFieldsWithSignedIntegerTypeSharedQuery } + +query predicate problems(BitField bitField, string message) { + not isExcluded(bitField, getQuery()) and + bitField.getDeclaredNumBits() = 1 and // Single-bit, + not bitField.isAnonymous() and // named, + bitField.getType().(IntegralType).isSigned() and // but its type is signed. + message = "A named bit-field with signed integral type should have at least 2 bits of storage." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.qll b/cpp/common/src/codingstandards/cpp/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.qll new file mode 100644 index 0000000000..a4c9255c89 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.qll @@ -0,0 +1,37 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Not using a qualified-id or `this->` syntax for identifiers used in a class template + * makes the code more difficult to understand. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.NameInDependentBase + +abstract class NameNotReferredUsingAQualifiedIdOrThisSharedQuery extends Query { } + +Query getQuery() { result instanceof NameNotReferredUsingAQualifiedIdOrThisSharedQuery } + +query predicate problems( + NameQualifiableElement fn, string message, Element actualTarget, string targetName, + Element dependentTypeMemberWithSameName, string dependentType_string +) { + not isExcluded(fn, getQuery()) and + missingNameQualifier(fn) and + exists(TemplateClass c | + fn = getConfusingFunctionAccess(c, targetName, actualTarget, dependentTypeMemberWithSameName) + or + fn = getConfusingFunctionCall(c, targetName, actualTarget, dependentTypeMemberWithSameName) and + not exists(Expr e | e = fn.(FunctionCall).getQualifier()) + or + fn = + getConfusingMemberVariableAccess(c, targetName, actualTarget, dependentTypeMemberWithSameName) and + not exists(Expr e | e = fn.(VariableAccess).getQualifier()) + ) and + not fn.isAffectedByMacro() and + message = + "Use of unqualified identifier " + targetName + + " targets $@ but a member with the name also exists $@." and + dependentType_string = "in the dependent base class" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.qll b/cpp/common/src/codingstandards/cpp/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.qll new file mode 100644 index 0000000000..d0a6251908 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.qll @@ -0,0 +1,38 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Not using a qualified-id or `this->` syntax for identifiers used in a class template + * makes the code more difficult to understand. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.NameInDependentBase + +abstract class NameNotReferredUsingAQualifiedIdOrThisAuditSharedQuery extends Query { } + +Query getQuery() { result instanceof NameNotReferredUsingAQualifiedIdOrThisAuditSharedQuery } + +query predicate problems( + NameQualifiableElement fn, string message, Element actualTarget, string targetName, + Element dependentTypeMemberWithSameName, string dependentType_string +) { + not isExcluded(fn, getQuery()) and + not isCustomExcluded(fn) and + missingNameQualifier(fn) and + exists(TemplateClass c | + fn = getConfusingFunctionAccess(c, targetName, actualTarget, dependentTypeMemberWithSameName) + or + fn = getConfusingFunctionCall(c, targetName, actualTarget, dependentTypeMemberWithSameName) and + not exists(Expr e | e = fn.(FunctionCall).getQualifier()) + or + not fn.(VariableAccess).getTarget() instanceof Parameter and + fn = + getConfusingMemberVariableAccess(c, targetName, actualTarget, dependentTypeMemberWithSameName) and + not exists(Expr e | e = fn.(VariableAccess).getQualifier()) + ) and + message = + "Use of unqualified identifier " + targetName + + " targets $@ but a member with the name also exists $@." and + dependentType_string = "in the dependent base class" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nestedlabelinswitch/NestedLabelInSwitch.qll b/cpp/common/src/codingstandards/cpp/rules/nestedlabelinswitch/NestedLabelInSwitch.qll new file mode 100644 index 0000000000..d6e75d6faf --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nestedlabelinswitch/NestedLabelInSwitch.qll @@ -0,0 +1,25 @@ +/** + * Provides a library which includes a `problems` predicate for reporting nested labels in a switch statement. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NestedLabelInSwitchSharedQuery extends Query { } + +Query getQuery() { result instanceof NestedLabelInSwitchSharedQuery } + +query predicate problems( + SwitchCase case, string message, SwitchCase caseLocation, string caseLabel, SwitchStmt switch, + string switchLabel +) { + not isExcluded(case, getQuery()) and + switch.getASwitchCase() = caseLocation and + not case.getParentStmt() = switch.getChildStmt() and + case = caseLocation and + message = + "The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement." and + caseLabel = case.toString() and + switchLabel = switch.toString() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.qll b/cpp/common/src/codingstandards/cpp/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.qll new file mode 100644 index 0000000000..bc3b620718 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.qll @@ -0,0 +1,38 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * If a function is declared to be noexcept, noexcept(true) or noexcept(), then it shall not exit with an exception. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.exceptions.ExceptionFlow +import ExceptionPathGraph +import codingstandards.cpp.exceptions.ExceptionSpecifications + +abstract class NoexceptFunctionShouldNotPropagateToTheCallerSharedQuery extends Query { } + +Query getQuery() { result instanceof NoexceptFunctionShouldNotPropagateToTheCallerSharedQuery } + +class NoExceptThrowingFunction extends ExceptionThrowingFunction { + NoExceptThrowingFunction() { + // Can exit with an exception + exists(getAFunctionThrownType(_, _)) and + // But is marked noexcept(true) or equivalent + isNoExceptTrue(this) + } +} + +query predicate problems( + NoExceptThrowingFunction f, ExceptionFlowNode exceptionSource, ExceptionFlowNode functionNode, + string message +) { + exists(ExceptionType exceptionType | + not isExcluded(f, getQuery()) and + f.hasExceptionFlow(exceptionSource, functionNode, exceptionType) and + message = + "Function " + f.getName() + " is declared noexcept(true) but can throw exceptions of type " + + exceptionType.getExceptionName() + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonbooleanifstmt/NonBooleanIfStmt.qll b/cpp/common/src/codingstandards/cpp/rules/nonbooleanifstmt/NonBooleanIfStmt.qll new file mode 100644 index 0000000000..bd84597a44 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonbooleanifstmt/NonBooleanIfStmt.qll @@ -0,0 +1,24 @@ +/** + * Provides a library which includes a `problems` predicate for reporting if statements which have non boolean conditions. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonBooleanIfStmtSharedQuery extends Query { } + +Query getQuery() { result instanceof NonBooleanIfStmtSharedQuery } + +query predicate problems(Expr condition, string message) { + not isExcluded(condition, getQuery()) and + exists(IfStmt ifStmt, Type explicitConversionType | + condition = ifStmt.getCondition() and + //exclude any generated conditions + not condition.isCompilerGenerated() and + not ifStmt.isFromUninstantiatedTemplate(_) and + explicitConversionType = condition.getExplicitlyConverted().getUnderlyingType() and + not explicitConversionType instanceof BoolType and + message = "If condition has non boolean type " + explicitConversionType + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.qll b/cpp/common/src/codingstandards/cpp/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.qll new file mode 100644 index 0000000000..83e58f72d5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.qll @@ -0,0 +1,25 @@ +/** + * Provides a library which includes a `problems` predicate for reporting non-boolean iteration conditions. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonBooleanIterationStmtSharedQuery extends Query { } + +Query getQuery() { result instanceof NonBooleanIterationStmtSharedQuery } + +query predicate problems(Loop loopStmt, string message) { + not isExcluded(loopStmt, getQuery()) and + exists(Expr condition, Type explicitConversionType | + condition = loopStmt.getCondition() and + explicitConversionType = condition.getExplicitlyConverted().getType().getUnspecifiedType() and + not explicitConversionType instanceof BoolType and + // exclude any generated conditions + not condition.isCompilerGenerated() and + // exclude any conditions in uninstantiated templates, because their type will be unknown. + not condition.isFromUninstantiatedTemplate(_) and + message = "Iteration condition has non boolean type " + explicitConversionType + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonconstantformat/NonConstantFormat.qll b/cpp/common/src/codingstandards/cpp/rules/nonconstantformat/NonConstantFormat.qll index 34d6c4f3e4..248cde106f 100644 --- a/cpp/common/src/codingstandards/cpp/rules/nonconstantformat/NonConstantFormat.qll +++ b/cpp/common/src/codingstandards/cpp/rules/nonconstantformat/NonConstantFormat.qll @@ -106,28 +106,28 @@ predicate isSanitizerNode(DataFlow::Node node) { cannotContainString(node.getType()) } -class NonConstFlow extends TaintTracking::Configuration { - NonConstFlow() { this = "NonConstFlow" } - - override predicate isSource(DataFlow::Node source) { +module NonConstConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { isNonConst(source) and not cannotContainString(source.getType()) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FormattingFunctionCall fc | sink.asExpr() = fc.getArgument(fc.getFormatParameterIndex())) } - override predicate isSanitizer(DataFlow::Node node) { isSanitizerNode(node) } + predicate isBarrier(DataFlow::Node node) { isSanitizerNode(node) } } +module NonConstFlow = TaintTracking::Global; + query predicate problems( Expr formatString, string message, FormattingFunctionCall call, string formatStringDescription ) { not isExcluded(formatString, getQuery()) and call.getArgument(call.getFormatParameterIndex()) = formatString and - exists(NonConstFlow cf, DataFlow::Node source, DataFlow::Node sink | - cf.hasFlow(source, sink) and + exists(DataFlow::Node source, DataFlow::Node sink | + NonConstFlow::flow(source, sink) and sink.asExpr() = formatString ) and message = diff --git a/cpp/common/src/codingstandards/cpp/rules/nonglobalfunctionmain/NonGlobalFunctionMain.qll b/cpp/common/src/codingstandards/cpp/rules/nonglobalfunctionmain/NonGlobalFunctionMain.qll new file mode 100644 index 0000000000..7366849d0c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonglobalfunctionmain/NonGlobalFunctionMain.qll @@ -0,0 +1,20 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The identifier main shall not be used for a function other than the global function + * main. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonGlobalFunctionMainSharedQuery extends Query { } + +Query getQuery() { result instanceof NonGlobalFunctionMainSharedQuery } + +query predicate problems(Function f, string message) { + not isExcluded(f, getQuery()) and + f.hasName("main") and + not f.hasGlobalName("main") and + message = "Identifier main used for a function other than the global function main." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.qll b/cpp/common/src/codingstandards/cpp/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.qll index e411b3afe6..e6b78f98f7 100644 --- a/cpp/common/src/codingstandards/cpp/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.qll +++ b/cpp/common/src/codingstandards/cpp/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.qll @@ -5,6 +5,7 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions +import codingstandards.cpp.StdNamespace abstract class NonStandardEntitiesInStandardNamespacesSharedQuery extends Query { } @@ -19,7 +20,7 @@ private class PosixNamespace extends Namespace { private Namespace getStandardNamespace(DeclarationEntry de) { result = de.getDeclaration().getNamespace().getParentNamespace*() and ( - result instanceof StdNamespace + result instanceof StdNS or result instanceof PosixNamespace ) diff --git a/cpp/common/src/codingstandards/cpp/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.qll b/cpp/common/src/codingstandards/cpp/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.qll new file mode 100644 index 0000000000..f23965ea7c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.qll @@ -0,0 +1,44 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Octal escape sequences, hexadecimal escape sequences, and universal character names + * shall be terminated. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonTerminatedEscapeSequencesSharedQuery extends Query { } + +Query getQuery() { result instanceof NonTerminatedEscapeSequencesSharedQuery } + +bindingset[s] +predicate isOctalEscape(string s) { + s.charAt(0) = "\\" and + exists(int i | i = [0 .. 7] | i.toString() = s.charAt(1)) +} + +bindingset[s] +predicate isHexEscape(string s) { s.indexOf("\\x") = 0 } + +query predicate problems(Literal l, string message) { + not isExcluded(l, getQuery()) and + exists(int idx, string sl, string escapeKind, string s | + sl = l.getValueText() and + idx = sl.indexOf("\\") and + s = sl.substring(idx, sl.length()) and + // Note: Octal representations must be 1-3 digits. There is no limitation on a + // Hex literal as long as the characters are valid. This query does not consider + // if the hex literal being constructed will overflow. + ( + isHexEscape(s) and + not s.regexpMatch("^((\\\\x[0-9A-F]+(?=[\"'\\\\])))[\\s\\S]*") and + escapeKind = "hexadecimal" + or + isOctalEscape(s) and + not s.regexpMatch("^(((\\\\[0-7]{1,3})(?=[\"'\\\\])))[\\s\\S]*") and + escapeKind = "octal" + ) and + message = "Invalid " + escapeKind + " escape in string literal at '" + s + "'." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.qll b/cpp/common/src/codingstandards/cpp/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.qll new file mode 100644 index 0000000000..f7afe8b3e7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.qll @@ -0,0 +1,38 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Within an enumerator list, the value of an implicitly-specified enumeration constant + * shall be unique. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonUniqueEnumerationConstantSharedQuery extends Query { } + +Query getQuery() { result instanceof NonUniqueEnumerationConstantSharedQuery } + +/** + * An `EnumConstant` that has an implicitly specified value: + * `enum e { explicit = 1, implicit }` + */ +class ImplicitlySpecifiedEnumConstant extends EnumConstant { + ImplicitlySpecifiedEnumConstant() { + //implicitly specified have an initializer with location: `file://:0:0:0:0` + not this.getInitializer().getLocation().getFile() = this.getFile() + } +} + +query predicate problems( + ImplicitlySpecifiedEnumConstant imp, string message, EnumConstant exp, string exp_string +) { + not isExcluded(imp, getQuery()) and + not isExcluded(exp, getQuery()) and + not exp = imp and + imp.getValue() = exp.getValue() and + imp.getDeclaringEnum() = exp.getDeclaringEnum() and + //can technically be the same declared enum across multiple headers but those are not relevant to this rule + imp.getFile() = exp.getFile() and + message = "Nonunique value of enum constant compared to $@" and + exp_string = exp.getName() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/notdistinctidentifier/NotDistinctIdentifier.qll b/cpp/common/src/codingstandards/cpp/rules/notdistinctidentifier/NotDistinctIdentifier.qll index 944168f0fe..102c53428b 100644 --- a/cpp/common/src/codingstandards/cpp/rules/notdistinctidentifier/NotDistinctIdentifier.qll +++ b/cpp/common/src/codingstandards/cpp/rules/notdistinctidentifier/NotDistinctIdentifier.qll @@ -1,47 +1,39 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * Using nondistinct external identifiers results in undefined behaviour. */ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -import codingstandards.cpp.Linkage - -class ExternalIdentifiers extends Declaration { - ExternalIdentifiers() { - this.getName().length() >= 31 and - hasExternalLinkage(this) and - getNamespace() instanceof GlobalNamespace and - not this.isFromTemplateInstantiation(_) and - not this.isFromUninstantiatedTemplate(_) and - not this.hasDeclaringType() and - not this instanceof UserType and - not this instanceof Operator and - not this.hasName("main") - } - - string getSignificantName() { - //C99 states the first 31 characters of external identifiers are significant - //C90 states the first 6 characters of external identifiers are significant and case is not required to be significant - //C90 is not currently considered by this rule - result = this.getName().prefix(31) - } -} +import codingstandards.cpp.Identifiers abstract class NotDistinctIdentifierSharedQuery extends Query { } Query getQuery() { result instanceof NotDistinctIdentifierSharedQuery } +bindingset[d, d2] +pragma[inline_late] +private predicate after(ExternalIdentifiers d, ExternalIdentifiers d2) { + exists(int dStartLine, int d2StartLine | + d.getLocation().hasLocationInfo(_, dStartLine, _, _, _) and + d2.getLocation().hasLocationInfo(_, d2StartLine, _, _, _) and + dStartLine >= d2StartLine + ) +} + query predicate problems( ExternalIdentifiers d, string message, ExternalIdentifiers d2, string nameplaceholder ) { not isExcluded(d, getQuery()) and not isExcluded(d2, getQuery()) and + d.getName().length() >= 31 and + d2.getName().length() >= 31 and not d = d2 and - d.getLocation().getStartLine() >= d2.getLocation().getStartLine() and d.getSignificantName() = d2.getSignificantName() and not d.getName() = d2.getName() and nameplaceholder = d2.getName() and + after(d, d2) and message = "External identifer " + d.getName() + " is nondistinct in characters at or over 31 limit, compared to $@" diff --git a/cpp/common/src/codingstandards/cpp/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.qll b/cpp/common/src/codingstandards/cpp/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.qll new file mode 100644 index 0000000000..2b24aa9410 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.qll @@ -0,0 +1,25 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * nullptr shall be the only form of the null-pointer-constant. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import semmle.code.cpp.commons.NULL + +abstract class NullptrNotTheOnlyFormOfTheNullPointerConstantSharedQuery extends Query { } + +Query getQuery() { result instanceof NullptrNotTheOnlyFormOfTheNullPointerConstantSharedQuery } + +query predicate problems(Literal l, string message) { + not isExcluded(l, getQuery()) and // Not the type of the nullptr literal + not l.getType() instanceof NullPointerType and + // Converted to a pointer type + l.getConversion().getType().getUnspecifiedType() instanceof PointerType and + // Value of zero + l.getValue() = "0" and + // Not the StringLiteral "0" + not l instanceof StringLiteral and + message = l.getValueText() + " is used as the null-pointer-constant but is not nullptr." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.qll b/cpp/common/src/codingstandards/cpp/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.qll new file mode 100644 index 0000000000..1303646ef5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.qll @@ -0,0 +1,91 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * An object’s dynamic type shall not be used from within its constructor or + * destructor. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class ObjectsDynamicTypeUsedFromConstructorOrDestructorSharedQuery extends Query { } + +Query getQuery() { result instanceof ObjectsDynamicTypeUsedFromConstructorOrDestructorSharedQuery } + +predicate thisCall(FunctionCall c) { + c.getQualifier() instanceof ThisExpr or + c.getQualifier().(PointerDereferenceExpr).getChild(0) instanceof ThisExpr +} + +predicate virtualThisCall(FunctionCall c, Function overridingFunction) { + c.isVirtual() and + thisCall(c) and + overridingFunction = c.getTarget().(VirtualFunction).getAnOverridingFunction() +} + +class DynamicTypeExpr extends Expr { + DynamicTypeExpr() { + this instanceof TypeidOperator and + this.getEnclosingFunction().getDeclaringType().isPolymorphic() + or + this instanceof DynamicCast + or + virtualThisCall(this.(FunctionCall), _) + } +} + +/* + * Catch most cases: go into functions in the same class, but only catch direct + * references to "this". + */ + +predicate nonVirtualMemberFunction(MemberFunction mf, Class c) { + mf = c.getAMemberFunction() and + not mf instanceof Constructor and + not mf instanceof Destructor and + not mf.isVirtual() +} + +predicate callFromNonVirtual(MemberFunction source, Class c, MemberFunction targ) { + exists(FunctionCall fc | + fc.getEnclosingFunction() = source and fc.getTarget() = targ and thisCall(fc) + ) and + targ = c.getAMemberFunction() and + nonVirtualMemberFunction(source, c) +} + +predicate indirectlyInvokesDynamicTypeExpr(MemberFunction caller, DynamicTypeExpr target) { + target = + any(DynamicTypeExpr expr | + expr.getEnclosingFunction() = caller and + nonVirtualMemberFunction(caller, caller.getDeclaringType()) + ) + or + exists(MemberFunction mid | + indirectlyInvokesDynamicTypeExpr(mid, target) and + callFromNonVirtual(caller, caller.getDeclaringType(), mid) + ) +} + +query predicate problems( + DynamicTypeExpr expr, string explanation, MemberFunction mf, string mf_string +) { + not isExcluded(expr, getQuery()) and + ( + mf instanceof Constructor or + mf instanceof Destructor + ) and + mf_string = mf.getQualifiedName() and + exists(FunctionCall call | + mf = expr.getEnclosingFunction() and + explanation = "$@ uses the dynamic type of its own object." + or + mf != expr.getEnclosingFunction() and + mf = call.getEnclosingFunction() and + thisCall(call) and + indirectlyInvokesDynamicTypeExpr(call.getTarget(), expr) and + explanation = + "$@ calls " + call.getTarget().getQualifiedName() + + ", which uses the dynamic type of its own object." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.qll b/cpp/common/src/codingstandards/cpp/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.qll new file mode 100644 index 0000000000..89c732ff5a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/onlyfreememoryallocateddynamicallyshared/OnlyFreeMemoryAllocatedDynamicallyShared.qll @@ -0,0 +1,132 @@ +/** + * Provides a library which includes a `problems` predicate for reporting memory + * that is not allocated dynamically being subsequently freed via a call to `free`. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Allocations +import semmle.code.cpp.dataflow.DataFlow +import NonDynamicPointerToFreeFlow::PathGraph + +/** + * A pointer to potentially dynamically allocated memory + */ +class AllocExprSource extends DataFlow::Node { + AllocExprSource() { + allocExprOrIndirect(this.asExpr(), _) + or + // additionally include calls to library functions or output parameters + // to heuristically reduce false-positives from library functions that + // might provide pointers to dynamically allocated memory + exists(FunctionCall fc | + not exists(fc.getTarget().getBlock()) and + ( + this.asExpr() = fc or + this.asDefiningArgument() = fc.getAnArgument() + ) + ) + } +} + +/** + * An argument to a call to `free` or `realloc`. + */ +class FreeExprSink extends DataFlow::Node { + FreeExprSink() { freeExpr(_, this.asExpr(), "free") } +} + +/** + * An `Expr` that is an `AddressOfExpr` of a `Variable`. + * + * `Field`s of `PointerType` are not included in order to reduce false-positives, + * as the data-flow library sometimes equates pointers to their underlying data. + */ +class AddressOfExprSourceNode extends Expr { + AddressOfExprSourceNode() { + exists(VariableAccess va | + this.(AddressOfExpr).getOperand() = va and + ( + va.getTarget() instanceof StackVariable or + va.getTarget() instanceof GlobalVariable or + // allow address-of field, but only if that field is not a pointer type, + // as there may be nested allocations assigned to fields of pointer types. + va.(FieldAccess).getTarget().getUnderlyingType() instanceof ArithmeticType + ) + or + this = va and + exists(GlobalVariable gv | + gv = va.getTarget() and + ( + gv.getUnderlyingType() instanceof ArithmeticType or + not exists(gv.getAnAssignedValue()) or + exists(AddressOfExprSourceNode other | + DataFlow::localExprFlow(other, gv.getAnAssignedValue()) + ) + ) + ) + ) and + // exclude alloc(&allocated_ptr) cases + not DynamicMemoryAllocationToAddressOfDefiningArgFlow::flowTo(DataFlow::definitionByReferenceNodeFromArgument(this)) + } +} + +/** + * A data-flow configuration that tracks flow from an `AllocExprSource` to a `FreeExprSink`. + */ +module DynamicMemoryAllocationToAddressOfDefiningArgConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof AllocExprSource } + + predicate isSink(DataFlow::Node sink) { sink.asDefiningArgument() instanceof AddressOfExpr } +} + +module DynamicMemoryAllocationToAddressOfDefiningArgFlow = + DataFlow::Global; + +/** + * A data-flow configuration that tracks flow from a + * `NonDynamicallyAllocatedVariableAssignment` to a `FreeExprSink`. + */ +module NonDynamicPointerToFreeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof AddressOfExprSourceNode } + + predicate isSink(DataFlow::Node sink) { sink instanceof FreeExprSink } + + 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 frees of the object base, 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 + ) + ) + } + + predicate isBarrierIn(DataFlow::Node node) { + // only the last source expression is relevant + isSource(node) + } +} + +module NonDynamicPointerToFreeFlow = DataFlow::Global; + +abstract class OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery extends Query { } + +Query getQuery() { result instanceof OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery } + +query predicate problems( + NonDynamicPointerToFreeFlow::PathNode element, NonDynamicPointerToFreeFlow::PathNode source, + NonDynamicPointerToFreeFlow::PathNode sink, string message +) { + not isExcluded(element.getNode().asExpr(), getQuery()) and + element = sink and + NonDynamicPointerToFreeFlow::flowPath(source, sink) and + message = "Free expression frees memory which was not dynamically allocated." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.qll b/cpp/common/src/codingstandards/cpp/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.qll index f7fb74bb80..1e9c025e4d 100644 --- a/cpp/common/src/codingstandards/cpp/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.qll +++ b/cpp/common/src/codingstandards/cpp/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.qll @@ -14,19 +14,19 @@ abstract class OrderingPredicateMustBeStrictlyWeakSharedQuery extends Query { } Query getQuery() { result instanceof OrderingPredicateMustBeStrictlyWeakSharedQuery } +class IsStrictlyWeaklyOrderedComment extends Comment { + IsStrictlyWeaklyOrderedComment() { + exists(getContents().regexpFind("(?m)^\\s*(//|\\*)\\s*@IsStrictlyWeaklyOrdered\\s*$", _, _)) + } +} + /** * User annotated class indicating a comparator is axiomatically strictly weakly * ordering. */ class UserDefinedStrictlyWeakOrderingComparator extends Class { UserDefinedStrictlyWeakOrderingComparator() { - exists(Comment c, string contents | - c.getCommentedElement() = this.getADeclarationEntry() and - contents = - c.getContents() - .splitAt("\n") - .regexpFind("^\\s*(//|\\*)\\s*@IsStrictlyWeaklyOrdered\\s*$", _, _) - ) + exists(IsStrictlyWeaklyOrderedComment c | c.getCommentedElement() = this.getADeclarationEntry()) } } @@ -173,13 +173,7 @@ class ContainerComparatorUsage extends ComparatorUsage { query predicate problems(ComparatorUsage cu, string message) { not cu.isStrictlyWeakOrdering() and not isExcluded(cu, getQuery()) and - exists(string s | - if exists(cu.getComparator().getQualifiedName()) - then s = cu.getComparator().getQualifiedName() - else s = cu.getComparator().getName() - | - message = - "Comparator '" + s + - "' used on container or sorting algorithm that is not strictly weakly ordered" - ) + message = + "Comparator '" + cu.getComparator().getName() + + "' used on container or sorting algorithm that is not strictly weakly ordered" } diff --git a/cpp/common/src/codingstandards/cpp/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.qll b/cpp/common/src/codingstandards/cpp/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.qll new file mode 100644 index 0000000000..acfa177561 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.qll @@ -0,0 +1,37 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Parameters in an overriding virtual function shall not specify different default + * arguments. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class OverridingShallSpecifyDifferentDefaultArgumentsSharedQuery extends Query { } + +Query getQuery() { result instanceof OverridingShallSpecifyDifferentDefaultArgumentsSharedQuery } + +query predicate problems(VirtualFunction f2, string message, VirtualFunction f1, string f1_string) { + not isExcluded(f2, getQuery()) and + not isExcluded(f1, getQuery()) and + f2 = f1.getAnOverridingFunction() and + exists(Parameter p1, Parameter p2 | + p1 = f1.getAParameter() and + p2 = f2.getParameter(p1.getIndex()) + | + if p1.hasInitializer() + then + // if there is no initializer + not p2.hasInitializer() + or + // if there is one and it doesn't match + not p1.getInitializer().getExpr().getValueText() = + p2.getInitializer().getExpr().getValueText() + else + // if p1 doesn't have an initializer p2 shouldn't either + p2.hasInitializer() + ) and + message = "Overriding function does not have the same default parameters as $@" and + f1_string = "overridden function" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.qll b/cpp/common/src/codingstandards/cpp/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.qll index ced6545e39..2ee92b1611 100644 --- a/cpp/common/src/codingstandards/cpp/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.qll +++ b/cpp/common/src/codingstandards/cpp/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.qll @@ -9,37 +9,67 @@ import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import codingstandards.cpp.SmartPointers import semmle.code.cpp.dataflow.TaintTracking -import DataFlow::PathGraph +import PointerToSmartPointerConstructorFlowFlow::PathGraph abstract class OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery extends Query { } Query getQuery() { result instanceof OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery } -private class PointerToSmartPointerConstructorFlowConfig extends TaintTracking::Configuration { - PointerToSmartPointerConstructorFlowConfig() { this = "PointerToSmartPointerConstructorFlow" } - - override predicate isSource(DataFlow::Node source) { +private module PointerToSmartPointerConstructorFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { exists(Variable v | v.getAnAssignedValue() = source.asExpr()) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(AutosarSmartPointer sp, ConstructorCall cc | sp.getAConstructorCall() = cc and cc.getArgument(0).getFullyConverted().getType() instanceof PointerType and cc.getArgument(0) = sink.asExpr() ) } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // Summarize flow through constructor calls + exists(AutosarSmartPointer sp, ConstructorCall cc | + sp.getAConstructorCall() = cc and + cc = node2.asExpr() and + cc.getArgument(0) = node1.asExpr() + ) + or + // Summarize flow through get() calls + exists(AutosarSmartPointer sp, FunctionCall fc | + sp.getAGetCall() = fc and + fc = node2.asExpr() and + fc.getQualifier() = node1.asExpr() + ) + } + + predicate isBarrierIn(DataFlow::Node node) { + // Exclude flow into header files outside the source archive which are summarized by the + // additional taint steps above. + exists(AutosarSmartPointer sp | + sp.getAConstructorCall().getTarget().getAParameter() = node.asParameter() + or + sp.getAGetCall().getTarget().getAParameter() = node.asParameter() + | + not exists(node.getLocation().getFile().getRelativePath()) + ) + } } +private module PointerToSmartPointerConstructorFlowFlow = + TaintTracking::Global; + query predicate problems( - DataFlow::Node sinkNode, DataFlow::PathNode source, DataFlow::PathNode sink, string message + DataFlow::Node sinkNode, PointerToSmartPointerConstructorFlowFlow::PathNode source, + PointerToSmartPointerConstructorFlowFlow::PathNode sink, string message ) { not isExcluded(sinkNode.asExpr(), getQuery()) and - exists(PointerToSmartPointerConstructorFlowConfig config, DataFlow::PathNode sink2 | + exists(PointerToSmartPointerConstructorFlowFlow::PathNode sink2 | sink != sink2 and sinkNode = sink.getNode() and - config.hasFlowPath(source, sink) and - config.hasFlowPath(source, sink2) and + PointerToSmartPointerConstructorFlowFlow::flowPath(source, sink) and + PointerToSmartPointerConstructorFlowFlow::flowPath(source, sink2) and message = "Raw pointer flows to initialize multiple unrelated smart pointers." ) } diff --git a/cpp/common/src/codingstandards/cpp/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.qll b/cpp/common/src/codingstandards/cpp/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.qll index 5e21cce51b..6b2c6c87c9 100644 --- a/cpp/common/src/codingstandards/cpp/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.qll +++ b/cpp/common/src/codingstandards/cpp/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.qll @@ -8,24 +8,23 @@ import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import codingstandards.cpp.allocations.PlacementNew import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import PlacementNewOriginFlow::PathGraph abstract class PlacementNewInsufficientStorageSharedQuery extends Query { } Query getQuery() { result instanceof PlacementNewInsufficientStorageSharedQuery } query predicate problems( - PlacementNewExpr placementNew, DataFlow::PathNode source, DataFlow::PathNode sink, string message, - PlacementNewMemoryOrigin memoryOrigin, string memoryOriginDescription + PlacementNewExpr placementNew, PlacementNewOriginFlow::PathNode source, + PlacementNewOriginFlow::PathNode sink, string message, PlacementNewMemoryOrigin memoryOrigin, + string memoryOriginDescription ) { not isExcluded(placementNew, getQuery()) and message = "Placement new expression is used with an insufficiently large memory allocation from $@." and - exists(PlacementNewOriginConfig config | - memoryOrigin = source.getNode() and - placementNew.getPlacementExpr() = sink.getNode().asExpr() and - memoryOriginDescription = memoryOrigin.toString() and - config.hasFlowPath(source, sink) and - memoryOrigin.getMaximumMemorySize() < placementNew.getMinimumAllocationSize() - ) + memoryOrigin = source.getNode() and + placementNew.getPlacementExpr() = sink.getNode().asExpr() and + memoryOriginDescription = memoryOrigin.toString() and + PlacementNewOriginFlow::flowPath(source, sink) and + memoryOrigin.getMaximumMemorySize() < placementNew.getMinimumAllocationSize() } diff --git a/cpp/common/src/codingstandards/cpp/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.qll b/cpp/common/src/codingstandards/cpp/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.qll index f45a83351c..d250061a23 100644 --- a/cpp/common/src/codingstandards/cpp/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.qll +++ b/cpp/common/src/codingstandards/cpp/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.qll @@ -8,7 +8,7 @@ import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import codingstandards.cpp.allocations.PlacementNew import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import PlacementNewOriginFlow::PathGraph abstract class PlacementNewNotProperlyAlignedSharedQuery extends Query { } @@ -19,20 +19,19 @@ Query getQuery() { result instanceof PlacementNewNotProperlyAlignedSharedQuery } */ query predicate problems( - PlacementNewExpr placementNew, DataFlow::PathNode source, DataFlow::PathNode sink, string message, - PlacementNewMemoryOrigin memoryOrigin, string memoryOriginDescription + PlacementNewExpr placementNew, PlacementNewOriginFlow::PathNode source, + PlacementNewOriginFlow::PathNode sink, string message, PlacementNewMemoryOrigin memoryOrigin, + string memoryOriginDescription ) { not isExcluded(placementNew, getQuery()) and - exists(PlacementNewOriginConfig config | - memoryOrigin = source.getNode() and - placementNew.getPlacementExpr() = sink.getNode().asExpr() and - memoryOriginDescription = memoryOrigin.toString() and - config.hasFlowPath(source, sink) and - exists(int originAlignment | - originAlignment = memoryOrigin.getAlignment() and - // The origin alignment should be exactly divisible by the placement alignment - (originAlignment / placementNew.getAllocatedType().getAlignment()).ceil() = 0 and - message = "Placement new expression is used with inappropriately aligned memory from $@." - ) + memoryOrigin = source.getNode() and + placementNew.getPlacementExpr() = sink.getNode().asExpr() and + memoryOriginDescription = memoryOrigin.toString() and + PlacementNewOriginFlow::flowPath(source, sink) and + exists(int originAlignment | + originAlignment = memoryOrigin.getAlignment() and + // The origin alignment should be exactly divisible by the placement alignment + (originAlignment / placementNew.getAllocatedType().getAlignment()).ceil() = 0 and + message = "Placement new expression is used with inappropriately aligned memory from $@." ) } diff --git a/cpp/common/src/codingstandards/cpp/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.qll b/cpp/common/src/codingstandards/cpp/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.qll new file mode 100644 index 0000000000..667480a43a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.qll @@ -0,0 +1,32 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A comparison of a potentially virtual pointer to member function shall only be with + * nullptr. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class PotentiallyVirtualPointerOnlyComparesToNullptrSharedQuery extends Query { } + +Query getQuery() { result instanceof PotentiallyVirtualPointerOnlyComparesToNullptrSharedQuery } + +query predicate problems( + EqualityOperation equalityComparison, string message, MemberFunction virtualFunction, + string virtualFunction_string, Expr otherOperand, string otherOperand_string +) { + not isExcluded(equalityComparison, getQuery()) and + exists(FunctionAccess accessOperand | + virtualFunction.isVirtual() and + equalityComparison.getAnOperand() = accessOperand and + accessOperand.getTarget() = virtualFunction and + otherOperand = equalityComparison.getAnOperand() and + not otherOperand = accessOperand and + not otherOperand.getType() instanceof NullPointerType and + message = + "A pointer to member virtual function $@ is tested for equality with non-null-pointer-constant $@." and + virtualFunction_string = virtualFunction.getName() and + otherOperand_string = otherOperand.toString() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.qll b/cpp/common/src/codingstandards/cpp/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.qll index a8b6ab7576..ba2f6ed82a 100644 --- a/cpp/common/src/codingstandards/cpp/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.qll +++ b/cpp/common/src/codingstandards/cpp/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.qll @@ -1,5 +1,7 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * "Non-static data members or captured values of predicate function objects + * that are state related to this object's identity shall not be copied. */ import cpp @@ -7,6 +9,7 @@ import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions import codingstandards.cpp.SideEffect import codingstandards.cpp.sideeffect.DefaultEffects +import semmle.code.cpp.dataflow.DataFlow abstract class PredicateFunctionObjectsShouldNotBeMutableSharedQuery extends Query { } diff --git a/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll b/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll index 8b8609ab7b..8361a07a31 100644 --- a/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll +++ b/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll @@ -1,5 +1,7 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * Arguments to a function-like macro shall not contain tokens that look like + * pre-processing directives or else behaviour after macro expansion is unpredictable. */ import cpp diff --git a/cpp/common/src/codingstandards/cpp/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.qll b/cpp/common/src/codingstandards/cpp/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.qll index deed463b7a..5175a6edab 100644 --- a/cpp/common/src/codingstandards/cpp/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.qll +++ b/cpp/common/src/codingstandards/cpp/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.qll @@ -2,9 +2,9 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -abstract class PreprocessorIncludesForbiddenHeaderNamesQuery extends Query { } +abstract class PreprocessorIncludesForbiddenHeaderNamesSharedQuery extends Query { } -Query getQuery() { result instanceof PreprocessorIncludesForbiddenHeaderNamesQuery } +Query getQuery() { result instanceof PreprocessorIncludesForbiddenHeaderNamesSharedQuery } class InvalidInclude extends Include { InvalidInclude() { this.getIncludeText().regexpMatch("[\"<].*(['\"\\\\]|\\/\\*|\\/\\/).*[\">]") } diff --git a/cpp/common/src/codingstandards/cpp/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.qll b/cpp/common/src/codingstandards/cpp/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.qll index 3767f023a0..db755293c6 100644 --- a/cpp/common/src/codingstandards/cpp/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.qll +++ b/cpp/common/src/codingstandards/cpp/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.qll @@ -24,7 +24,11 @@ predicate getAnOrderedLockPair( lock1 = node.coveredByLock() and lock2 = node.coveredByLock() and not lock1 = lock2 and - lock1.getEnclosingFunction() = lock2.getEnclosingFunction() and + exists(Function f | + lock1.getEnclosingFunction() = f and + lock2.getEnclosingFunction() = f and + node.getBasicBlock().getEnclosingFunction() = f + ) and exists(Location l1Loc, Location l2Loc | l1Loc = lock1.getLocation() and l2Loc = lock2.getLocation() diff --git a/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll b/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll index 9de640db9c..8d701cb26c 100644 --- a/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll +++ b/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll @@ -131,6 +131,8 @@ class UninitializedVariable extends LocalVariable { // Not static or thread local, because they are not initialized with indeterminate values not isStatic() and not isThreadLocal() and + // Not atomic, which have special initialization rules + not getType().hasSpecifier("atomic") and // Not a class type, because default initialization of a class calls the default constructor // The default constructor may leave certain fields uninitialized, but that would be a separate // field-wise analysis diff --git a/cpp/common/src/codingstandards/cpp/rules/reinterpretcastused/ReinterpretCastUsed.qll b/cpp/common/src/codingstandards/cpp/rules/reinterpretcastused/ReinterpretCastUsed.qll new file mode 100644 index 0000000000..b49a488ab2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/reinterpretcastused/ReinterpretCastUsed.qll @@ -0,0 +1,16 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The statement reinterpret_cast shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class ReinterpretCastUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof ReinterpretCastUsedSharedQuery } + +query predicate problems(ReinterpretCast rc, string message) { + not isExcluded(rc, getQuery()) and message = "Use of reinterpret_cast." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.qll b/cpp/common/src/codingstandards/cpp/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.qll new file mode 100644 index 0000000000..04a106b5c4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.qll @@ -0,0 +1,18 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The result of an assignment operator should not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class ResultOfAnAssignmentOperatorShouldNotBeUsedSharedQuery extends Query { } + +Query getQuery() { result instanceof ResultOfAnAssignmentOperatorShouldNotBeUsedSharedQuery } + +query predicate problems(AssignExpr e, string message) { + not isExcluded(e, getQuery()) and + not exists(ExprStmt s | s.getExpr() = e) and + message = "Use of an assignment operator's result." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.qll b/cpp/common/src/codingstandards/cpp/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.qll new file mode 100644 index 0000000000..b37a9cd02b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.qll @@ -0,0 +1,36 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Functions that return a reference or a pointer to an automatic variable (including + * parameters) potentially lead to undefined behaviour. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class ReturnReferenceOrPointerToAutomaticLocalVariableSharedQuery extends Query { } + +Query getQuery() { result instanceof ReturnReferenceOrPointerToAutomaticLocalVariableSharedQuery } + +query predicate problems( + ReturnStmt rs, string message, Function f, string f_string, StackVariable auto, string auto_string +) { + exists(VariableAccess va, string returnType | + not isExcluded(rs, getQuery()) and + f = rs.getEnclosingFunction() and + ( + f.getType() instanceof ReferenceType and va = rs.getExpr() and returnType = "reference" + or + f.getType() instanceof PointerType and + va = rs.getExpr().(AddressOfExpr).getOperand() and + returnType = "pointer" + ) and + auto = va.getTarget() and + not auto.isStatic() and + not f.isCompilerGenerated() and + not auto.getType() instanceof ReferenceType and + message = "The $@ returns a " + returnType + "to an $@ variable" and + f_string = f.getName() and + auto_string = "automatic" + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.qll b/cpp/common/src/codingstandards/cpp/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.qll index 240b9b75da..fd56f5d899 100644 --- a/cpp/common/src/codingstandards/cpp/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.qll +++ b/cpp/common/src/codingstandards/cpp/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.qll @@ -31,8 +31,7 @@ class CharIStreamConstructorCall extends CharIStreamSource, Expr { } override Expr getAUse() { - any(CharIStreamConstructorCallUseConfig c) - .hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(result)) + CharIStreamConstructorCallUseFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(result)) } } @@ -40,18 +39,16 @@ class CharIStreamConstructorCall extends CharIStreamSource, Expr { * A global taint tracking configuration used to track from `CharIStream` constructor calls to uses * of that stream later in the program. */ -private class CharIStreamConstructorCallUseConfig extends TaintTracking::Configuration { - CharIStreamConstructorCallUseConfig() { this = "CharIStreamUse" } - - override predicate isSource(DataFlow::Node source) { +private module CharIStreamConstructorCallUseConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CharIStreamConstructorCall } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink.asExpr().getType().stripType() instanceof CharIStream } - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { // By default we do not get flow from ConstructorFieldInit expressions to accesses // of the field in other member functions, so we add it explicitly here. exists(ConstructorFieldInit cfi, Field f | @@ -63,6 +60,9 @@ private class CharIStreamConstructorCallUseConfig extends TaintTracking::Configu } } +private module CharIStreamConstructorCallUseFlow = + TaintTracking::Global; + /** * A `CharIStream` defined externally, and which therefore cannot be tracked as a source by taint tracking. * diff --git a/cpp/common/src/codingstandards/cpp/rules/switchcasepositioncondition/SwitchCasePositionCondition.qll b/cpp/common/src/codingstandards/cpp/rules/switchcasepositioncondition/SwitchCasePositionCondition.qll new file mode 100644 index 0000000000..979621762d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/switchcasepositioncondition/SwitchCasePositionCondition.qll @@ -0,0 +1,33 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The switch statement syntax is weak and may lead to unspecified behaviour. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.SwitchStatement + +abstract class SwitchCasePositionConditionSharedQuery extends Query { } + +Query getQuery() { result instanceof SwitchCasePositionConditionSharedQuery } + +//from SwitchStmt switch, SwitchCase case +//where +//not isExcluded(switch, ConditionalsPackage::switchDoesNotStartWithCaseQuery()) and +//case = switch.getASwitchCase() and +//switchWithCaseNotFirst(switch) +//select switch, +//"$@ statement not well formed because the first statement in a well formed switch statement must be a case clause.", +//switch, "Switch" +query predicate problems( + SwitchStmt switch, string message, SwitchStmt switchLocation, string switchMessage +) { + not isExcluded(switch, getQuery()) and + exists(SwitchCase case | case = switch.getASwitchCase()) and + switchWithCaseNotFirst(switch) and + switchLocation = switch and + switchMessage = "Switch" and + message = + "$@ statement not well formed because the first statement in a well formed switch statement must be a case clause." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/switchcompoundcondition/SwitchCompoundCondition.qll b/cpp/common/src/codingstandards/cpp/rules/switchcompoundcondition/SwitchCompoundCondition.qll new file mode 100644 index 0000000000..ab888abfec --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/switchcompoundcondition/SwitchCompoundCondition.qll @@ -0,0 +1,46 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * If the body of a switch is not enclosed in braces, then this can lead to incorrect + * execution, and hard for developers to maintain. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class SwitchCompoundConditionSharedQuery extends Query { } + +Query getQuery() { result instanceof SwitchCompoundConditionSharedQuery } + +/** + * Class to differentiate between extractor generated blockstmt and actual blockstmt. The extractor + * will generate an artificial blockstmt when there is a single case and statement, e.g. + * ``` + * switch(x) + * case 1: + * f(); + * ``` + * This is because our AST model considers the `case` to be a statement in its own right, so the + * extractor needs an aritifical block to hold both the case and the statement. + */ +class ArtificialBlock extends BlockStmt { + ArtificialBlock() { + exists(Location block, Location firstStatement | + block = getLocation() and firstStatement = getStmt(0).getLocation() + | + // We can identify artificial blocks as those where the start of the statement is at the same + // location as the start of the first statement in the block i.e. there was no opening brace. + block.getStartLine() = firstStatement.getStartLine() and + block.getStartColumn() = firstStatement.getStartColumn() + ) + } +} + +query predicate problems(SwitchStmt switch, string message) { + ( + switch.getStmt() instanceof ArtificialBlock or + not switch.getStmt() instanceof BlockStmt + ) and + not isExcluded(switch, getQuery()) and + message = "Switch body not enclosed within braces." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/switchnotwellformed/SwitchNotWellFormed.qll b/cpp/common/src/codingstandards/cpp/rules/switchnotwellformed/SwitchNotWellFormed.qll new file mode 100644 index 0000000000..cb2e61c3ad --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/switchnotwellformed/SwitchNotWellFormed.qll @@ -0,0 +1,27 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The switch statement syntax is weak and may lead to unspecified behaviour. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.SwitchStatement + +abstract class SwitchNotWellFormedSharedQuery extends Query { } + +Query getQuery() { result instanceof SwitchNotWellFormedSharedQuery } + +query predicate problems( + SwitchStmt switch, string message, SwitchStmt switchLocation, string switchMessage, + SwitchCase case, string caseMessage +) { + not isExcluded(switch, getQuery()) and + case = switch.getASwitchCase() and + switchCaseNotWellFormed(case) and + switch = switchLocation and + message = + "$@ statement not well formed because this $@ block uses a statement that is not allowed." and + switchMessage = "Switch" and + caseMessage = "case" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.qll b/cpp/common/src/codingstandards/cpp/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.qll index f44fd5a06a..e28ef7ab07 100644 --- a/cpp/common/src/codingstandards/cpp/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.qll +++ b/cpp/common/src/codingstandards/cpp/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.qll @@ -9,16 +9,14 @@ import codingstandards.cpp.allocations.CustomOperatorNewDelete import codingstandards.cpp.exceptions.ExceptionSpecifications import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -import DataFlow::PathGraph +import NullFlow::PathGraph abstract class ThrowingOperatorNewReturnsNullSharedQuery extends Query { } Query getQuery() { result instanceof ThrowingOperatorNewReturnsNullSharedQuery } -class NullConfig extends DataFlow::Configuration { - NullConfig() { this = "NullConfig" } - - override predicate isSource(DataFlow::Node source) { +module NullConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NullValue or // Call to an allocation function that may return null @@ -32,7 +30,7 @@ class NullConfig extends DataFlow::Configuration { ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(CustomOperatorNew co, ReturnStmt rs | co.getNumberOfParameters() = 1 and rs.getEnclosingFunction() = co and @@ -41,11 +39,13 @@ class NullConfig extends DataFlow::Configuration { } } +module NullFlow = DataFlow::Global; + query predicate problems( - ReturnStmt e, DataFlow::PathNode source, DataFlow::PathNode sink, string message + ReturnStmt e, NullFlow::PathNode source, NullFlow::PathNode sink, string message ) { not isExcluded(e, getQuery()) and - any(NullConfig nc).hasFlowPath(source, sink) and + NullFlow::flowPath(source, sink) and sink.getNode().asExpr() = e.getExpr() and exists(CustomOperatorNew op | message = diff --git a/cpp/common/src/codingstandards/cpp/rules/typeomitted/TypeOmitted.qll b/cpp/common/src/codingstandards/cpp/rules/typeomitted/TypeOmitted.qll index 420a384208..0906a1de4f 100644 --- a/cpp/common/src/codingstandards/cpp/rules/typeomitted/TypeOmitted.qll +++ b/cpp/common/src/codingstandards/cpp/rules/typeomitted/TypeOmitted.qll @@ -1,10 +1,12 @@ /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * Omission of type specifiers may not be supported by some compilers. */ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions +import codingstandards.cpp.Identifiers abstract class TypeOmittedSharedQuery extends Query { } @@ -12,13 +14,6 @@ Query getQuery() { result instanceof TypeOmittedSharedQuery } query predicate problems(Declaration d, string message) { not isExcluded(d, getQuery()) and - d.hasSpecifier("implicit_int") and - exists(Type t | - (d.(Variable).getType() = t or d.(Function).getType() = t) and - // Exclude "short" or "long", as opposed to "short int" or "long int". - t instanceof IntType and - // Exclude "signed" or "unsigned", as opposed to "signed int" or "unsigned int". - not exists(IntegralType it | it = t | it.isExplicitlySigned() or it.isExplicitlyUnsigned()) - ) and + isDeclaredImplicit(d) and message = "Declaration " + d.getName() + " is missing a type specifier." } diff --git a/cpp/common/src/codingstandards/cpp/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.qll b/cpp/common/src/codingstandards/cpp/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.qll new file mode 100644 index 0000000000..acc7888d2c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.qll @@ -0,0 +1,117 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unchecked range, domain and pole errors. + */ + +import cpp +import codingstandards.cpp.CodingStandards +import codingstandards.cpp.FloatingPoint +import codingstandards.cpp.FloatingPoint::SimpleDomainError +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +abstract class UncheckedRangeDomainPoleErrorsSharedQuery extends Query { } + +Query getQuery() { result instanceof UncheckedRangeDomainPoleErrorsSharedQuery } + +predicate hasRangeError(FunctionCall fc, string description) { + exists(Function functionWithRangeError | fc.getTarget() = functionWithRangeError | + functionWithRangeError.hasGlobalOrStdName(["abs", "labs", "llabs", "imaxabs"]) and + fc.getArgument(0) = any(MINMacro m).getAnInvocation().getExpr() and + description = "argument is most negative number" + ) +} + +predicate hasPoleError(FunctionCall fc, string description) { + exists(Function functionWithPoleError | fc.getTarget() = functionWithPoleError | + functionWithPoleError = getMathVariants("atanh") and + ( + fc.getArgument(0).getValue().toFloat() = -1.0 + or + fc.getArgument(0).getValue().toFloat() = 1.0 + ) and + description = "argument is plus or minus 1" + or + functionWithPoleError = getMathVariants("log1p") and + fc.getArgument(0).getValue().toFloat() = -1 and + description = "argument is equal to negative one" + or + functionWithPoleError = getMathVariants("pow") and + fc.getArgument(0).getValue().toFloat() = 0.0 and + fc.getArgument(1).getValue().toFloat() < 0.0 and + description = "base is zero and exp is negative" + or + functionWithPoleError = getMathVariants("lgamma") and + fc.getArgument(0).getValue().toFloat() = 0 and + description = "argument is equal to zero" + or + functionWithPoleError = getMathVariants(["log", "log10", "log2"]) and + fc.getArgument(0).getValue().toFloat() = 0.0 and + description = "argument is equal to zero" + ) +} + +predicate unspecifiedValueCases(FunctionCall fc, string description) { + exists(Function functionWithUnspecifiedResultError | + fc.getTarget() = functionWithUnspecifiedResultError + | + functionWithUnspecifiedResultError = getMathVariants("frexp") and + ( + fc.getArgument(0) = any(InfinityMacro m).getAnInvocation().getExpr() or + fc.getArgument(0) = any(NanMacro m).getAnInvocation().getExpr() + ) and + description = "Arg is Nan or infinity and exp is unspecified as a result" + ) +} + +/** + * A macro which is representing infinity + */ +class InfinityMacro extends Macro { + InfinityMacro() { this.getName().toLowerCase().matches("infinity") } +} + +/** + * A macro which is representing nan + */ +class NanMacro extends Macro { + NanMacro() { this.getName().toLowerCase().matches("nan") } +} + +/** + * A macro which is representing INT_MIN or LONG_MIN or LLONG_MIN + */ +class MINMacro extends Macro { + MINMacro() { this.getName().toLowerCase().matches(["int_min", "long_min", "llong_min"]) } +} + +/* + * Domain cases not covered by this query: + * - pow - x is finite and negative and y is finite and not an integer value. + * - tgamma - negative integer can't be covered. + * - lrint/llrint/lround/llround - no domain errors checked + * - remainder - no domain errors checked. + * - remquo - no domain errors checked. + * + * Pole cases not covered by this query: + * - lgamma - negative integer can't be covered. + * + * Implementations may also define their own domain errors (as per the C99 standard), which are not + * covered by this query. + */ + +query predicate problems(FunctionCall fc, string message) { + not isExcluded(fc, getQuery()) and + exists(string description | + hasDomainError(fc, description) and + message = "Domain error in call to '" + fc.getTarget().getName() + "': " + description + "." + or + hasRangeError(fc, description) and + message = "Range error in call to '" + fc.getTarget().getName() + "': " + description + "." + or + hasPoleError(fc, description) and + message = "Pole error in call to '" + fc.getTarget().getName() + "': " + description + "." + or + unspecifiedValueCases(fc, description) and + message = + "Unspecified error in call to '" + fc.getTarget().getName() + "': " + description + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.qll b/cpp/common/src/codingstandards/cpp/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.qll index 00f2a66394..d5cafe7416 100644 --- a/cpp/common/src/codingstandards/cpp/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.qll +++ b/cpp/common/src/codingstandards/cpp/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.qll @@ -1,9 +1,10 @@ import cpp import codingstandards.cpp.Exclusions +import codingstandards.cpp.PreprocessorDirective -abstract class UndefinedMacroIdentifiersQuery extends Query { } +abstract class UndefinedMacroIdentifiersSharedQuery extends Query { } -Query getQuery() { result instanceof UndefinedMacroIdentifiersQuery } +Query getQuery() { result instanceof UndefinedMacroIdentifiersSharedQuery } pragma[noinline] predicate isMacroAccessFileAndLine(MacroAccess ma, string filepath, int startLine) { @@ -76,17 +77,10 @@ string getAnIfDefdMacroIdentifier(PreprocessorBranch pb) { ) } -class IfAndElifs extends PreprocessorBranch { - IfAndElifs() { - this instanceof PreprocessorIf or - this instanceof PreprocessorElif - } -} - -class BadIfAndElifs extends IfAndElifs { +class UndefinedIdIfOrElif extends PreprocessorIfOrElif { string undefinedMacroIdentifier; - BadIfAndElifs() { + UndefinedIdIfOrElif() { exists(string defRM | defRM = this.getHead() @@ -113,7 +107,7 @@ class BadIfAndElifs extends IfAndElifs { string getAnUndefinedMacroIdentifier() { result = undefinedMacroIdentifier } } -query predicate problems(BadIfAndElifs b, string message) { +query predicate problems(UndefinedIdIfOrElif b, string message) { not isExcluded(b, getQuery()) and message = "#if/#elif that uses a macro identifier " + b.getAnUndefinedMacroIdentifier() + diff --git a/cpp/common/src/codingstandards/cpp/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.qll b/cpp/common/src/codingstandards/cpp/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.qll new file mode 100644 index 0000000000..695a8740b6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.qll @@ -0,0 +1,302 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unnecessarily exposed identifiers due to too broad of a scope. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Scope +import codingstandards.cpp.SideEffect +import codingstandards.cpp.sideeffect.Customizations + +class ExternalCall extends Call { + ExternalCall() { + exists(Function f | this.getTarget() = f | + not f.hasDefinition() and not f.isCompilerGenerated() + ) + } +} + +class LoopOrSwitchBody extends BlockStmt { + LoopOrSwitchBody() { + exists(Loop l | l.getStmt() = this.getParentScope*()) + or + exists(SwitchStmt ss | ss.getStmt() = this) + } +} + +/* Gets a scope for `b` that is an ancestor of `b`, but is not a loop or switch scope. */ +Scope getCandidateScope(Scope b) { + if b instanceof LoopOrSwitchBody or b instanceof ControlStructure + then result = getCandidateScope(b.getStrictParent()) + else + if b.isGenerated() + then result = b.getStrictParent() + else result = b +} + +private predicate getLocationInfo( + CandidateDeclaration d, PreprocessorBranchDirective pbd, int startline, int endline, string path1, + string path2 +) { + d.getLocation().getEndLine() = endline and + pbd.getLocation().getStartLine() = startline and + d.getFile().getAbsolutePath() = path1 and + pbd.getFile().getAbsolutePath() = path2 +} + +predicate isStrictlyBefore(CandidateDeclaration d, PreprocessorBranchDirective branch) { + exists(string path, int startLine, int endLine | + getLocationInfo(d, branch, startLine, endLine, path, path) and + endLine < startLine + ) +} + +Variable getADependentVariable(Variable v) { + exists(VariableAccess va | + va.getTarget() = result and v.getInitializer().getExpr().getAChild*() = va + ) +} + +/** + * Holds if it is assigned a value that is modified in between the declaration of `v` and a use of `v`. + */ +predicate isTempVariable(LocalVariable v) { + exists( + DeclStmt ds, VariableDeclarationEntry vde, Variable dependentVariable, Expr sideEffect, + VariableAccess va + | + v.getAnAccess() = va and + dependentVariable = getADependentVariable(v) and + exists( + BasicBlock declarationStmtBb, BasicBlock sideEffectBb, BasicBlock variableAccessBb, + int declarationStmtPos, int sideEffectPos, int variableAccessPos + | + declarationStmtBb.getNode(declarationStmtPos) = ds and + variableAccessBb.getNode(variableAccessPos) = va + | + ( + ( + sideEffect.(VariableEffect).getTarget() = dependentVariable and + if not sideEffect.getEnclosingFunction() = va.getEnclosingFunction() + then + exists(FunctionCall call | + call.getEnclosingFunction() = va.getEnclosingFunction() and + call.getTarget().calls(sideEffect.getEnclosingFunction()) and + sideEffectBb.getNode(sideEffectPos) = call + ) + else sideEffectBb.getNode(sideEffectPos) = sideEffect + ) + or + dependentVariable instanceof GlobalVariable and + sideEffect instanceof ExternalCall and + ds.getEnclosingFunction() = sideEffect.getEnclosingFunction() and + sideEffectBb.getNode(sideEffectPos) = sideEffect + ) and + ( + declarationStmtBb.getASuccessor+() = sideEffectBb + or + declarationStmtBb = sideEffectBb and declarationStmtPos < sideEffectPos + ) and + ( + sideEffectBb.getASuccessor+() = variableAccessBb + or + sideEffectBb = variableAccessBb and sideEffectPos < variableAccessPos + ) + ) and + vde.getDeclaration() = v and + ds.getDeclarationEntry(_) = vde + ) +} + +private predicate isTypeUse(Type t1, Type t2) { + t1.getUnspecifiedType() = t2 + or + t1.(PointerType).getBaseType().getUnspecifiedType() = t2 + or + t1.(ReferenceType).getBaseType().getUnspecifiedType() = t2 + or + t1.(ArrayType).getBaseType().getUnspecifiedType() = t2 +} + +newtype TDeclarationAccess = + ObjectAccess(Variable v, VariableAccess va) { + va = v.getAnAccess() or + v.(TemplateVariable).getAnInstantiation().getAnAccess() = va + } or + /* Type access can be done in a declaration or an expression (e.g., static member function call) */ + TypeAccess(Type t, Element access) { + isTypeUse(access.(Variable).getUnspecifiedType(), t) + or + exists(ClassTemplateInstantiation cti | + isTypeUse(cti.getATemplateArgument(), t) and + access.(Variable).getUnspecifiedType() = cti + ) + or + exists(FunctionTemplateInstantiation fti | + isTypeUse(fti.getATemplateArgument(), t) and + fti = access + ) + or + exists(FunctionCall call, MemberFunction mf | + call = access and call.getTarget() = mf and mf.isStatic() and mf.getDeclaringType() = t + ) + or + exists(Function f | + isTypeUse(f.getType(), t) and + f = access + ) + } + +class DeclarationAccess extends TDeclarationAccess { + Location getLocation() { + exists(VariableAccess va, Variable v | this = ObjectAccess(v, va) and result = va.getLocation()) + or + exists(Element access | + this = TypeAccess(_, access) and + result = access.getLocation() + ) + } + + string toString() { + exists(Variable v | this = ObjectAccess(v, _) and result = "Object access for " + v.getName()) + or + exists(Type t | + this = TypeAccess(t, _) and + result = "Type access for " + t.getName() + ) + } + + /* Gets the declaration that is being accessed. */ + Declaration getDeclaration() { + this = ObjectAccess(result, _) + or + this = TypeAccess(result, _) + } + + /* Gets the declaration or expression that uses the type being accessed. */ + Element getUnderlyingTypeAccess() { this = TypeAccess(_, result) } + + VariableAccess getUnderlyingObjectAccess() { this = ObjectAccess(_, result) } + + /* Gets the scope of the access. */ + Scope getScope() { + exists(VariableAccess va | + va = getUnderlyingObjectAccess() and + result.getAnExpr() = va + ) + or + exists(Element e | e = getUnderlyingTypeAccess() and result = e.getParentScope()) + } + + /* Holds if a type access is generated from the template instantiation `instantionion` */ + predicate isFromTemplateInstantiation(Element instantiation) { + exists(Element access | + this = TypeAccess(_, access) and access.isFromTemplateInstantiation(instantiation) + ) + } + + predicate isCompilerGenerated() { + exists(VariableAccess va | va = getUnderlyingObjectAccess() and va.isCompilerGenerated()) + or + exists(Element e | + e = getUnderlyingTypeAccess() and + (compgenerated(underlyingElement(e)) or compgenerated(underlyingElement(e.getParentScope()))) + ) + } +} + +class CandidateDeclaration extends Declaration { + CandidateDeclaration() { + this instanceof LocalVariable and + not this.(LocalVariable).isConstexpr() and + not this.isFromTemplateInstantiation(_) + or + this instanceof GlobalOrNamespaceVariable and + not this.isFromTemplateInstantiation(_) and + not this.(GlobalOrNamespaceVariable).isConstexpr() + or + this instanceof Type and + not this instanceof ClassTemplateInstantiation and + not this instanceof TemplateParameter + } +} + +/* Gets the scopes that include all the declaration accesses for declaration `d`. */ +Scope possibleScopesForDeclaration(CandidateDeclaration d) { + forex(Scope scope, DeclarationAccess da | + da.getDeclaration() = d and + // Exclude declaration accesses that are compiler generated so we can minimize the visibility of types. + // Otherwise, for example, we cannot reduce the scope of classes with compiler generated member functions based on + // declaration accesses. + not da.isCompilerGenerated() and + not da.isFromTemplateInstantiation(_) and + scope = da.getScope() + | + result = scope.getStrictParent*() + ) and + // Limit the best scope to block statements and namespaces or control structures + ( + result instanceof BlockStmt and + // Template variables cannot be in block scope + not d instanceof TemplateVariable + or + result instanceof Namespace + ) +} + +/* Gets the smallest scope that includes all the declaration accesses of declaration `d`. */ +Scope bestScopeForDeclarationEntry(CandidateDeclaration d, Scope currentScope) { + result = possibleScopesForDeclaration(d) and + not exists(Scope other | other = possibleScopesForDeclaration(d) | result = other.getAnAncestor()) and + currentScope.getADeclaration() = d and + result.getAnAncestor() = currentScope and + not result instanceof LoopOrSwitchBody and + not result.isGenerated() +} + +/** + * Gets a string suitable for printing a scope in an alert message, that includes an `$@` + * formatting string. + * + * This is necessary because some scopes (e.g. `Namespace`) do not have meaningful + * locations in the database and the alert message will not render the name if that is the case. + */ +string getScopeDescription(Scope s) { + if s instanceof GlobalNamespace then result = "the global namespace scope$@" else result = "$@" +} + +abstract class UnnecessaryExposedIdentifierDeclarationSharedSharedQuery extends Query { } + +Query getQuery() { result instanceof UnnecessaryExposedIdentifierDeclarationSharedSharedQuery } + +query predicate problems( + CandidateDeclaration d, string message, Scope currentScope, string msgP1, Scope candidateScope, + string msgP2 +) { + not isExcluded(d, getQuery()) and + candidateScope = bestScopeForDeclarationEntry(d, currentScope) and + // We can't reduce the scope if the value stored in the declaration is changed before the declared + // variable is used, because this would change the semantics of the use. + (d instanceof Variable implies not isTempVariable(d)) and + not exists(AddressOfExpr e | e.getAddressable() = d) and + // We can't reduce the scope of the declaration if its minimal scope resides inside a preprocessor + // branch directive while the current scope isn't. This can result in an incorrect program + // where a variable is used but not declared. + not exists(PreprocessorBranchDirective branch | + isStrictlyBefore(d, branch) and + branch = candidateScope.getAnEnclosingPreprocessorBranchDirective() + ) and + // We can't promote a class to a local class if it has static data members (See [class.local] paragraph 4 N3797.) + ( + (d instanceof Class and candidateScope.getStrictParent() instanceof Function) + implies + not exists(Variable member | d.(Class).getAMember() = member and member.isStatic()) + ) and + not candidateScope.isAffectedByMacro() and + msgP1 = "scope" and + msgP2 = "scope" and + message = + "The declaration " + d.getName() + " should be moved from " + getScopeDescription(currentScope) + + " into the " + getScopeDescription(candidateScope) + " too minimize its visibility." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/unreachablecode/UnreachableCode.qll b/cpp/common/src/codingstandards/cpp/rules/unreachablecode/UnreachableCode.qll new file mode 100644 index 0000000000..6697d14827 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/unreachablecode/UnreachableCode.qll @@ -0,0 +1,48 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unreachable code. + * + * This problems predicate identifies unreachable code at the level of `BasicBlock`. We use + * `BasicBlock`s for this because they represent sequences of statements which, according to the CFG, + * are either all unreachable or all reachable, because control flow cannot escape from the middle + * of the basic block. + * + * We use the `BasicBlock.isUnreachable()` predicate to identify `BasicBlock`s which are unreachable + * according to our calculated control flow graph. In practice, this can resolve expressions used in + * conditions which are constant, accesses of constant values (even across function boundaries), and + * operations, recursively, on such expressions. There is no attempt made to resolve conditional + * expressions which are not statically constant or derived directly from statically constant variables. + * + * One potential problem with using `BasicBlock`s is that for template functions the `BasicBlock` is + * duplicated across multiple `Function` instances, one for uninstantiated templates, and one for + * each instantiation. Rather than considering each template instantiation independently, we instead + * only report a `BasicBlock` in a template as unreachable, if it is unreachable in all template + * instantiations (and in the uninstantiated template). This helps avoid flagging examples such as + * `return 1` as dead code in this example, where `T::isVal()` is statically deducible in some + * template instantiations: + * ``` + * template int f() { + * if (T::isVal()) return 1; + * return 2; + * } + * ``` + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.deadcode.UnreachableCode + +abstract class UnreachableCodeSharedQuery extends Query { } + +Query getQuery() { result instanceof UnreachableCodeSharedQuery } + +query predicate problems(UnreachableBasicBlock b, string message, Function f, string functionName) { + // None of the basic blocks are excluded + not isExcluded(b.getABasicBlock(), getQuery()) and + message = "This statement in function $@ is unreachable." and + // Exclude results where at least one of the basic blocks appears in a macro expansion, as + // macros can easily result in unreachable blocks through no fault of the user of the macro + not inMacroExpansion(b.getABasicBlock()) and + f = b.getPrimaryFunction() and + functionName = f.getName() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.qll b/cpp/common/src/codingstandards/cpp/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.qll new file mode 100644 index 0000000000..a9535d9bfc --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.qll @@ -0,0 +1,31 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Unsigned integer literals shall be appropriately suffixed. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Cpp14Literal + +abstract class UnsignedIntegerLiteralsNotAppropriatelySuffixedSharedQuery extends Query { } + +Query getQuery() { result instanceof UnsignedIntegerLiteralsNotAppropriatelySuffixedSharedQuery } + +query predicate problems(Cpp14Literal::NumericLiteral nl, string message) { + exists(string literalKind | + not isExcluded(nl, getQuery()) and + ( + nl instanceof Cpp14Literal::OctalLiteral and literalKind = "Octal" + or + nl instanceof Cpp14Literal::HexLiteral and literalKind = "Hex" + or + nl instanceof Cpp14Literal::BinaryLiteral and literalKind = "Binary" + ) and + // This either directly has an unsigned integer type, or it is converted to an unsigned integer type + nl.getType().getUnspecifiedType().(IntegralType).isUnsigned() and + // The literal already has a `u` or `U` suffix. + not nl.getValueText().regexpMatch(".*[lL]*[uU][lL]*") and + message = literalKind + " literal is an unsigned integer but does not include a 'U' suffix." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.qll b/cpp/common/src/codingstandards/cpp/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.qll new file mode 100644 index 0000000000..bc0c6d8fc1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.qll @@ -0,0 +1,33 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * An unsigned arithmetic operation with constant operands should not wrap. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Overflow +import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +abstract class UnsignedOperationWithConstantOperandsWrapsSharedQuery extends Query { } + +Query getQuery() { result instanceof UnsignedOperationWithConstantOperandsWrapsSharedQuery } + +query predicate problems(InterestingOverflowingOperation op, string message) { + not isExcluded(op, getQuery()) and + op.getType().getUnderlyingType().(IntegralType).isUnsigned() and + // Not within a guard condition + not exists(GuardCondition gc | gc.getAChild*() = op) and + // Not guarded by a check, where the check is not an invalid overflow check + not op.hasValidPreCheck() and + // Is not checked after the operation + not op.hasValidPostCheck() and + // Permitted by exception 3 + not op instanceof LShiftExpr and + // Permitted by exception 2 - zero case is handled in separate query + not op instanceof DivExpr and + not op instanceof RemExpr and + message = + "Operation " + op.getOperator() + " of type " + op.getType().getUnderlyingType() + " may wrap." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll b/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll new file mode 100644 index 0000000000..2bdbc3887d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll @@ -0,0 +1,30 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unused parameters. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.deadcode.UnusedParameters + +abstract class UnusedParameterSharedQuery extends Query { } + +Query getQuery() { result instanceof UnusedParameterSharedQuery } + +predicate isMaybeUnusedParameter(Parameter parameter) { + parameter.getAnAttribute().toString() = "maybe_unused" +} + +predicate isLambdaParameter(Parameter parameter) { + exists(LambdaExpression lambda | lambda.getLambdaFunction().getParameter(_) = parameter) +} + +query predicate problems(UnusedParameter p, string message, Function f, string fName) { + not isExcluded(p, getQuery()) and + not isMaybeUnusedParameter(p) and + (if isLambdaParameter(p) then fName = "lambda expression" else fName = f.getQualifiedName()) and + f = p.getFunction() and + // Virtual functions are covered by a different rule + not f.isVirtual() and + message = "Unused parameter '" + p.getName() + "' for function $@." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/unusedtypedeclarations/UnusedTypeDeclarations.qll b/cpp/common/src/codingstandards/cpp/rules/unusedtypedeclarations/UnusedTypeDeclarations.qll new file mode 100644 index 0000000000..b6d085619a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/unusedtypedeclarations/UnusedTypeDeclarations.qll @@ -0,0 +1,21 @@ +/** + * Provides a library which includes a `problems` predicate for reporting unused types. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.types.Uses + +abstract class UnusedTypeDeclarationsSharedQuery extends Query { } + +Query getQuery() { result instanceof UnusedTypeDeclarationsSharedQuery } + +query predicate problems(UserType ut, string message) { + not isExcluded(ut, getQuery()) and + message = "Type declaration " + ut.getName() + " is not used." and + not ut instanceof TemplateParameter and + not ut instanceof ProxyClass and + not exists(getATypeUse(ut)) and + not ut.isFromUninstantiatedTemplate(_) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.qll b/cpp/common/src/codingstandards/cpp/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.qll new file mode 100644 index 0000000000..8cdb9cbbf0 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.qll @@ -0,0 +1,19 @@ +/** + * Provides a library which includes a `problems` predicate for reporting + * undocumented uses of assembly. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class UsageOfAssemblerNotDocumentedSharedQuery extends Query { } + +Query getQuery() { result instanceof UsageOfAssemblerNotDocumentedSharedQuery } + +query predicate problems(AsmStmt a, string message) { + not isExcluded(a, getQuery()) and + not exists(Comment c | c.getCommentedElement() = a) and + not a.isAffectedByMacro() and + message = "Use of assembler is not documented." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.qll b/cpp/common/src/codingstandards/cpp/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.qll new file mode 100644 index 0000000000..d81670b558 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.qll @@ -0,0 +1,59 @@ +/** + * Provides a library which includes a `problems` predicate for reporting initializers + * with brace structures that do not match the structure of the object being initialized. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.enhancements.AggregateLiteralEnhancements + +abstract class UseInitializerBracesToMatchAggregateTypeStructureSharedQuery extends Query { } + +Query getQuery() { result instanceof UseInitializerBracesToMatchAggregateTypeStructureSharedQuery } + +query predicate problems( + InferredAggregateLiteral inferredAggregateLiteral, string message, Type aggType, + string aggTypeName, Element explanationElement, string explanationDescription +) { + not isExcluded(inferredAggregateLiteral, getQuery()) and + // Not an inferred aggregate literal that acts as a "leading zero" for the root aggregate + // e.g. + // ``` + // int i[2][4] { 0 } + // ``` + // Has an inferred aggregate literal (i.e. it's `{ { 0 } }`), but we shouldn't report it + not isLeadingZeroInitialized(getRootAggregate(inferredAggregateLiteral)) and + // Provide a good message, dependending on the type of the parent + exists(string parentDescription | + // For class aggergate literal parents, report which field is being assigned to + exists(ClassAggregateLiteral cal, Field field | + cal.getAFieldExpr(field) = inferredAggregateLiteral and + parentDescription = "to field $@" and + explanationElement = field + | + explanationDescription = field.getName() + ) + or + // For array aggregate literal parents, report which index is being assigned to + exists(ArrayAggregateLiteral aal, int elementIndex | + aal.getAnElementExpr(elementIndex) = inferredAggregateLiteral and + parentDescription = "to index " + elementIndex + " in $@" and + explanationElement = aal and + explanationDescription = "array of type " + aal.getType().getName() + ) + or + // In some cases, we seem to have missing link, so provide a basic message + not any(ArrayAggregateLiteral aal).getAnElementExpr(_) = inferredAggregateLiteral and + not any(ClassAggregateLiteral aal).getAFieldExpr(_) = inferredAggregateLiteral and + parentDescription = "to an unnamed field of $@" and + explanationElement = inferredAggregateLiteral.getParent() and + explanationDescription = " " + explanationElement.(Expr).getType().getName() + | + aggTypeName = aggType.getName() and + message = + "Missing braces on aggregate literal of " + + getAggregateTypeDescription(inferredAggregateLiteral, aggType) + " which is assigned " + + parentDescription + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.qll b/cpp/common/src/codingstandards/cpp/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.qll new file mode 100644 index 0000000000..f5d1834723 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.qll @@ -0,0 +1,19 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * Octal constants shall not be used. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Cpp14Literal + +abstract class UseOfNonZeroOctalLiteralSharedQuery extends Query { } + +Query getQuery() { result instanceof UseOfNonZeroOctalLiteralSharedQuery } + +query predicate problems(Cpp14Literal::OctalLiteral octalLiteral, string message) { + not isExcluded(octalLiteral, getQuery()) and + not octalLiteral.getValue() = "0" and + message = "Non zero octal literal " + octalLiteral.getValueText() + "." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.qll b/cpp/common/src/codingstandards/cpp/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.qll index faa4442ba3..3b0abbad0d 100644 --- a/cpp/common/src/codingstandards/cpp/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.qll +++ b/cpp/common/src/codingstandards/cpp/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.qll @@ -10,21 +10,19 @@ import semmle.code.cpp.dataflow.DataFlow abstract class UseOnlyArrayIndexingForPointerArithmeticSharedQuery extends Query { } -class ArrayToArrayBaseConfig extends DataFlow::Configuration { - ArrayToArrayBaseConfig() { this = "ArrayToArrayBaseConfig" } - - override predicate isSource(DataFlow::Node source) { +module ArrayToArrayBaseConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr().(VariableAccess).getType() instanceof ArrayType or // Consider array to pointer decay for parameters. source.asExpr().(VariableAccess).getTarget().(Parameter).getType() instanceof ArrayType } - override predicate isSink(DataFlow::Node sink) { - exists(ArrayExpr e | e.getArrayBase() = sink.asExpr()) - } + predicate isSink(DataFlow::Node sink) { exists(ArrayExpr e | e.getArrayBase() = sink.asExpr()) } } +module ArrayToArrayBaseFlow = DataFlow::Global; + predicate hasPointerResult(PointerArithmeticOperation op) { op instanceof PointerAddExpr or @@ -34,8 +32,7 @@ predicate hasPointerResult(PointerArithmeticOperation op) { predicate shouldBeArray(ArrayExpr arrayExpr) { arrayExpr.getArrayBase().getUnspecifiedType() instanceof PointerType and not exists(VariableAccess va | - any(ArrayToArrayBaseConfig config) - .hasFlow(DataFlow::exprNode(va), DataFlow::exprNode(arrayExpr.getArrayBase())) + ArrayToArrayBaseFlow::flow(DataFlow::exprNode(va), DataFlow::exprNode(arrayExpr.getArrayBase())) ) and not exists(Variable v | v.getAnAssignedValue().getType() instanceof ArrayType and diff --git a/cpp/common/src/codingstandards/cpp/rules/validcontainerelementaccess/ValidContainerElementAccess.qll b/cpp/common/src/codingstandards/cpp/rules/validcontainerelementaccess/ValidContainerElementAccess.qll index 74c6b4f707..93e121d44c 100644 --- a/cpp/common/src/codingstandards/cpp/rules/validcontainerelementaccess/ValidContainerElementAccess.qll +++ b/cpp/common/src/codingstandards/cpp/rules/validcontainerelementaccess/ValidContainerElementAccess.qll @@ -36,7 +36,7 @@ query predicate problems( ContainerInvalidationOperation cio, string actionType ) { not isExcluded(cio, getQuery()) and - not isExcluded(ca) and + not isExcluded(ca, getQuery()) and // The definition of an invalidation is slightly different // for references vs iterators -- this check ensures that the conditions // under which a call should be an invalidator are considered correctly. diff --git a/cpp/common/src/codingstandards/cpp/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.qll b/cpp/common/src/codingstandards/cpp/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.qll new file mode 100644 index 0000000000..ca23306c55 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.qll @@ -0,0 +1,36 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * The std::vector specialization differs from all other containers + * std::vector such that sizeof bool is implementation defined which causes errors + * when using some STL algorithms. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.StdNamespace + +abstract class VectorShouldNotBeSpecializedWithBoolSharedQuery extends Query { } + +Query getQuery() { result instanceof VectorShouldNotBeSpecializedWithBoolSharedQuery } + +predicate isVectorBool(ClassTemplateInstantiation c) { + c.getNamespace() instanceof StdNS and + c.getTemplateArgument(0) instanceof BoolType and + c.getSimpleName() = "vector" +} + +predicate isUsingVectorBool(ClassTemplateInstantiation c) { + isVectorBool(c) or + isUsingVectorBool(c.getTemplateArgument(_)) +} + +query predicate problems(Variable v, string message) { + exists(ClassTemplateInstantiation c | + not isExcluded(v, getQuery()) and + v.getUnderlyingType() = c and + not v.isFromTemplateInstantiation(_) and + isUsingVectorBool(c) and + message = "Use of std::vector specialization." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.qll b/cpp/common/src/codingstandards/cpp/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.qll new file mode 100644 index 0000000000..f29d69d1ac --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.qll @@ -0,0 +1,40 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * An accessible base class shall not be both virtual and non-virtual in the same + * hierarchy. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class VirtualAndNonVirtualClassInTheHierarchySharedQuery extends Query { } + +Query getQuery() { result instanceof VirtualAndNonVirtualClassInTheHierarchySharedQuery } + +query predicate problems( + Class c3, string message, Class base, string base_string, ClassDerivation cd1, string cd1_string, + Class c2, string c2_string +) { + exists(Class c1, ClassDerivation cd2 | + not isExcluded(c3, getQuery()) and + // for each pair of classes, get all of their derivations + cd1 = c1.getADerivation() and + cd2 = c2.getADerivation() and + // where they share the same base class + base = cd1.getBaseClass() and + base = cd2.getBaseClass() and + // but one is virtual, and one is not, and the derivations are in different classes + cd1.isVirtual() and + not cd2.isVirtual() and + // and there is some 'other class' that derives from both of these classes + c3.derivesFrom*(c1) and + c3.derivesFrom*(c2) and + // and the base class is accessible from the 'other class' + c3.getAMemberFunction().getEnclosingAccessHolder().canAccessClass(base, c3) and + message = "Class inherits base class $@, which is derived virtual by $@ and non-virtual by $@." and + base_string = base.getName() and + cd1_string = cd1.getDerivedClass().toString() and + c2_string = cd2.getDerivedClass().toString() + ) +} diff --git a/cpp/common/src/codingstandards/cpp/sideeffect/DefaultEffects.qll b/cpp/common/src/codingstandards/cpp/sideeffect/DefaultEffects.qll index bb3fb7fad7..3302864e9c 100644 --- a/cpp/common/src/codingstandards/cpp/sideeffect/DefaultEffects.qll +++ b/cpp/common/src/codingstandards/cpp/sideeffect/DefaultEffects.qll @@ -16,7 +16,8 @@ private class ResourceReleaseCall extends ExternalSideEffect::Range { } private class DirectStaticStorageDurationVariableModification extends VariableEffect, - GlobalSideEffect::Range { + GlobalSideEffect::Range +{ DirectStaticStorageDurationVariableModification() { this.getTarget() instanceof StaticStorageDurationVariable } diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/CStdLib.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/CStdLib.qll index c91cc4a6c4..5a9a1f5324 100644 --- a/cpp/common/src/codingstandards/cpp/standardlibrary/CStdLib.qll +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/CStdLib.qll @@ -4,15 +4,15 @@ import cpp /** The function `std::quick_exit`. */ class StdQuickExit extends Function { - StdQuickExit() { hasQualifiedName("std", "quick_exit") } + StdQuickExit() { this.hasGlobalOrStdName("quick_exit") } } /** The function `std::abort`. */ class StdAbort extends Function { - StdAbort() { hasQualifiedName("std", "abort") } + StdAbort() { this.hasGlobalOrStdName("abort") } } /** The function `std::_Exit`. */ class Std_Exit extends Function { - Std_Exit() { hasQualifiedName("std", "_Exit") } + Std_Exit() { this.hasGlobalOrStdName("_Exit") } } diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll index bd5522e3eb..58d93de1a9 100644 --- a/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll @@ -125,7 +125,7 @@ class ImplicitFileAccess extends FileAccess { /** The expression corresponding to the accessed file */ override Expr getFileExpr() { - fileName = result.(VariableAccess).getTarget().(GlobalVariable).toString() or + result = any(MacroInvocation mi | mi.getMacroName() = fileName).getExpr() or fileName = result.findRootCause().(Macro).getName() } } @@ -144,7 +144,7 @@ class InBandErrorReadFunctionCall extends FileAccess { override Expr getFileExpr() { if this instanceof ImplicitFileAccess then result = this.(ImplicitFileAccess).getFileExpr() - else result = this.getArgument(0) + else result = [this.getArgument(0), this.getArgument(0).(AddressOfExpr).getAnOperand()] } } @@ -170,7 +170,8 @@ class FileReadFunctionCall extends FileAccess { override Expr getFileExpr() { if this instanceof ImplicitFileAccess then result = this.(ImplicitFileAccess).getFileExpr() - else result = this.getArgument(filePos) + else + result = [this.getArgument(filePos), this.getArgument(filePos).(AddressOfExpr).getAnOperand()] } } @@ -198,7 +199,8 @@ class FileWriteFunctionCall extends FileAccess { override Expr getFileExpr() { if this instanceof ImplicitFileAccess then result = this.(ImplicitFileAccess).getFileExpr() - else result = this.getArgument(filePos) + else + result = [this.getArgument(filePos), this.getArgument(filePos).(AddressOfExpr).getAnOperand()] } } @@ -209,7 +211,9 @@ class FileCloseFunctionCall extends FileAccess { FileCloseFunctionCall() { this.getTarget().hasGlobalName("fclose") } /** The expression corresponding to the accessed file */ - override Expr getFileExpr() { result = this.getArgument(0) } + override VariableAccess getFileExpr() { + result = [this.getArgument(0), this.getArgument(0).(AddressOfExpr).getAnOperand()] + } } /** @@ -221,5 +225,7 @@ class FilePositioningFunctionCall extends FileAccess { } /** The expression corresponding to the accessed file */ - override Expr getFileExpr() { result = this.getArgument(0) } + override Expr getFileExpr() { + result = [this.getArgument(0), this.getArgument(0).(AddressOfExpr).getAnOperand()] + } } diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/FileStreams.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/FileStreams.qll index ae1aa55b99..99eec1f5e0 100644 --- a/cpp/common/src/codingstandards/cpp/standardlibrary/FileStreams.qll +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/FileStreams.qll @@ -10,8 +10,9 @@ */ import cpp -import semmle.code.cpp.dataflow.DataFlow -import semmle.code.cpp.dataflow.TaintTracking +private import semmle.code.cpp.dataflow.DataFlow +private import semmle.code.cpp.dataflow.TaintTracking +private import codingstandards.cpp.Operator /** * A `basic_fstream` like `std::fstream` @@ -23,15 +24,31 @@ class FileStream extends ClassTemplateInstantiation { /** * A `basic_istream` like `std::istream` */ -class IStream extends ClassTemplateInstantiation { - IStream() { this.getTemplate().hasQualifiedName("std", "basic_istream") } +class IStream extends Type { + IStream() { + this.(Class).getQualifiedName().matches("std::basic\\_istream%") + or + this.getUnspecifiedType() instanceof IStream + or + this.(Class).getABaseClass() instanceof IStream + or + this.(ReferenceType).getBaseType() instanceof IStream + } } /** * A `basic_ostream` like `std::ostream` */ -class OStream extends ClassTemplateInstantiation { - OStream() { this.getTemplate().hasQualifiedName("std", "basic_ostream") } +class OStream extends Type { + OStream() { + this.(Class).getQualifiedName().matches("std::basic\\_ostream%") + or + this.getUnspecifiedType() instanceof OStream + or + this.(Class).getABaseClass() instanceof OStream + or + this.(ReferenceType).getBaseType() instanceof OStream + } } /** @@ -53,7 +70,7 @@ predicate sameStreamSource(FileStreamFunctionCall a, FileStreamFunctionCall b) { * Insertion `operator<<` and Extraction `operator>>` operators. */ class InsertionOperatorCall extends FileStreamFunctionCall { - InsertionOperatorCall() { this.getTarget().(Operator).hasQualifiedName("std", "operator<<") } + InsertionOperatorCall() { this.getTarget() instanceof StreamInsertionOperator } override Expr getFStream() { result = this.getQualifier() @@ -63,7 +80,7 @@ class InsertionOperatorCall extends FileStreamFunctionCall { } class ExtractionOperatorCall extends FileStreamFunctionCall { - ExtractionOperatorCall() { this.getTarget().(Operator).hasQualifiedName("std", "operator>>") } + ExtractionOperatorCall() { this.getTarget() instanceof StreamExtractionOperator } override Expr getFStream() { result = this.getQualifier() @@ -144,8 +161,7 @@ class FileStreamConstructorCall extends FileStreamSource, Expr { } override Expr getAUse() { - any(FileStreamConstructorCallUseConfig c) - .hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(result)) + FileStreamConstructorCallUseFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(result)) } } @@ -164,18 +180,14 @@ class FileStreamExternGlobal extends FileStreamSource, GlobalOrNamespaceVariable /** * A global taint tracking configuration to track `FileStream` uses in the program. */ -private class FileStreamConstructorCallUseConfig extends TaintTracking::Configuration { - FileStreamConstructorCallUseConfig() { this = "FileStreamUse" } +private module FileStreamConstructorCallUseConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof FileStreamConstructorCall } - override predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof FileStreamConstructorCall - } - - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink.asExpr().getType().stripType() instanceof FileStream } - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { // By default we do not get flow from ConstructorFieldInit expressions to accesses // of the field in other member functions, so we add it explicitly here. exists(ConstructorFieldInit cfi, Field f | @@ -186,3 +198,6 @@ private class FileStreamConstructorCallUseConfig extends TaintTracking::Configur ) } } + +private module FileStreamConstructorCallUseFlow = + TaintTracking::Global; diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/Random.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/Random.qll index 9fc23cfb5a..5bcdf3a739 100644 --- a/cpp/common/src/codingstandards/cpp/standardlibrary/Random.qll +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/Random.qll @@ -54,7 +54,8 @@ class RandomNumberEngineCreation extends TRandomNumberEngineCreation { * A `ConstructorCall` which targets a `RandomNumberEngine`. */ class RandomNumberEngineConstructorCall extends TRandomNumberEngineConstructorCall, - RandomNumberEngineCreation { + RandomNumberEngineCreation +{ ConstructorCall getConstructorCall() { this = TRandomNumberEngineConstructorCall(result) } override Element getExclusionElement() { result = getConstructorCall() } @@ -82,7 +83,8 @@ class RandomNumberEngineConstructorCall extends TRandomNumberEngineConstructorCa * This is because no `ConstructorCall`s are generated in this case. */ class RandomNumberEngineMemberVariableDefaultInit extends TRandomNumberEngineMemberVariableDefaultInit, - RandomNumberEngineCreation { + RandomNumberEngineCreation +{ MemberVariable getMemberVariable() { this = TRandomNumberEngineMemberVariableDefaultInit(result, _) } diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/String.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/String.qll index eb75dcfc96..f233410b89 100644 --- a/cpp/common/src/codingstandards/cpp/standardlibrary/String.qll +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/String.qll @@ -16,9 +16,9 @@ class StdBasicString extends ClassTemplateInstantiation { PointerType getConstCharTPointer() { exists(SpecifiedType specType | specType = result.getBaseType() and - specType.getBaseType() = getCharT() and specType.isConst() and - count(specType.getASpecifier()) = 1 + count(specType.getASpecifier()) = 1 and + (specType.getBaseType() = getCharT() or specType.getBaseType().getName() = "value_type") ) } @@ -45,7 +45,8 @@ class StdBasicString extends ClassTemplateInstantiation { Type getConstIteratorType() { exists(TypedefType t | t.getDeclaringType() = this and - t.getName() = "const_iterator" and + // Certain compilers user __const_iterator instead of const_iterator. + t.getName() = ["const_iterator", "__const_iterator"] and result = t ) } diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll new file mode 100644 index 0000000000..83983e1df4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -0,0 +1,532 @@ +import cpp +import codeql.util.Boolean +import codingstandards.cpp.types.Graph +import codingstandards.cpp.types.FunctionType + +module TypeNamesMatchConfig implements TypeEquivalenceSig { + predicate resolveTypedefs() { + // We don't want to resolve typedefs here, as we want to compare the names of the types. + none() + } +} + +/** + * The set of types that are used in function signatures. + */ +class FunctionSignatureType extends Type { + FunctionSignatureType() { + exists(FunctionDeclarationEntry f | + this = f.getType() or this = f.getAParameterDeclarationEntry().getType() + ) + } +} + +class VariableType extends Type { + VariableType() { this = any(VariableDeclarationEntry v).getType() } +} + +predicate parameterNamesUnmatched(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + pragma[only_bind_into](f1).getDeclaration() = pragma[only_bind_into](f2).getDeclaration() and + exists(string p1Name, string p2Name, int i | + p1Name = f1.getParameterDeclarationEntry(i).getName() and + p2Name = f2.getParameterDeclarationEntry(i).getName() + | + not p1Name = p2Name + ) +} + +/** + * Implements type compatibility as defined in C17, assuming typedefs have already been resolved. + * + * The default TypeEquivalence already handles the following: + * - A type is compatible with itself + * - All specifiers must match, but the order does not matter + * - Function types are compatible if they have the same return type and compatible parameters. + * + * Additional override for array sizes and enums are added here. + */ +module TypesCompatibleConfig implements TypeEquivalenceSig { + bindingset[t1, t2] + predicate equalLeafTypes(Type t1, Type t2) { + t1 = t2 + or + t1.(IntegralType).getCanonicalArithmeticType() = t2.(IntegralType).getCanonicalArithmeticType() + or + // Enum types are compatible with one of char, int, or signed int, but the implementation + // decides. + t1 instanceof Enum and + (t2 instanceof CharType or t2 instanceof IntType) + or + t2 instanceof Enum and + (t1 instanceof CharType or t1 instanceof IntType) + } + + bindingset[t1, t2] + predicate equalArrayTypes(ArrayType t1, ArrayType t2, Boolean baseTypesEqual) { + baseTypesEqual = true and + // Compatible array types have compatible element types. If both have a constant size then that + // size must match. + count(int i | i = [t1, t2].(ArrayType).getSize()) < 2 + } +} + +/** + * Utilize QlBuiltins::InternSets to efficiently compare the sets of specifiers on two types. + */ +bindingset[t1, t2] +private predicate specifiersMatchExactly(Type t1, Type t2) { + t1 = t2 + or + SpecifierSet::getSet(t1) = SpecifierSet::getSet(t2) +} + +/** + * Base predicate for `QlBuiltins::InternSets` to get the specifier set for a type. + * + * Note that this also efficiently handles complex typedef cases, where a specified type points to + * a typedef that points to another specified type. In this case, `getASpecifier()` will return all + * of specifiers, not just those above the TypedefType. + */ +Specifier getASpecifier(SpecifiedType key) { result = key.getASpecifier() } + +module SpecifierSet = QlBuiltins::InternSets; + +/** + * Signature module for handling various kinds of potentially recursive type equivalence using the + * module `TypeEquivalence`. + * + * The various kinds of types to be compared all have an overridable predicate with default + * behavior here, and a boolean flag that indicates whether the base types are equal. This pattern + * is used because we can't make a default implementation of a predicate such as + * `equalPointerTypes` that recurses into the `TypeEquivalence` module. Instead, the + * `TypeEquivalence` module drives all of the recursion, and these predicates take the result of + * that recursion and use it to determine whether the types are equivalent. + */ +signature module TypeEquivalenceSig { + /** + * Whether two leaf types are equivalent, such as `int`s and structs. By default, we assume only + * that types are equal to themselves and that equivalent arithmetic types are equal. + */ + bindingset[t1, t2] + default predicate equalLeafTypes(Type t1, Type t2) { + t1 = t2 + or + t1.(IntegralType).getCanonicalArithmeticType() = t2.(IntegralType).getCanonicalArithmeticType() + } + + /** + * A predicate to arbitrarily override the default behavior of the `TypeEquivalence` module, + * including preventing recursion. If this predicate holds for a pair of types, then + * `TypeEquivalence::equalTypes()` holds only if `areEqual` is true. + */ + bindingset[t1, t2] + default predicate overrideTypeComparison(Type t1, Type t2, Boolean areEqual) { none() } + + /** + * Whether two specified types are equivalent. By default, we assume that the specifier sets are + * exactly the same, and the inner types also match. + */ + bindingset[t1, t2] + default predicate equalSpecifiedTypes( + SpecifiedType t1, SpecifiedType t2, Boolean unspecifiedTypesEqual + ) { + specifiersMatchExactly(t1, t2) and + unspecifiedTypesEqual = true + } + + /** + * Whether two specified types are equivalent. By default, we only require that the base (pointed + * to) types match. + */ + bindingset[t1, t2] + default predicate equalPointerTypes(PointerType t1, PointerType t2, Boolean baseTypesEqual) { + baseTypesEqual = true + } + + /** + * Whether two array types are equivalent. By default, we only require that the element types and + * array sizes match. + */ + bindingset[t1, t2] + default predicate equalArrayTypes(ArrayType t1, ArrayType t2, Boolean baseTypesEqual) { + t1.getSize() = t2.getSize() and + baseTypesEqual = true + } + + /** + * Whether two reference types are equivalent. By default, we only require that the base types match. + */ + bindingset[t1, t2] + default predicate equalReferenceTypes(ReferenceType t1, ReferenceType t2, Boolean baseTypesEqual) { + baseTypesEqual = true + } + + /** + * Whether typedefs should be resolved before comparison. By default, we assume `TypeEquivalence` + * should resolve typedefs before comparison. + */ + default predicate resolveTypedefs() { any() } + + /** + * Whether two typedef types are equivalent. + * + * This predicate is only used if `resolveTypedefs()` is false. If so, then we assume two + * typedefs are the same if they have the same name and their base types are equal. + */ + bindingset[t1, t2] + default predicate equalTypedefTypes(TypedefType t1, TypedefType t2, Boolean baseTypesEqual) { + t1.getName() = t2.getName() and + baseTypesEqual = true + } + + /** + * Whether two routine types are equivalent. By default, we only require that the return types and + * parameter types match. + */ + bindingset[t1, t2] + default predicate equalRoutineTypes( + RoutineType t1, RoutineType t2, Boolean returnTypeEqual, Boolean parameterTypesEqual + ) { + returnTypeEqual = true and parameterTypesEqual = true + } + + /** + * Whether two function pointer/reference types are equivalent. By default, we only require that + * the return types and parameter types match. + */ + bindingset[t1, t2] + default predicate equalFunctionPointerIshTypes( + FunctionPointerIshType t1, FunctionPointerIshType t2, Boolean returnTypeEqual, + Boolean parameterTypesEqual + ) { + returnTypeEqual = true and parameterTypesEqual = true + } +} + +/** + * The default equivalence behavior for the `TypeEquivalence` module. + */ +module DefaultEquivalence implements TypeEquivalenceSig { } + +/** + * A signature predicate used to restrict the set of types considered by `TypeEquivalence`, for + * performance reasons. + */ +signature predicate interestedInEquality(Type a, Type b); + +/** + * A module to check the equivalence of two types, as defined by the provided `TypeEquivalenceSig`. + * + * For performance reasons, this module is designed to be used with a predicate + * `interestedInEquality` that restricts the set of considered pairwise comparisons. + * + * To use this module, define a `TypeEquivalenceSig` module and implement a subset of `Type` that + * selects the relevant root types to be considered. Then use the predicate `equalTypes(a, b)`. + * Note that `equalTypes(a, b)` only holds if `interestedIn(a, b)` holds. A type is always + * considered to be equal to itself, and this module does not support configurations that declare + * otherwise. Additionally, `interestedIn(a, b)` implies `interestedIn(b, a)`. + * + * This module will recursively select pairs of types to be compared. For instance, if + * `interestedInEquality(a, b)` holds, then types `a` and `b` will be compared. If + * `Config::equalPointerTypes(a, b, true)` holds, then the pointed-to types of `a` and `b` will be + * compared. However, if `Config::equalPointerTypes(a, b, false)` holds, then `a` and `b` will be + * compared, but their pointed-to types will not. Similarly, inner types will not be compared if + * `Config::overrideTypeComparison(a, b, _)` holds. For detail, see the module predicates + * `shouldRecurseOn` and `interestedInNestedTypes`. + */ +module TypeEquivalence { + /** + * Performance-related predicate that holds for a pair of types `(a, b)` such that + * `interestedIn(a, b)` holds, or there exists a pair of types `(c, d)` such that + * `interestedIn(c, d)` holds, and computing `equalTypes(a, b)` requires computing + * `equalTypes(c, d)`. + * + * The goal of this predicate is to force top down rather than bottom up evaluation of type + * equivalence. That is to say, if we compare array types `int[]` and `int[]`, we to compare that + * both types are arrays first, and then compare that their base types are equal. Naively, CodeQL + * is liable to compute this kind of recursive equality in a bottom up fashion, where the cross + * product of all types is considered in computing `equalTypes(a, b)`. + * + * This interoperates with the predicate `shouldRecurseOn` to find types that will be compared, + * along with the inner types of those types that will be compared. See `shouldRecurseOn` for + * cases where this algorithm will or will not recurse. We still need to know which types are + * compared, even if we do not recurse on them, in order to properly constrain `equalTypes(x, y)` + * to hold for types such as leaf types, where we do not recurse during comparison. + * + * At each stage of recursion, we specify `pragma[only_bind_into]` to ensure that the + * prior `shouldRecurseOn` results are considered first in the pipeline. + */ + private predicate interestedInNestedTypes(Type t1, Type t2) { + // Base case: config specifies that these root types will be compared. + interestedInUnordered(t1, t2) + or + // If derived types are compared, their base types must be compared. + exists(DerivedType t1Derived, DerivedType t2Derived | + not t1Derived instanceof SpecifiedType and + not t2Derived instanceof SpecifiedType and + shouldRecurseOn(pragma[only_bind_into](t1Derived), pragma[only_bind_into](t2Derived)) and + t1 = t1Derived.getBaseType() and + t2 = t2Derived.getBaseType() + ) + or + // If specified types are compared, their unspecified types must be compared. + exists(SpecifiedType t1Spec, SpecifiedType t2Spec | + shouldRecurseOn(pragma[only_bind_into](t1Spec), pragma[only_bind_into](t2Spec)) and + ( + t1 = unspecify(t1Spec) and + t2 = unspecify(t2Spec) + ) + ) + or + // If function types are compared, their return types and parameter types must be compared. + exists(FunctionType t1Func, FunctionType t2Func | + shouldRecurseOn(pragma[only_bind_into](t1Func), pragma[only_bind_into](t2Func)) and + ( + t1 = t1Func.getReturnType() and + t2 = t2Func.getReturnType() + or + exists(int i | + t1 = t1Func.getParameterType(pragma[only_bind_out](i)) and + t2 = t2Func.getParameterType(i) + ) + ) + ) + or + // If the config says to resolve typedefs, and a typedef type is compared to a non-typedef + // type, then the non-typedef type must be compared to the base type of the typedef. + Config::resolveTypedefs() and + exists(TypedefType tdtype | + tdtype.getBaseType() = t1 and + shouldRecurseOn(pragma[only_bind_into](tdtype), t2) + or + tdtype.getBaseType() = t2 and + shouldRecurseOn(t1, pragma[only_bind_into](tdtype)) + ) + or + // If two typedef types are compared, then their base types must be compared. + exists(TypedefType t1Typedef, TypedefType t2Typedef | + shouldRecurseOn(pragma[only_bind_into](t1Typedef), pragma[only_bind_into](t2Typedef)) and + ( + t1 = t1Typedef.getBaseType() and + t2 = t2Typedef.getBaseType() + ) + ) + } + + /** + * Performance related predicate to force top down rather than bottom up evaluation of type + * equivalence. + * + * This predicate is used to determine whether we should recurse on a type. It is used in + * conjunction with the `interestedInNestedTypes` predicate to only recurse on types that are + * being compared. + * + * We don't recurse on identical types, as they are already equal. We also don't recurse on + * types that are overriden by `Config::overrideTypeComparison`, as that predicate determines + * their equalivance. + * + * For the other types, we have a set of predicates such as `Config::equalPointerTypes` that + * holds for `(x, y, true)` if the types `x` and `y` should be considered equivalent when the + * pointed-to types of `x` and `y` are equivalent. If the predicate does not hold, or holds for + * `(x, y, false)`, then we do not recurse on the types. + * + * We do not recurse on leaf types. + */ + private predicate shouldRecurseOn(Type t1, Type t2) { + // We only recurse on types we are comparing. + interestedInNestedTypes(pragma[only_bind_into](t1), pragma[only_bind_into](t2)) and + // We don't recurse on identical types, as they are already equal. + not t1 = t2 and + // We don't recurse on overriden comparisons + not Config::overrideTypeComparison(t1, t2, _) and + ( + // These pointer types are equal if their base types are equal: recurse. + Config::equalPointerTypes(t1, t2, true) + or + // These array types are equal if their base types are equal: recurse. + Config::equalArrayTypes(t1, t2, true) + or + // These reference types are equal if their base types are equal: recurse. + Config::equalReferenceTypes(t1, t2, true) + or + // These routine types are equal if their return and parameter types are equal: recurse. + Config::equalRoutineTypes(t1, t2, true, true) + or + // These function pointer-ish types are equal if their return and parameter types are equal: recurse. + Config::equalFunctionPointerIshTypes(t1, t2, true, true) + or + // These typedef types are equal if their base types are equal: recurse. + Config::equalTypedefTypes(t1, t2, true) + or + // These specified types are equal if their unspecified types are equal: recurse. + Config::equalSpecifiedTypes(t1, t2, true) + or + // We resolve typedefs, and one of these types is a typedef type: recurse. + Config::resolveTypedefs() and + ( + t1 instanceof TypedefType + or + t2 instanceof TypedefType + ) + ) + } + + /** + * Check whether two types are equivalent, as defined by the `TypeEquivalenceSig` module. + * + * This only holds if the specified predicate `interestedIn` holds for the types, or + * `interestedInNestedTypes` holds for the types, and holds if `t1` and `t2` are identical, + * regardless of how `TypeEquivalenceSig` is defined. + */ + predicate equalTypes(Type t1, Type t2) { + interestedInNestedTypes(pragma[only_bind_into](t1), pragma[only_bind_into](t2)) and + ( + t1 = t2 + or + not t1 = t2 and + if Config::overrideTypeComparison(t1, t2, _) + then Config::overrideTypeComparison(t1, t2, true) + else ( + equalLeafRelation(t1, t2) + or + equalDerivedTypes(t1, t2) + or + equalFunctionTypes(t1, t2) + or + Config::resolveTypedefs() and + ( + equalTypes(t1.(TypedefType).getBaseType(), t2) + or + equalTypes(t1, t2.(TypedefType).getBaseType()) + ) + or + not Config::resolveTypedefs() and + equalTypedefTypes(t1, t2) + ) + ) + } + + /** Whether two types will be compared, regardless of order (a, b) or (b, a). */ + private predicate interestedInUnordered(Type t1, Type t2) { + interestedIn(t1, t2) or + interestedIn(t2, t1) + } + + bindingset[t1, t2] + private predicate equalLeafRelation(LeafType t1, LeafType t2) { Config::equalLeafTypes(t1, t2) } + + bindingset[t] + private Type unspecify(SpecifiedType t) { + // This subtly and importantly handles the complicated cases of typedefs. Under most scenarios, + // if we see a typedef in `equalTypes()` we can simply get the base type and continue. However, + // there is an exception if we have a specified type that points to a typedef that points to + // another specified type. In this case, `SpecifiedType::getASpecifier()` will return all of + // specifiers, not just those above the TypedefType, and `stripTopLevelSpecifiers` will return + // the innermost type that is not a TypedefType or a SpecifiedType, which is what we want, as + // all specifiers have already been accounted for when we visit the outermost `SpecifiedType`. + if Config::resolveTypedefs() + then result = t.(SpecifiedType).stripTopLevelSpecifiers() + else result = t.(SpecifiedType).getBaseType() + } + + bindingset[t1, t2] + private predicate equalDerivedTypes(DerivedType t1, DerivedType t2) { + exists(Boolean baseTypesEqual | + (baseTypesEqual = true implies equalTypes(t1.getBaseType(), t2.getBaseType())) and + ( + Config::equalPointerTypes(t1, t2, baseTypesEqual) + or + Config::equalArrayTypes(t1, t2, baseTypesEqual) + or + Config::equalReferenceTypes(t1, t2, baseTypesEqual) + ) + ) + or + exists(Boolean unspecifiedTypesEqual | + // Note that this case is different from the above, in that we don't merely get the base + // type (as that could be a TypedefType that points to another SpecifiedType). We need to + // unspecify the type to see if the base types are equal. + (unspecifiedTypesEqual = true implies equalTypes(unspecify(t1), unspecify(t2))) and + Config::equalSpecifiedTypes(t1, t2, unspecifiedTypesEqual) + ) + } + + bindingset[t1, t2] + private predicate equalFunctionTypes(FunctionType t1, FunctionType t2) { + exists(Boolean returnTypeEqual, Boolean parameterTypesEqual | + (returnTypeEqual = true implies equalTypes(t1.getReturnType(), t2.getReturnType())) and + ( + parameterTypesEqual = true + implies + forall(int i | exists([t1, t2].getParameterType(i)) | + equalTypes(t1.getParameterType(i), t2.getParameterType(i)) + ) + ) and + ( + Config::equalRoutineTypes(t1, t2, returnTypeEqual, parameterTypesEqual) + or + Config::equalFunctionPointerIshTypes(t1, t2, returnTypeEqual, parameterTypesEqual) + ) + ) + } + + bindingset[t1, t2] + private predicate equalTypedefTypes(TypedefType t1, TypedefType t2) { + exists(Boolean baseTypesEqual | + (baseTypesEqual = true implies equalTypes(t1.getBaseType(), t2.getBaseType())) and + Config::equalTypedefTypes(t1, t2, baseTypesEqual) + ) + } +} + +signature predicate interestedInFunctionDeclarations( + FunctionDeclarationEntry f1, FunctionDeclarationEntry f2 +); + +module FunctionDeclarationTypeEquivalence< + TypeEquivalenceSig Config, interestedInFunctionDeclarations/2 interestedInFunctions> +{ + private predicate interestedInReturnTypes(Type a, Type b) { + exists(FunctionDeclarationEntry aFun, FunctionDeclarationEntry bFun | + interestedInFunctions(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and + a = aFun.getType() and + b = bFun.getType() + ) + } + + private predicate interestedInParameterTypes(Type a, Type b) { + exists(FunctionDeclarationEntry aFun, FunctionDeclarationEntry bFun, int i | + interestedInFunctions(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and + a = aFun.getParameterDeclarationEntry(i).getType() and + b = bFun.getParameterDeclarationEntry(i).getType() + ) + } + + predicate equalReturnTypes(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + interestedInFunctions(f1, f2) and + TypeEquivalence::equalTypes(f1.getType(), f2.getType()) + } + + predicate equalParameterTypes(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + interestedInFunctions(f1, f2) and + f1.getDeclaration() = f2.getDeclaration() and + forall(int i | exists([f1, f2].getParameterDeclarationEntry(i)) | + equalParameterTypesAt(f1, f2, pragma[only_bind_into](i)) + ) + } + + predicate equalParameterTypesAt(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2, int i) { + interestedInFunctions(f1, f2) and + f1.getDeclaration() = f2.getDeclaration() and + TypeEquivalence::equalTypes(f1.getParameterDeclarationEntry(pragma[only_bind_into](i)) + .getType(), f2.getParameterDeclarationEntry(pragma[only_bind_into](i)).getType()) + } +} + +private class LeafType extends Type { + LeafType() { + not this instanceof DerivedType and + not this instanceof FunctionType and + not this instanceof FunctionType + } +} diff --git a/cpp/common/src/codingstandards/cpp/types/FunctionType.qll b/cpp/common/src/codingstandards/cpp/types/FunctionType.qll new file mode 100644 index 0000000000..20166322f1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/FunctionType.qll @@ -0,0 +1,19 @@ +import cpp + +/** + * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` + * don't have a common ancestor. + */ +class FunctionType extends Type { + FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } + + Type getReturnType() { + result = this.(RoutineType).getReturnType() or + result = this.(FunctionPointerIshType).getReturnType() + } + + Type getParameterType(int i) { + result = this.(RoutineType).getParameterType(i) or + result = this.(FunctionPointerIshType).getParameterType(i) + } +} diff --git a/cpp/common/src/codingstandards/cpp/types/Graph.qll b/cpp/common/src/codingstandards/cpp/types/Graph.qll new file mode 100644 index 0000000000..70c51a40ba --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Graph.qll @@ -0,0 +1,15 @@ +import cpp + +predicate typeGraph(Type t, Type refersTo) { + refersTo = t.(DerivedType).getBaseType() + or + refersTo = t.(RoutineType).getReturnType() + or + refersTo = t.(RoutineType).getAParameterType() + or + refersTo = t.(FunctionPointerIshType).getReturnType() + or + refersTo = t.(FunctionPointerIshType).getAParameterType() + or + refersTo = t.(TypedefType).getBaseType() +} diff --git a/cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll b/cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll new file mode 100644 index 0000000000..252e783438 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll @@ -0,0 +1,38 @@ +import cpp + +/** + * Get the type of an lvalue after lvalue conversion. + * + * This will return the type itself if no conversion is performed. + */ +Type getLvalueConverted(Type t) { + if exists(performLvalueConversion(t, _)) + then result = performLvalueConversion(t, _) + else result = t +} + +/** + * Perform lvalue conversion on a type, allowing for a description of why the type was converted + * if it was. + * + * Does not return a value if no lvalue conversion was performed. + * + * Warning: This predicate may not return a result if the resulting type is not in the database. + * For convenience, this is accepted here, otherwise we would have to create a new type to return + * that wouldn't implement the type APIs and likely wouldn't be very useful. + */ +Type performLvalueConversion(Type t, string reason) { + result.(PointerType).getBaseType() = t.(ArrayType).getBaseType() and + reason = "array-to-pointer decay" + or + t instanceof RoutineType and + result.(PointerType).getBaseType() = t and + reason = "function-to-function-pointer decay" + or + isObjectType(t) and + exists(t.getASpecifier()) and + result = t.stripTopLevelSpecifiers() and + reason = "qualifiers removed" +} + +private predicate isObjectType(Type t) { not t.stripTopLevelSpecifiers() instanceof PointerType } diff --git a/cpp/common/src/codingstandards/cpp/types/Pointers.qll b/cpp/common/src/codingstandards/cpp/types/Pointers.qll new file mode 100644 index 0000000000..28b6abc340 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Pointers.qll @@ -0,0 +1,127 @@ +/** + * A module for representing pointers + */ + +import cpp +import codingstandards.cpp.Type + +/** + * A type that is a pointer or array type after stripping top-level specifiers. + */ +class PointerOrArrayType extends DerivedType { + PointerOrArrayType() { + this.stripTopLevelSpecifiers() instanceof PointerType or + this.stripTopLevelSpecifiers() instanceof ArrayType + } +} + +/** + * A type that is a pointer or array type. + */ +class UnspecifiedPointerOrArrayType extends DerivedType { + UnspecifiedPointerOrArrayType() { + this instanceof PointerType or + this instanceof ArrayType + } +} + +/** + * 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() } +} + +/** + * A null pointer constant, which is either in the form `NULL` or `(void *)0`. + */ +predicate isNullPointerConstant(Expr e) { + e.findRootCause() instanceof NullMacro + or + // 8.11 Pointer type conversions states: + // A null pointer constant, i.e. the value 0, optionally cast to void *. + e instanceof Zero + or + isNullPointerConstant(e.(Conversion).getExpr()) +} + +predicate isCastNullPointerConstant(Cast c) { + isNullPointerConstant(c.getExpr()) and + c.getUnderlyingType() instanceof PointerType +} + +/** + * A type representing a pointer to object + */ +class PointerToObjectType extends PointerType { + PointerToObjectType() { + not ( + this.getUnspecifiedType() instanceof FunctionPointerType or + this.getUnspecifiedType() instanceof VoidPointerType or + this.getBaseType().getUnspecifiedType() instanceof IncompleteType + ) + } +} + +/** + * 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. + */ +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 +} diff --git a/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll b/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll new file mode 100644 index 0000000000..a31400a340 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll @@ -0,0 +1,77 @@ +/** + * Helper predicates related to C11/C17 constraints on simple assignment between two types. + * + * Currently only a subset of the constraints are implemented, specifically those + * related to pointer types. + */ + +import codingstandards.cpp.types.LvalueConversion +import codingstandards.cpp.types.Compatible + +module SimpleAssignment { + /** + * Whether a pair of qualified or unqualified pointer types satisfy the simple assignment + * constraints from 6.5.16.1. + * + * There are additional constraints not implemented here involving one or more arithmetic types. + */ + predicate satisfiesSimplePointerAssignment(Type left, Type right) { + checksAssignment(left, right) and + simplePointerAssignmentImpl(getLvalueConverted(left), right) + } + + private predicate satisfiedWhenTypesCompatible(Type left, Type right, Type checkA, Type checkB) { + interestedInTypes(left, right) and + exists(Type leftBase, Type rightBase | + // The left operand has atomic, qualified, or unqualified pointer type: + leftBase = left.stripTopLevelSpecifiers().(PointerType).getBaseType() and + rightBase = right.stripTopLevelSpecifiers().(PointerType).getBaseType() and + ( + // and both operands are pointers to qualified or unqualified versions of compatible types: + checkA = leftBase.stripTopLevelSpecifiers() and + checkB = rightBase.stripTopLevelSpecifiers() + ) and + // and the type pointed to by the left has all the qualifiers of the type pointed to by the + // right: + forall(Specifier s | s = rightBase.getASpecifier() | s = leftBase.getASpecifier()) + ) + } + + predicate interestedInTypes(Type left, Type right) { + exists(Type unconverted | + left = getLvalueConverted(unconverted) and + checksAssignment(unconverted, right) + ) + } + + predicate checksCompatibility(Type left, Type right) { + // Check if the types are compatible + exists(Type assignA, Type assignB | + checksAssignment(assignA, assignB) and + satisfiedWhenTypesCompatible(assignA, assignB, left, right) + ) + } + + /** + * Implementation of 6.5.16.1 for a pair of pointer types, that assumes lvalue conversion has been + * performed on the left operand. + */ + bindingset[left, right] + private predicate simplePointerAssignmentImpl(Type left, Type right) { + exists(Type checkA, Type checkB | + satisfiedWhenTypesCompatible(left, right, checkA, checkB) and + TypeEquivalence::equalTypes(checkA, checkB) + ) + or + exists(Type leftBase, Type rightBase | + // The left operand has atomic, qualified, or unqualified pointer type: + leftBase = left.stripTopLevelSpecifiers().(PointerType).getBaseType() and + rightBase = right.stripTopLevelSpecifiers().(PointerType).getBaseType() and + // or one operand is a pointer to a qualified or unqualified version of void + [leftBase, rightBase].stripTopLevelSpecifiers() instanceof VoidType and + // and the type pointed to by the left has all the qualifiers of the type pointed to by the + // right: + forall(Specifier s | s = rightBase.getASpecifier() | s = leftBase.getASpecifier()) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/TrivialType.qll b/cpp/common/src/codingstandards/cpp/types/TrivialType.qll similarity index 97% rename from cpp/common/src/codingstandards/cpp/TrivialType.qll rename to cpp/common/src/codingstandards/cpp/types/TrivialType.qll index bbbdea852d..1965f12934 100644 --- a/cpp/common/src/codingstandards/cpp/TrivialType.qll +++ b/cpp/common/src/codingstandards/cpp/types/TrivialType.qll @@ -48,7 +48,7 @@ predicate hasTrivialMoveConstructor(Class c) { forall(Class baseClass | baseClass = c.getABaseClass() | hasTrivialMoveConstructor(baseClass)) and // The class has to be defined, otherwise we may not see the information required to deduce // whether it does or does not have a trivial move constructor - c.isDefined() + c.hasDefinition() } /** A trivial copy or move constructor (see [class.copy]/12). */ @@ -281,10 +281,21 @@ predicate isTrivialType(Type t) { isTrivialType(t.getUnspecifiedType()) } +/** Holds if `t` is a trivially copyable type. */ +predicate isTriviallyCopyableType(Type t) { + isScalarType(t) + or + t instanceof TriviallyCopyableClass + or + isTriviallyCopyableType(t.(ArrayType).getBaseType()) + or + isTriviallyCopyableType(t.getUnspecifiedType()) +} + /** A POD type as defined by [basic.types]/9. */ class PODType extends Type { PODType() { - this.(Class).isPOD() + this.(Class).isPod() or isScalarType(this) or diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll new file mode 100644 index 0000000000..42d77b8055 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -0,0 +1,96 @@ +/** + * A module for representing different `Type`s. + */ + +import cpp + +/** + * A fundamental type, as defined by `[basic.fundamental]`. + */ +class FundamentalType extends BuiltInType { + FundamentalType() { + // A fundamental type is any `BuiltInType` except types indicating errors during extraction, or + // "unknown" types inserted into uninstantiated templates + not this instanceof ErroneousType and + not this instanceof UnknownType + } +} + +/** + * A type that is incomplete. + */ +class IncompleteType extends Class { + IncompleteType() { not hasDefinition() } +} + +/** + * A type that implements the BitmaskType trait. + * https://en.cppreference.com/w/cpp/named_req/BitmaskType + */ +abstract class BitmaskType extends Type { } + +/** + * Holds if `enum` implements required overload `overload` to implement + * the BitmaskType trait. + */ +private predicate isRequiredEnumOverload(Enum enum, Function overload) { + overload.getName().regexpMatch("operator([&|^~]|&=|\\|=)") and + forex(Parameter p | p = overload.getAParameter() | + ( + p.getType() = enum + or + p.getType().(ReferenceType).getBaseType() = enum + ) + ) +} + +private class EnumBitmaskType extends BitmaskType, Enum { + EnumBitmaskType() { + // Implements all the required overload + count(Function overload | isRequiredEnumOverload(this, overload)) = 6 + } +} + +/** + * A type without `const` and `volatile` specifiers. + */ +Type stripSpecifiers(Type type) { + if type instanceof SpecifiedType + then result = stripSpecifiers(type.(SpecifiedType).getBaseType()) + else result = type +} + +signature class PossiblySpecifiedBaseType extends Type; + +/** + * This module defines a class `Type` which holds for types `T` and `const/volatile T` etc. + * + * Similar to `getUnspecifiedType()`, but does not resolve typedefs. Useful for matching + * potentially qualified versions of standard typedef types, such as `const mtx_t`. + * + * Example usage: `someType.(PossiblySpecified::Type).strip()` + */ +module PossiblySpecified { + import cpp as cpp + + final class CppType = cpp::Type; + + class Type extends CppType { + BaseType baseType; + + Type() { baseType = stripSpecifiers(this) } + + BaseType strip() { result = baseType } + } +} + +/** + * Get the precision of an integral type, where precision is defined as the number of bits + * that can be used to represent the numeric value. + * https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions + */ +int getPrecision(IntegralType type) { + type.isExplicitlyUnsigned() and result = type.getSize() * 8 + or + type.isExplicitlySigned() and result = type.getSize() * 8 - 1 +} diff --git a/cpp/common/src/codingstandards/cpp/types/Uses.qll b/cpp/common/src/codingstandards/cpp/types/Uses.qll new file mode 100644 index 0000000000..7cbe8cc97c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Uses.qll @@ -0,0 +1,188 @@ +/** + * A module for identifying uses of a `Type` within the source code. + * + * The strategy used within this module is to identify types directly used within the AST classes + * represented in the databases - for example, `Type`s associated with variables, function return + * types, casts/sizeofs, name qualifiers and so forth. A recursive approach is then used to unwrap + * those types to determine which types are used transitively, such as base types, type arguments + * and so forth. + * + * The analysis also makes some basic attempts to exclude type self references, to avoid marking a + * type as "used" if it is only referenced from itself. + */ + +import cpp + +/** + * Gets a typedef with the same qualified name and declared at the same location. + * + * A single typedef may occur in our database multiple times, if it is compiled into multiple + * separate link targets with a different context. This predicate implements a hueristic for + * identifying "equivalent" typedefs, so that we can say if one is "live", the others are live. + */ +pragma[noinline, nomagic] +private TypedefType getAnEquivalentTypeDef(TypedefType type) { + // pragmas due to bad magic, bad inlining + type.getQualifiedName() = result.getQualifiedName() and + type.getLocation() = result.getLocation() +} + +/** + * Gets a meaningful use of `type` in the source code. + * + * This reports all the places in the source checkout root that the type was used, directly or + * indirectly. The makes a basic attempt to exclude self-referential types where the only reference + * is from within the function signature or field declaration of the type itself. + */ +Locatable getATypeUse(Type type) { + result = getATypeUse_i(type, _) + or + // Identify `TypeMention`s of typedef types, where the underlying type is used. + // + // In principle, we shouldn't need to use `TypeMention` at all, because `getATypeUse_i` captures + // all the AST structures which reference types. Unfortunately, there is one case where the AST + // type structures don't capture full fidelity type information: type arguments of template + // instantiations and specializations which are typedef'd types. Instead of the type argument + // being represented as the typedef type, it is represented as the _underlying_ type. There is, + // however, a type mention in the appropriate place for the typedef'd type. We therefore use + // the TypeMention information in this one case only, restricting it to avoid largely duplicating + // the work already determined as part of `getATypeUse_i`. + // + // For example, in: + // ``` + // 1 | typedef X Y; + // 2 | std::list x; + // ``` + // The type of `x` is `std::list` not `std::list`. There is, however a `TypeMention` of `Y` + // on line 2, we determine `Y` is used + exists(TypeMention tm, TypedefType typedefType | + result = tm and + type = getAnEquivalentTypeDef(typedefType) and + tm.getMentionedType() = typedefType + | + exists(tm.getFile().getRelativePath()) and + exists(getATypeUse_i(typedefType.getUnderlyingType(), _)) + ) +} + +private Locatable getATypeUse_i(Type type, string reason) { + ( + // Restrict to uses within the source checkout root + exists(result.getFile().getRelativePath()) + or + // Unless it's an alias template instantiation, as they do not have correct locations (last + // verified in CodeQL CLI 2.7.6) + result instanceof UsingAliasTypedefType and + not exists(result.getLocation()) + ) and + ( + // Used as a variable type + exists(Variable v | result = v | + type = v.getType() and + // Ignore self referential variables and parameters + not v.getDeclaringType().refersTo(type) and + not type = v.(Parameter).getFunction().getDeclaringType() + ) and + reason = "used as a variable type" + or + // Used a function return type + exists(Function f | + result = f and + not f.isCompilerGenerated() and + not type = f.getDeclaringType() + | + type = f.getType() and reason = "used as a function return type" + or + type = f.getATemplateArgument() and reason = "used as a function template argument" + ) + or + // Used either in a function call as a template argument, or as the declaring type + // of the function + exists(FunctionCall fc | result = fc | + type = fc.getTarget().getDeclaringType() and reason = "used in call to member function" + or + type = fc.getATemplateArgument() and reason = "used in function call template argument" + ) + or + // Aliased in a user typedef + exists(TypedefType t | result = t | type = t.getBaseType()) and + reason = "aliased in user typedef" + or + // A use in a `FunctionAccess` + exists(FunctionAccess fa | result = fa | type = fa.getTarget().getDeclaringType()) and + reason = "used in a function accesses" + or + // A use in a `sizeof` expr + exists(SizeofTypeOperator soto | result = soto | type = soto.getTypeOperand()) and + reason = "used in a sizeof expr" + or + // A use in a `Cast` + exists(Cast c | c = result | type = c.getType()) and + reason = "used in a cast" + or + // Use of the type name in source + exists(TypeName t | t = result | type = t.getType()) and + reason = "used in a typename" + or + // Access of an enum constant + exists(EnumConstantAccess eca | result = eca | type = eca.getTarget().getDeclaringEnum()) and + reason = "used in an enum constant access" + or + // Accessing a field on the type + exists(FieldAccess fa | + result = fa and + type = fa.getTarget().getDeclaringType() + ) and + reason = "used in a field access" + or + // Name qualifiers + exists(NameQualifier nq | + result = nq and + type = nq.getQualifyingElement() + ) and + reason = "used in name qualifier" + or + // Temporary object creation of type `type` + exists(TemporaryObjectExpr toe | result = toe | type = toe.getType()) and + reason = "used in temporary object expr" + ) + or + // Recursive case - used by a used type + exists(Type used | result = getATypeUse_i(used, _) | + // The `used` class has `type` as a base class + type = used.(DerivedType).getBaseType() and + reason = "used in derived type" + or + // The `used` class has `type` as a template argument + type = used.(Class).getATemplateArgument() and + reason = "used in class template argument" + or + // A used class is derived from the type class + type = used.(Class).getABaseClass() and + reason = "used in derived class" + or + // This is a TemplateClass where one of the instantiations is used + type.(TemplateClass).getAnInstantiation() = used and + reason = "used in template class instantiation" + or + // This is a TemplateClass where one of the specializations is used + type = used.(ClassTemplateSpecialization).getPrimaryTemplate() and + reason = "used in template class specialization" + or + // Alias templates - alias templates and instantiations are not properly captured by the + // extractor (last verified in CodeQL CLI 2.7.6). The only distinguishing factor is that + // instantiations of alias templates do not have a location. + exists(UsingAliasTypedefType template, UsingAliasTypedefType instantiation | + // Instantiation is a "use" of the template + used = instantiation and + type = template and + // The template has a location + exists(template.getLocation()) and + // The instantiation does not + not exists(instantiation.getLocation()) and + // Template and instantiation both have the same qualified name + template.getQualifiedName() = instantiation.getQualifiedName() + ) and + reason = "used in alias template instantiation" + ) +} diff --git a/cpp/common/src/codingstandards/cpp/types/VariablyModifiedTypes.qll b/cpp/common/src/codingstandards/cpp/types/VariablyModifiedTypes.qll new file mode 100644 index 0000000000..9de533d050 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/VariablyModifiedTypes.qll @@ -0,0 +1,224 @@ +import cpp + +/** + * A declaration involving a variably-modified type. + * + * Note, this holds for both VLA variable and VLA typedefs. + */ +class VmtDeclarationEntry extends DeclarationEntry { + Expr sizeExpr; + CandidateVlaType vlaType; + // `before` and `after` are captured for debugging, see doc comment for + // `declarationSubsumes`. + Location before; + Location after; + + VmtDeclarationEntry() { + // Most of this library looks for candidate VLA types, by looking for arrays + // without a size. These may or may not be VLA types. To confirm an a + // candidate type is really a VLA type, we check that the location of the + // declaration subsumes a `VlaDimensionStmt` which indicates a real VLA. + sizeExpr = any(VlaDimensionStmt vla).getDimensionExpr() and + declarationSubsumes(this, sizeExpr.getLocation(), before, after) and + ( + if this instanceof ParameterDeclarationEntry + then vlaType = this.getType().(VariablyModifiedTypeIfAdjusted).getInnerVlaType() + else vlaType = this.getType().(VariablyModifiedTypeIfUnadjusted).getInnerVlaType() + ) + } + + Expr getSizeExpr() { result = sizeExpr } + + CandidateVlaType getVlaType() { result = vlaType } + + /* VLAs may occur in macros, and result in duplication that messes up our analysis. */ + predicate appearsDuplicated() { + exists(VmtDeclarationEntry other | + other != this and + other.getSizeExpr() = getSizeExpr() + ) + } +} + +/** + * Check that the declaration entry, which may be a parameter or a variable + * etc., seems to subsume the location of `inner`, including the declaration + * type text. + * + * The location of the `DeclarationEntry` itself points to the _identifier_ + * that is declared. This range will not include the type of the declaration. + * + * For parameters, the `before` and `end` `Location` objects will be + * constrained to the closest earlier element (parameter or function body), + * these values can therefore be captured and inspected for debugging. + * + * For declarations which occur in statements, the `before` and `end` + * `Location` objects will be both constrained to be equal, and equal to, + * the `Location` of the containing `DeclStmt`. + */ +private predicate declarationSubsumes( + DeclarationEntry entry, Location inner, Location before, Location after +) { + inner.getFile() = entry.getLocation().getFile() and + ( + exists(ParameterDeclarationEntry param, FunctionDeclarationEntry func, int i | + param = entry and + func = param.getFunctionDeclarationEntry() and + func.getParameterDeclarationEntry(i) = param and + before = entry.getLocation() and + ( + after = func.getParameterDeclarationEntry(i + 1).getLocation() + or + not exists(ParameterDeclarationEntry afterParam | + afterParam = func.getParameterDeclarationEntry(i + 1) + ) and + after = func.getBlock().getLocation() + ) + ) and + before.isBefore(inner, _) and + inner.isBefore(after, _) + or + exists(DeclStmt s | + s.getADeclaration() = entry.getDeclaration() and + before = s.getLocation() and + after = before and + before.subsumes(inner) + ) + ) +} + +/** + * A candidate to be a variably length array type (VLA). + * + * This class represents a candidate only, for a few reasons. + * + * Firstly, the `ArrayType` class does not know when it has variable size, so + * this class matches all array types with unknown size, including `x[]` which + * is not a VLA. To determine the difference, we must compare locations between + * where * these types are declared, and the location of `VlaDecl`s etc. + * + * Secondly, function parameters of array type are adjusted into pointers. This + * means that while a parameter type can be a `CandidateVlaType`, that + * parameter is not a VLA. + */ +class CandidateVlaType extends ArrayType { + CandidateVlaType() { not hasArraySize() } + + Type getVariableBaseType() { result = this.getBaseType() } +} + +/** + * A type that is a variably modified type (VMT) if it does not undergo + * parameter type adjustment. + * + * A variably modified type is a VLA type, or a type containing a VMT type, for + * instance, a pointer to a VLA or a pointer to a pointer to a VLA. + * + * Function parameters and function type parameters of type `T[]` are adjusted + * to type `T*`, which can turn VMTs into non-VMTs. To check if a parameter + * type is a VMT, use `VariablyModifiedTypeIfAdjusted`. + */ +class VariablyModifiedTypeIfUnadjusted extends Type { + CandidateVlaType innerVlaType; + + VariablyModifiedTypeIfUnadjusted() { + // Take care that `int[x][y]` only matches for `innerVlaType = int[y]`. + if this instanceof CandidateVlaType + then innerVlaType = this + else innerVlaType = this.(NoAdjustmentVariablyModifiedType).getInnerVlaType() + } + + CandidateVlaType getInnerVlaType() { result = innerVlaType } +} + +/** + * A type that is a variably modified type (VMT) if it undergoes parameter type + * adjustment. + * + * A variably modified type is a VLA type, or a type containing a VMT type, for + * instance, a pointer to a VLA or a pointer to a pointer to a VLA. + * + * Function parameters and function type parameters of type `T[]` are adjusted + * to type `T*`, which can turn VMTs into non-VMTs. To check if a non-parameter + * type (for instance, the type of a local variable) is a VMT, use + * `VariablyModifiedTypeIfUnadjusted`. + */ +class VariablyModifiedTypeIfAdjusted extends Type { + CandidateVlaType innerVlaType; + + VariablyModifiedTypeIfAdjusted() { + innerVlaType = this.(ParameterAdjustedVariablyModifiedType).getInnerVlaType() + or + innerVlaType = this.(NoAdjustmentVariablyModifiedType).getInnerVlaType() + } + + CandidateVlaType getInnerVlaType() { result = innerVlaType } +} + +/** + * A variably modified type candidate which is unaffected by parameter type + * adjustment (from `T[]` to `*T`). + * + * Parameter adjustment (from `T[]` to `*T`) occurs on all function parameter + * types for exactly one level of depth. + * + * A variably-modified type (VMT) is a type which includes an inner type that is + * a VLA type. That is to say, a pointer to a VLA is a VMT, and a pointer to a + * VMT is a VMT. + * + * Note: This class does *not* match all VLA types. While VLA types *are* VMTs, + * VMTs can be parameter-adjusted to pointers, which are not VLA types. This + * class *will* match multidimensional VLAs, as those are adjusted to pointers + * to VLAs, and pointers to VLAs are VMTs. + */ +class NoAdjustmentVariablyModifiedType extends Type { + CandidateVlaType vlaType; + + NoAdjustmentVariablyModifiedType() { + exists(Type innerType | + ( + innerType = this.(DerivedType).getBaseType() + or + innerType = this.(RoutineType).getReturnType() + or + innerType = this.(FunctionPointerType).getReturnType() + or + innerType = this.(TypedefType).getBaseType() + ) and + vlaType = innerType.(VariablyModifiedTypeIfUnadjusted).getInnerVlaType() + ) + or + vlaType = + this.(FunctionPointerType) + .getAParameterType() + .(VariablyModifiedTypeIfAdjusted) + .getInnerVlaType() + or + vlaType = + this.(RoutineType).getAParameterType().(VariablyModifiedTypeIfAdjusted).getInnerVlaType() + } + + CandidateVlaType getInnerVlaType() { result = vlaType } +} + +/** + * An array type that adjusts to a variably-modified type (a type which is or + * contains a VLA type) when it is a parameter type. + * + * A variably-modified type (VMT) is a VLA type or a type which has an inner type + * that is a VMT type, for instance, a pointer to a VLA type. + * + * Parameter adjustment occurs on all function parameter types, changing type + * `T[]` to `*T` for exactly one level of depth. Therefore, a VLA type will not + * be a VLA type/VMT after parameter adjustment, unless it is an array of VMTs, + * such that it parameter adjustment produces a pointer to a VMT. + */ +class ParameterAdjustedVariablyModifiedType extends ArrayType { + CandidateVlaType innerVlaType; + + ParameterAdjustedVariablyModifiedType() { + innerVlaType = getBaseType().(VariablyModifiedTypeIfUnadjusted).getInnerVlaType() + } + + CandidateVlaType getInnerVlaType() { result = innerVlaType } +} diff --git a/cpp/common/src/ext/stdc++.model.yml b/cpp/common/src/ext/stdc++.model.yml new file mode 100644 index 0000000000..37919dceea --- /dev/null +++ b/cpp/common/src/ext/stdc++.model.yml @@ -0,0 +1,7 @@ +extensions: + - addsTo: + pack: codeql/common-cpp-coding-standards + extensible: throwingFunctionModel + data: + - ["std", "basic_string", "append", "std", "out_of_range"] + - ["std", "basic_string", "reserve", "std", "length_error"] \ No newline at end of file diff --git a/cpp/common/src/qlpack.yml b/cpp/common/src/qlpack.yml index fe070599d5..8b60314b91 100644 --- a/cpp/common/src/qlpack.yml +++ b/cpp/common/src/qlpack.yml @@ -1,3 +1,7 @@ -name: common-cpp-coding-standards -version: 2.9.0 -libraryPathDependencies: codeql-cpp +name: codeql/common-cpp-coding-standards +version: 2.49.0-dev +license: MIT +dependencies: + codeql/cpp-all: 4.0.3 +dataExtensions: +- ext/*.model.yml diff --git a/cpp/common/test/codeql-pack.lock.yml b/cpp/common/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/common/test/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/cpp/common/test/deviations/deviation_permits_basic_test/UnusedReturnValue.ql b/cpp/common/test/deviations/deviation_permits_basic_test/UnusedReturnValue.ql index 38b75bda3c..469a7f7f73 100644 --- a/cpp/common/test/deviations/deviation_permits_basic_test/UnusedReturnValue.ql +++ b/cpp/common/test/deviations/deviation_permits_basic_test/UnusedReturnValue.ql @@ -16,7 +16,6 @@ import cpp import codingstandards.cpp.CodingStandards -import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.exclusions.cpp.RuleMetadata /* This is a copy of an AUTOSAR rule, which we are using for testing purposes. */ diff --git a/cpp/common/test/deviations/deviations_basic_test/ListDeviationRecords.expected b/cpp/common/test/deviations/deviations_basic_test/ListDeviationRecords.expected index 3a095d8fb9..5115cc8a70 100644 --- a/cpp/common/test/deviations/deviations_basic_test/ListDeviationRecords.expected +++ b/cpp/common/test/deviations/deviations_basic_test/ListDeviationRecords.expected @@ -1,3 +1,3 @@ -| A0-1-1 | cpp/autosar/useless-assignment | Applies to the following file paths: deviations/deviations_basic_test | | This useless assignment is required. | | | -| A0-1-2 | cpp/autosar/unused-return-value | Applies to the following file paths: deviations/deviations_basic_test/nested/nested1,deviations/deviations_basic_test/nested/nested2 | | Unused return value. | | | +| A0-1-1 | cpp/autosar/useless-assignment | Applies to the following file paths: (root) | | This useless assignment is required. | | | +| A0-1-2 | cpp/autosar/unused-return-value | Applies to the following file paths: nested/nested1,nested/nested2 | | Unused return value. | | | | A0-4-2 | cpp/autosar/type-long-double-used | Identified by the use of the code-identifier: a-0-4-2-deviation | | long double is required for interaction with third-party libraries. | | | diff --git a/cpp/common/test/deviations/deviations_basic_test/TypeLongDoubleUsed.expected b/cpp/common/test/deviations/deviations_basic_test/TypeLongDoubleUsed.expected index 7b78d54892..afc613642a 100644 --- a/cpp/common/test/deviations/deviations_basic_test/TypeLongDoubleUsed.expected +++ b/cpp/common/test/deviations/deviations_basic_test/TypeLongDoubleUsed.expected @@ -1 +1,14 @@ +| attribute_syntax.cpp:6:15:6:17 | dd1 | Use of long double type. | +| attribute_syntax.cpp:21:15:21:17 | d10 | Use of long double type. | +| attribute_syntax.cpp:29:15:29:17 | d14 | Use of long double type. | +| attribute_syntax.cpp:34:20:34:22 | d16 | Use of long double type. | +| attribute_syntax.cpp:55:15:55:16 | d1 | Use of long double type. | +| attribute_syntax.cpp:57:17:57:18 | d2 | Use of long double type. | +| attribute_syntax.cpp:60:17:60:18 | d3 | Use of long double type. | | main.cpp:13:15:13:16 | d1 | Use of long double type. | +| main.cpp:18:15:18:16 | d4 | Use of long double type. | +| main.cpp:21:15:21:16 | d6 | Use of long double type. | +| main.cpp:30:15:30:17 | d10 | Use of long double type. | +| main.cpp:38:15:38:17 | d14 | Use of long double type. | +| main.cpp:42:15:42:17 | d15 | Use of long double type. | +| main.cpp:43:113:43:115 | d18 | Use of long double type. | diff --git a/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.expected b/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.expected index 7cc5d2e1ab..fc7af4b197 100644 --- a/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.expected +++ b/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.expected @@ -1,3 +1,17 @@ +| attribute_syntax.cpp:5:3:5:6 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:16:5:16:8 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:18:5:18:8 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:24:5:24:8 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:26:5:26:8 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:30:3:30:6 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:42:3:42:6 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:49:5:49:8 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | +| attribute_syntax.cpp:61:5:61:8 | call to getZ | Return value from call to $@ is unused. | attribute_syntax.cpp:1:5:1:8 | getZ | getZ | | main.cpp:12:3:12:6 | call to getX | Return value from call to $@ is unused. | main.cpp:8:5:8:8 | getX | getX | +| main.cpp:25:3:25:6 | call to getX | Return value from call to $@ is unused. | main.cpp:8:5:8:8 | getX | getX | +| main.cpp:27:3:27:6 | call to getX | Return value from call to $@ is unused. | main.cpp:8:5:8:8 | getX | getX | +| main.cpp:33:3:33:6 | call to getX | Return value from call to $@ is unused. | main.cpp:8:5:8:8 | getX | getX | +| main.cpp:35:3:35:6 | call to getX | Return value from call to $@ is unused. | main.cpp:8:5:8:8 | getX | getX | +| main.cpp:39:3:39:6 | call to getX | Return value from call to $@ is unused. | main.cpp:8:5:8:8 | getX | getX | | nested/nested3/test3.h:5:3:5:7 | call to getZ3 | Return value from call to $@ is unused. | nested/nested3/test3.h:1:5:1:9 | getZ3 | getZ3 | | nested/test.h:5:3:5:6 | call to getY | Return value from call to $@ is unused. | nested/test.h:1:5:1:8 | getY | getY | diff --git a/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.ql b/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.ql index 38b75bda3c..469a7f7f73 100644 --- a/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.ql +++ b/cpp/common/test/deviations/deviations_basic_test/UnusedReturnValue.ql @@ -16,7 +16,6 @@ import cpp import codingstandards.cpp.CodingStandards -import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.exclusions.cpp.RuleMetadata /* This is a copy of an AUTOSAR rule, which we are using for testing purposes. */ diff --git a/cpp/common/test/deviations/deviations_basic_test/attribute_syntax.cpp b/cpp/common/test/deviations/deviations_basic_test/attribute_syntax.cpp new file mode 100644 index 0000000000..e363de55af --- /dev/null +++ b/cpp/common/test/deviations/deviations_basic_test/attribute_syntax.cpp @@ -0,0 +1,76 @@ +int getZ() { return 5; } + +int alt() { + int x = 0; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT + long double dd1; // NON_COMPLIANT (A0-4-2) + + long double [[codeql::autosar_deviation( + "a-0-4-2-deviation")]] dd3; // COMPLIANT[DEVIATED] + + [[codeql::autosar_deviation( + "a-0-4-2-deviation")]] long double dd4; // COMPLIANT[DEVIATED] + + [[codeql::autosar_deviation("a-0-4-2-deviation")]] { + long double d7; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT (A0-1-2) + long double d8; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT (A0-1-2) + long double d9; // COMPLIANT[DEVIATED] + } + long double d10; // NON_COMPLIANT (A0-4-2) + [[codeql::autosar_deviation("a-0-4-2-deviation")]] { + long double d11; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT (A0-1-2) + long double d12; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT (A0-1-2) + long double d13; // COMPLIANT[DEVIATED] + } + long double d14; // NON_COMPLIANT (A0-4-2) + getZ(); // NON_COMPLIANT (A0-1-2) + [[codeql::autosar_deviation("a-0-4-2-deviation")]] for (long double d15 = 0.0; + true;) { + } // COMPLIANT[DEVIATED] + for (long double d16 = 0.0; true;) { // NON_COMPLIANT (A0-4-2) + } + return 0; +} + +[[codeql::autosar_deviation("a-0-4-2-deviation")]] int +test_function_deviation() { + int x = 0; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT + long double dd1; // COMPLIANT[DEVIATED] +} + +[[codeql::autosar_deviation("a-0-4-2-deviation")]] void test_lambdas() { + auto l = []() { + long double d4; // COMPLIANT[DEVIATED] + getZ(); // NON_COMPLIANT + }; +} + +// Attributes are not supported on a class level at the moment +[[codeql::autosar_deviation("a-0-4-2-deviation")]] class ClassA { + long double d1; // COMPLIANT[DEVIATED - false positive] + class ClassNested { + long double d2; // COMPLIANT[DEVIATED - false positive] + }; + void test() { + long double d3; // COMPLIANT[DEVIATED - false positive] + getZ(); // NON_COMPLIANT + } +}; + +// static_assert, templates, noexcept, multiple declarations + +// Namespaces not currently supported by attributes +// [[codeql::autosar_deviation("a-0-4-2-deviation")]] namespace NS { +// long double d1; // COMPLIANT[DEVIATED] +// class ClassA { +// long double d1; // COMPLIANT[DEVIATED] +// }; +// void test() { +// long double d1; // COMPLIANT[DEVIATED] +// } +// } \ No newline at end of file diff --git a/cpp/common/test/deviations/deviations_basic_test/main.cpp b/cpp/common/test/deviations/deviations_basic_test/main.cpp index 0b302ea1f2..17126364f4 100644 --- a/cpp/common/test/deviations/deviations_basic_test/main.cpp +++ b/cpp/common/test/deviations/deviations_basic_test/main.cpp @@ -12,5 +12,35 @@ int main(int argc, char **argv) { getX(); // NON_COMPLIANT long double d1; // NON_COMPLIANT (A0-4-2) long double d2; // a-0-4-2-deviation COMPLIANT[DEVIATED] + + long double d3; // codeql::autosar_deviation(a-0-4-2-deviation) + // COMPLIANT[DEVIATED] + long double d4; // NON_COMPLIANT (A0-4-2) + // codeql::autosar_deviation_next_line(a-0-4-2-deviation) + long double d5; // COMPLIANT[DEVIATED] + long double d6; // NON_COMPLIANT (A0-4-2) + + // codeql::autosar_deviation_begin(a-0-4-2-deviation) + long double d7; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d8; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d9; // COMPLIANT[DEVIATED] + // codeql::autosar_deviation_end(a-0-4-2-deviation) + long double d10; // NON_COMPLIANT (A0-4-2) + // codeql::autosar_deviation_begin(a-0-4-2-deviation) + long double d11; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d12; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d13; // COMPLIANT[DEVIATED] + // codeql::autosar_deviation_end(a-0-4-2-deviation) + long double d14; // NON_COMPLIANT (A0-4-2) + getX(); // NON_COMPLIANT (A0-1-2) + + // clang-format off + long double d15; /* NON_COMPLIANT*/ /* codeql::autosar_deviation_begin(a-0-4-2-deviation) */ long double d16; // COMPLIANT[DEVIATED] + long double d17; /* COMPLIANT[DEVIATED] */ /* codeql::autosar_deviation_end(a-0-4-2-deviation) */ long double d18; // NON_COMPLIANT + // clang-format on return 0; } \ No newline at end of file diff --git a/cpp/common/test/deviations/deviations_report_deviated/DeviationsSuppression.expected b/cpp/common/test/deviations/deviations_report_deviated/DeviationsSuppression.expected index 50ceb35b9d..35fca84928 100644 --- a/cpp/common/test/deviations/deviations_report_deviated/DeviationsSuppression.expected +++ b/cpp/common/test/deviations/deviations_report_deviated/DeviationsSuppression.expected @@ -1,7 +1,12 @@ -| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:12:1:12:58 | Deviation of cpp/autosar/type-long-double-used for comment // a-0-4-2-deviation COMPLIANT[DEVIATED]. | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:12:1:12:58 | Deviation of cpp/autosar/type-long-double-used applied to main.cpp Line 12 | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:14:1:14:65 | Deviation of cpp/autosar/type-long-double-used applied to main.cpp Line 14 | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:18:1:18:40 | Deviation of cpp/autosar/type-long-double-used applied to main.cpp Line 18 | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:21:3:27:53 | Deviation of cpp/autosar/type-long-double-used applied to main.cpp Line 21:3:27:53 | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:29:3:35:53 | Deviation of cpp/autosar/type-long-double-used applied to main.cpp Line 29:3:35:53 | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/type-long-double-used] | lgtm[cpp/autosar/type-long-double-used] | main.cpp:40:39:41:99 | Deviation of cpp/autosar/type-long-double-used applied to main.cpp Line 40:39:41:99 | | file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/unused-return-value] | lgtm[cpp/autosar/unused-return-value] | nested/nested2/test2.h:1:1:6:1 | Deviation of cpp/autosar/unused-return-value for nested/nested2/test2.h. | | file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/useless-assignment] | lgtm[cpp/autosar/useless-assignment] | coding-standards.xml:1:1:17:19 | Deviation of cpp/autosar/useless-assignment for coding-standards.xml. | -| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/useless-assignment] | lgtm[cpp/autosar/useless-assignment] | main.cpp:1:1:14:1 | Deviation of cpp/autosar/useless-assignment for main.cpp. | +| file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/useless-assignment] | lgtm[cpp/autosar/useless-assignment] | main.cpp:1:1:44:1 | Deviation of cpp/autosar/useless-assignment for main.cpp. | | file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/useless-assignment] | lgtm[cpp/autosar/useless-assignment] | nested/coding-standards.xml:1:1:13:19 | Deviation of cpp/autosar/useless-assignment for nested/coding-standards.xml. | | file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/useless-assignment] | lgtm[cpp/autosar/useless-assignment] | nested/nested2/test2.h:1:1:6:1 | Deviation of cpp/autosar/useless-assignment for nested/nested2/test2.h. | | file://:0:0:0:0 | (no string representation) | // lgtm[cpp/autosar/useless-assignment] | lgtm[cpp/autosar/useless-assignment] | nested/test.h:1:1:6:1 | Deviation of cpp/autosar/useless-assignment for nested/test.h. | diff --git a/cpp/common/test/deviations/deviations_report_deviated/TypeLongDoubleUsed.expected b/cpp/common/test/deviations/deviations_report_deviated/TypeLongDoubleUsed.expected new file mode 100644 index 0000000000..e9099fa64a --- /dev/null +++ b/cpp/common/test/deviations/deviations_report_deviated/TypeLongDoubleUsed.expected @@ -0,0 +1,18 @@ +| main.cpp:11:15:11:16 | d1 | Use of long double type. | +| main.cpp:12:15:12:16 | d2 | Use of long double type. | +| main.cpp:14:15:14:16 | d3 | Use of long double type. | +| main.cpp:16:15:16:16 | d4 | Use of long double type. | +| main.cpp:18:15:18:16 | d5 | Use of long double type. | +| main.cpp:19:15:19:16 | d6 | Use of long double type. | +| main.cpp:22:15:22:16 | d7 | Use of long double type. | +| main.cpp:24:15:24:16 | d8 | Use of long double type. | +| main.cpp:26:15:26:16 | d9 | Use of long double type. | +| main.cpp:28:15:28:17 | d10 | Use of long double type. | +| main.cpp:30:15:30:17 | d11 | Use of long double type. | +| main.cpp:32:15:32:17 | d12 | Use of long double type. | +| main.cpp:34:15:34:17 | d13 | Use of long double type. | +| main.cpp:36:15:36:17 | d14 | Use of long double type. | +| main.cpp:40:15:40:17 | d15 | Use of long double type. | +| main.cpp:40:108:40:110 | d16 | Use of long double type. | +| main.cpp:41:15:41:17 | d17 | Use of long double type. | +| main.cpp:41:113:41:115 | d18 | Use of long double type. | diff --git a/cpp/common/test/deviations/deviations_report_deviated/TypeLongDoubleUsed.ql b/cpp/common/test/deviations/deviations_report_deviated/TypeLongDoubleUsed.ql new file mode 100644 index 0000000000..0ff7b93251 --- /dev/null +++ b/cpp/common/test/deviations/deviations_report_deviated/TypeLongDoubleUsed.ql @@ -0,0 +1,38 @@ +/** + * @id cpp/autosar/type-long-double-used + * @name A0-4-2: Type long double shall not be used + * @description The type long double has an implementation-defined width and therefore shall not be + * used. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/autosar/id/a0-4-2 + * correctness + * readability + * external/autosar/allocated-target/implementation + * external/autosar/enforcement/automated + * external/autosar/obligation/required + */ + +import cpp +import codingstandards.cpp.CodingStandards +import codingstandards.cpp.exclusions.cpp.RuleMetadata + +predicate isUsingLongDouble(ClassTemplateInstantiation c) { + c.getATemplateArgument() instanceof LongDoubleType or + isUsingLongDouble(c.getATemplateArgument()) +} + +from Variable v +where + not isExcluded(v, BannedTypesPackage::typeLongDoubleUsedQuery()) and + ( + v.getUnderlyingType() instanceof LongDoubleType and + not v.isFromTemplateInstantiation(_) + or + exists(ClassTemplateInstantiation c | + c = v.getType() and + isUsingLongDouble(c) + ) + ) +select v, "Use of long double type." diff --git a/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.expected b/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.expected index 7b8860d5a3..ab75d81f6f 100644 --- a/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.expected +++ b/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.expected @@ -1,3 +1,8 @@ | main.cpp:10:3:10:6 | call to getX | Return value from call to $@ is unused. | main.cpp:6:5:6:8 | getX | getX | +| main.cpp:23:3:23:6 | call to getX | Return value from call to $@ is unused. | main.cpp:6:5:6:8 | getX | getX | +| main.cpp:25:3:25:6 | call to getX | Return value from call to $@ is unused. | main.cpp:6:5:6:8 | getX | getX | +| main.cpp:31:3:31:6 | call to getX | Return value from call to $@ is unused. | main.cpp:6:5:6:8 | getX | getX | +| main.cpp:33:3:33:6 | call to getX | Return value from call to $@ is unused. | main.cpp:6:5:6:8 | getX | getX | +| main.cpp:37:3:37:6 | call to getX | Return value from call to $@ is unused. | main.cpp:6:5:6:8 | getX | getX | | nested/nested2/test2.h:5:3:5:6 | call to getZ | Return value from call to $@ is unused. | nested/nested2/test2.h:1:5:1:8 | getZ | getZ | | nested/test.h:5:3:5:6 | call to getY | Return value from call to $@ is unused. | nested/test.h:1:5:1:8 | getY | getY | diff --git a/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.ql b/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.ql index 38b75bda3c..469a7f7f73 100644 --- a/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.ql +++ b/cpp/common/test/deviations/deviations_report_deviated/UnusedReturnValue.ql @@ -16,7 +16,6 @@ import cpp import codingstandards.cpp.CodingStandards -import semmle.code.cpp.dataflow.DataFlow import codingstandards.cpp.exclusions.cpp.RuleMetadata /* This is a copy of an AUTOSAR rule, which we are using for testing purposes. */ diff --git a/cpp/common/test/deviations/deviations_report_deviated/main.cpp b/cpp/common/test/deviations/deviations_report_deviated/main.cpp index c59dea5609..d2f58d9db1 100644 --- a/cpp/common/test/deviations/deviations_report_deviated/main.cpp +++ b/cpp/common/test/deviations/deviations_report_deviated/main.cpp @@ -10,5 +10,35 @@ int main(int argc, char **argv) { getX(); // NON_COMPLIANT long double d1; // NON_COMPLIANT (A0-4-2) long double d2; // a-0-4-2-deviation COMPLIANT[DEVIATED] + + long double d3; // codeql::autosar_deviation(a-0-4-2-deviation) + // COMPLIANT[DEVIATED] + long double d4; // NON_COMPLIANT (A0-4-2) + // codeql::autosar_deviation_next_line(a-0-4-2-deviation) + long double d5; // COMPLIANT[DEVIATED] + long double d6; // NON_COMPLIANT (A0-4-2) + + // codeql::autosar_deviation_begin(a-0-4-2-deviation) + long double d7; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d8; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d9; // COMPLIANT[DEVIATED] + // codeql::autosar_deviation_end(a-0-4-2-deviation) + long double d10; // NON_COMPLIANT (A0-4-2) + // codeql::autosar_deviation_begin(a-0-4-2-deviation) + long double d11; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d12; // COMPLIANT[DEVIATED] + getX(); // NON_COMPLIANT (A0-1-2) + long double d13; // COMPLIANT[DEVIATED] + // codeql::autosar_deviation_end(a-0-4-2-deviation) + long double d14; // NON_COMPLIANT (A0-4-2) + getX(); // NON_COMPLIANT (A0-1-2) + + // clang-format off + long double d15; /* NON_COMPLIANT*/ /* codeql::autosar_deviation_begin(a-0-4-2-deviation) */ long double d16; // COMPLIANT[DEVIATED] + long double d17; /* COMPLIANT[DEVIATED] */ /* codeql::autosar_deviation_end(a-0-4-2-deviation) */ long double d18; // NON_COMPLIANT + // clang-format on return 0; } \ No newline at end of file diff --git a/cpp/common/test/deviations/invalid_deviations/InvalidDeviationCodeIdentifier.expected b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationCodeIdentifier.expected new file mode 100644 index 0000000000..1d7153bafd --- /dev/null +++ b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationCodeIdentifier.expected @@ -0,0 +1,13 @@ +| invalidcodeidentifiers.cpp:1:1:1:45 | // codeql::misra_deviation(x) - invalid, no x | Deviation marker does not match an expected format, or references an unknown code identifier. | +| invalidcodeidentifiers.cpp:2:1:2:47 | // codeql::autosar_deviation(x) - invalid, no x | Deviation marker does not match an expected format, or references an unknown code identifier. | +| invalidcodeidentifiers.cpp:3:1:3:44 | // codeql::cert_deviation(x) - invalid, no x | Deviation marker does not match an expected format, or references an unknown code identifier. | +| invalidcodeidentifiers.cpp:4:1:4:71 | // codeql::misra_deviation_next(a-0-4-2-deviation) - invalid, next_line | Deviation marker does not match an expected format, or references an unknown code identifier. | +| invalidcodeidentifiers.cpp:5:1:5:73 | // codeql::autosar_deviation_next(a-0-4-2-deviation) - invalid, next_line | Deviation marker does not match an expected format, or references an unknown code identifier. | +| invalidcodeidentifiers.cpp:6:1:6:70 | // codeql::cert_deviation_next(a-0-4-2-deviation) - invalid, next_line | Deviation marker does not match an expected format, or references an unknown code identifier. | +| invalidcodeidentifiers.cpp:14:1:14:74 | // codeql::misra_deviation_end(a-0-4-2-deviation) - invalid, unmatched end | Deviation end block is unmatched. | +| invalidcodeidentifiers.cpp:15:1:15:76 | // codeql::autosar_deviation_end(a-0-4-2-deviation) - invalid, unmatched end | Deviation end block is unmatched. | +| invalidcodeidentifiers.cpp:16:1:16:73 | // codeql::cert_deviation_end(a-0-4-2-deviation) - invalid, unmatched end | Deviation end block is unmatched. | +| invalidcodeidentifiers.cpp:17:1:17:78 | // codeql::misra_deviation_begin(a-0-4-2-deviation) - invalid, unmatched begin | Deviation start block is unmatched. | +| invalidcodeidentifiers.cpp:18:1:18:80 | // codeql::autosar_deviation_begin(a-0-4-2-deviation) - invalid, unmatched begin | Deviation start block is unmatched. | +| invalidcodeidentifiers.cpp:19:1:19:77 | // codeql::cert_deviation_begin(a-0-4-2-deviation) - invalid, unmatched begin | Deviation start block is unmatched. | +| invalidcodeidentifiers.cpp:21:3:21:25 | misra_deviation | Deviation attribute references unknown code identifier x. | diff --git a/cpp/common/test/deviations/invalid_deviations/InvalidDeviationCodeIdentifier.qlref b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationCodeIdentifier.qlref new file mode 100644 index 0000000000..c70989966f --- /dev/null +++ b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationCodeIdentifier.qlref @@ -0,0 +1 @@ +codingstandards/cpp/deviations/InvalidDeviationCodeIdentifier.ql \ No newline at end of file diff --git a/cpp/common/test/deviations/invalid_deviations/InvalidDeviationPermits.expected b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationPermits.expected index f5630aa947..4378fdf11d 100644 --- a/cpp/common/test/deviations/invalid_deviations/InvalidDeviationPermits.expected +++ b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationPermits.expected @@ -1,2 +1,2 @@ -| coding-standards.xml:96:7:99:33 | deviation-permits-entry | deviations/invalid_deviations/coding-standards.xml: Deviation permit does not specify a permit identifier. | -| coding-standards.xml:100:7:103:33 | deviation-permits-entry | deviations/invalid_deviations/coding-standards.xml: Deviation permit specifies unknown property `invalid-property`. | +| coding-standards.xml:105:7:108:33 | deviation-permits-entry | coding-standards.xml: Deviation permit does not specify a permit identifier. | +| coding-standards.xml:109:7:112:33 | deviation-permits-entry | coding-standards.xml: Deviation permit specifies unknown property `invalid-property`. | diff --git a/cpp/common/test/deviations/invalid_deviations/InvalidDeviationRecords.expected b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationRecords.expected index f68aa49e43..2cd438c5c6 100644 --- a/cpp/common/test/deviations/invalid_deviations/InvalidDeviationRecords.expected +++ b/cpp/common/test/deviations/invalid_deviations/InvalidDeviationRecords.expected @@ -1,13 +1,14 @@ -| coding-standards.xml:5:7:5:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: No rule-id and query-id specified for this deviation record. | -| coding-standards.xml:6:7:8:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: The rule-id `bad rule id` for this deviation matches none of the available queries. | -| coding-standards.xml:9:7:11:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A query-id of `bad rule id` is specified for this deviation, but not rule-id is specified. | -| coding-standards.xml:15:7:17:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A query-id of `cpp/autosar/useless-assignment` is specified for this deviation, but not rule-id is specified. | -| coding-standards.xml:22:7:26:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `raised-by` is specified without providing an `approved-by`. | -| coding-standards.xml:22:7:26:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `raised-by` is specified without providing both a `name` and `date`. | -| coding-standards.xml:27:7:33:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `raised-by` is specified without providing an `approved-by`. | -| coding-standards.xml:27:7:33:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `raised-by` is specified without providing both a `name` and `date`. | -| coding-standards.xml:34:7:41:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `raised-by` is specified without providing an `approved-by`. | -| coding-standards.xml:42:7:50:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `approved-by` is specified without providing both a `name` and `date`. | -| coding-standards.xml:51:7:61:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: A deviation `approved-by` is specified without providing both a `name` and `date`. | -| coding-standards.xml:74:7:78:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: There is no deviation permit with id `non-existing-permit`. | -| coding-standards.xml:79:7:81:26 | deviations-entry | deviations/invalid_deviations/coding-standards.xml: No rule-id and query-id specified for this deviation record. | +| coding-standards.xml:5:7:5:27 | deviations-entry | coding-standards.xml: No rule-id and query-id specified for this deviation record. | +| coding-standards.xml:6:7:8:26 | deviations-entry | coding-standards.xml: The rule-id `bad rule id` for this deviation matches none of the available queries. | +| coding-standards.xml:9:7:11:26 | deviations-entry | coding-standards.xml: A query-id of `bad rule id` is specified for this deviation, but not rule-id is specified. | +| coding-standards.xml:15:7:17:26 | deviations-entry | coding-standards.xml: A query-id of `cpp/autosar/useless-assignment` is specified for this deviation, but not rule-id is specified. | +| coding-standards.xml:22:7:26:26 | deviations-entry | coding-standards.xml: A deviation `raised-by` is specified without providing an `approved-by`. | +| coding-standards.xml:22:7:26:26 | deviations-entry | coding-standards.xml: A deviation `raised-by` is specified without providing both a `name` and `date`. | +| coding-standards.xml:27:7:33:26 | deviations-entry | coding-standards.xml: A deviation `raised-by` is specified without providing an `approved-by`. | +| coding-standards.xml:27:7:33:26 | deviations-entry | coding-standards.xml: A deviation `raised-by` is specified without providing both a `name` and `date`. | +| coding-standards.xml:34:7:41:26 | deviations-entry | coding-standards.xml: A deviation `raised-by` is specified without providing an `approved-by`. | +| coding-standards.xml:42:7:50:26 | deviations-entry | coding-standards.xml: A deviation `approved-by` is specified without providing both a `name` and `date`. | +| coding-standards.xml:51:7:61:26 | deviations-entry | coding-standards.xml: A deviation `approved-by` is specified without providing both a `name` and `date`. | +| coding-standards.xml:74:7:78:26 | deviations-entry | coding-standards.xml: There is no deviation permit with id `non-existing-permit`. | +| coding-standards.xml:79:7:81:26 | deviations-entry | coding-standards.xml: No rule-id and query-id specified for this deviation record. | +| coding-standards.xml:85:7:88:26 | deviations-entry | coding-standards.xml: The deviation is applied to a query with the rule category 'mandatory' that does not permit a deviation. | diff --git a/cpp/common/test/deviations/invalid_deviations/coding-standards.xml b/cpp/common/test/deviations/invalid_deviations/coding-standards.xml index 9667878e0f..ff0d69eaec 100644 --- a/cpp/common/test/deviations/invalid_deviations/coding-standards.xml +++ b/cpp/common/test/deviations/invalid_deviations/coding-standards.xml @@ -1,8 +1,8 @@ - + - + bad rule id @@ -22,7 +22,7 @@ A0-1-1 cpp/autosar/useless-assignment - + A0-1-1 @@ -46,7 +46,7 @@ foo3 1970-01-01Z - + A0-1-1 @@ -82,6 +82,15 @@ DP2 + + RULE-9-1 + c/misra/object-with-auto-storage-duration-read-before-init + + + A0-4-2 + long double is required for interaction with third-party libraries. + a-0-4-2-deviation + @@ -102,4 +111,4 @@ invalid-property - + \ No newline at end of file diff --git a/cpp/common/test/deviations/invalid_deviations/coding-standards.yml b/cpp/common/test/deviations/invalid_deviations/coding-standards.yml index 1f0b1b3b09..ceea31c460 100644 --- a/cpp/common/test/deviations/invalid_deviations/coding-standards.yml +++ b/cpp/common/test/deviations/invalid_deviations/coding-standards.yml @@ -44,6 +44,11 @@ deviations: permit-id: non-existing-permit - permit-id: DP1 - permit-id: DP2 + - rule-id: RULE-9-1 + query-id: c/misra/object-with-auto-storage-duration-read-before-init + - rule-id: A0-4-2 + justification: long double is required for interaction with third-party libraries. + code-identifier: a-0-4-2-deviation deviation-permits: - permit-id: DP1 justification: foo bar baz diff --git a/cpp/common/test/deviations/invalid_deviations/dummy.cpp b/cpp/common/test/deviations/invalid_deviations/dummy.cpp deleted file mode 100644 index 4a3cb36e40..0000000000 --- a/cpp/common/test/deviations/invalid_deviations/dummy.cpp +++ /dev/null @@ -1 +0,0 @@ -// Deliberately blank \ No newline at end of file diff --git a/cpp/common/test/deviations/invalid_deviations/invalidcodeidentifiers.cpp b/cpp/common/test/deviations/invalid_deviations/invalidcodeidentifiers.cpp new file mode 100644 index 0000000000..a4da098dcb --- /dev/null +++ b/cpp/common/test/deviations/invalid_deviations/invalidcodeidentifiers.cpp @@ -0,0 +1,24 @@ +// codeql::misra_deviation(x) - invalid, no x +// codeql::autosar_deviation(x) - invalid, no x +// codeql::cert_deviation(x) - invalid, no x +// codeql::misra_deviation_next(a-0-4-2-deviation) - invalid, next_line +// codeql::autosar_deviation_next(a-0-4-2-deviation) - invalid, next_line +// codeql::cert_deviation_next(a-0-4-2-deviation) - invalid, next_line + +// codeql::misra_deviation_begin(a-0-4-2-deviation) +// codeql::autosar_deviation_begin(a-0-4-2-deviation) +// codeql::cert_deviation_begin(a-0-4-2-deviation) +// codeql::misra_deviation_end(a-0-4-2-deviation) +// codeql::autosar_deviation_end(a-0-4-2-deviation) +// codeql::cert_deviation_end(a-0-4-2-deviation) +// codeql::misra_deviation_end(a-0-4-2-deviation) - invalid, unmatched end +// codeql::autosar_deviation_end(a-0-4-2-deviation) - invalid, unmatched end +// codeql::cert_deviation_end(a-0-4-2-deviation) - invalid, unmatched end +// codeql::misra_deviation_begin(a-0-4-2-deviation) - invalid, unmatched begin +// codeql::autosar_deviation_begin(a-0-4-2-deviation) - invalid, unmatched begin +// codeql::cert_deviation_begin(a-0-4-2-deviation) - invalid, unmatched begin + +[[codeql::misra_deviation("x")]] // invalid +void test() {} + +[[codeql::autosar_deviation("a-0-4-2-deviation")]] void test2() {} diff --git a/cpp/common/test/guideline_recategorizations/DisappliedQuery.expected b/cpp/common/test/guideline_recategorizations/DisappliedQuery.expected new file mode 100644 index 0000000000..215a334c23 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/DisappliedQuery.expected @@ -0,0 +1 @@ +| a0-1-6.cpp:1:7:1:7 | A | Unused type declaration A is not reported with reason 'The query is disapplied.'. | diff --git a/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql b/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql new file mode 100644 index 0000000000..aa2caa8ad2 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/guideline-recategorizations/disapplied-query + * @name Query based on A0-1-6 to test disapplied category + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/autosar/id/a0-1-6 + * external/autosar/obligation/advisory + */ + +import cpp +import codingstandards.cpp.CodingStandards +import codingstandards.cpp.types.Uses +import codingstandards.cpp.exclusions.cpp.RuleMetadata + +from UserType ut, string reason +where + isExcluded(ut, DeadCodePackage::unusedTypeDeclarationsQuery(), reason) and + exists(ut.getFile()) and + not ut instanceof TypeTemplateParameter and + not ut instanceof ProxyClass and + not exists(getATypeUse(ut)) and + not ut.isFromUninstantiatedTemplate(_) +select ut, + "Unused type declaration " + ut.getName() + " is not reported with reason '" + reason + "'." diff --git a/cpp/common/test/guideline_recategorizations/InvalidGuidelineRecategorizations.expected b/cpp/common/test/guideline_recategorizations/InvalidGuidelineRecategorizations.expected new file mode 100644 index 0000000000..6d9c892eba --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/InvalidGuidelineRecategorizations.expected @@ -0,0 +1,5 @@ +| invalid/coding-standards.xml:5:7:8:43 | guideline-recategorizations-entry | invalid/coding-standards.xml: 'Invalid recategorization from 'required' to 'advisory'.' for rule A0-1-1. | +| invalid/coding-standards.xml:9:7:12:43 | guideline-recategorizations-entry | invalid/coding-standards.xml: 'Invalid recategorization from 'required' to 'disapplied'.' for rule A0-1-2. | +| invalid/coding-standards.xml:13:7:16:43 | guideline-recategorizations-entry | invalid/coding-standards.xml: 'Unknown rule id 'A1-4-3'.' for rule A1-4-3. | +| invalid/coding-standards.xml:17:7:20:43 | guideline-recategorizations-entry | invalid/coding-standards.xml: 'Invalid recategorization from 'mandatory' to 'required'.' for rule RULE-9-1. | +| invalid/coding-standards.xml:21:7:24:43 | guideline-recategorizations-entry | invalid/coding-standards.xml: 'Invalid recategorization from 'rule' to 'required'.' for rule CON50-CPP. | \ No newline at end of file diff --git a/cpp/common/test/guideline_recategorizations/InvalidGuidelineRecategorizations.qlref b/cpp/common/test/guideline_recategorizations/InvalidGuidelineRecategorizations.qlref new file mode 100644 index 0000000000..516ca4187e --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/InvalidGuidelineRecategorizations.qlref @@ -0,0 +1 @@ +codingstandards/cpp/guideline_recategorizations/InvalidGuidelineRecategorizations.ql \ No newline at end of file diff --git a/cpp/common/test/guideline_recategorizations/ListGuidelineRecategorizations.expected b/cpp/common/test/guideline_recategorizations/ListGuidelineRecategorizations.expected new file mode 100644 index 0000000000..8e6a397620 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/ListGuidelineRecategorizations.expected @@ -0,0 +1,8 @@ +| A0-1-1 | required | advisory | +| A0-1-1 | required | mandatory | +| A0-1-2 | required | disapplied | +| A0-1-6 | advisory | disapplied | +| A10-4-1 | advisory | required | +| A11-0-1 | advisory | mandatory | +| CON50-CPP | rule | required | +| RULE-9-1 | mandatory | required | diff --git a/cpp/common/test/guideline_recategorizations/ListGuidelineRecategorizations.qlref b/cpp/common/test/guideline_recategorizations/ListGuidelineRecategorizations.qlref new file mode 100644 index 0000000000..a6720a0ddb --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/ListGuidelineRecategorizations.qlref @@ -0,0 +1 @@ +codingstandards/cpp/guideline_recategorizations/ListGuidelineRecategorizations.ql \ No newline at end of file diff --git a/cpp/common/test/guideline_recategorizations/a0-1-6.cpp b/cpp/common/test/guideline_recategorizations/a0-1-6.cpp new file mode 100644 index 0000000000..f0dae258ba --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/a0-1-6.cpp @@ -0,0 +1 @@ +class A {}; // Unused type declaration \ No newline at end of file diff --git a/cpp/common/test/guideline_recategorizations/dummy.xml b/cpp/common/test/guideline_recategorizations/dummy.xml new file mode 100644 index 0000000000..c390fb1e16 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/dummy.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/cpp/common/test/guideline_recategorizations/invalid/coding-standards.xml b/cpp/common/test/guideline_recategorizations/invalid/coding-standards.xml new file mode 100644 index 0000000000..dfb7b6f13c --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/invalid/coding-standards.xml @@ -0,0 +1,26 @@ + + + + + + A0-1-1 + advisory + + + A0-1-2 + disapplied + + + A1-4-3 + mandatory + + + RULE-9-1 + required + + + CON50-CPP + required + + + diff --git a/cpp/common/test/guideline_recategorizations/invalid/coding-standards.yml b/cpp/common/test/guideline_recategorizations/invalid/coding-standards.yml new file mode 100644 index 0000000000..cd6abbf120 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/invalid/coding-standards.yml @@ -0,0 +1,11 @@ +guideline-recategorizations: + - rule-id: "A0-1-1" + category: "advisory" + - rule-id: "A0-1-2" + category: "disapplied" + - rule-id: "A1-4-3" + category: "mandatory" + - rule-id: "RULE-9-1" + category: "required" + - rule-id: "CON50-CPP" + category: "required" diff --git a/cpp/common/test/guideline_recategorizations/test.cpp b/cpp/common/test/guideline_recategorizations/test.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/common/test/guideline_recategorizations/valid/coding-standards.xml b/cpp/common/test/guideline_recategorizations/valid/coding-standards.xml new file mode 100644 index 0000000000..afc7919a89 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/valid/coding-standards.xml @@ -0,0 +1,22 @@ + + + + + + A0-1-1 + mandatory + + + A0-1-6 + disapplied + + + A10-4-1 + required + + + A11-0-1 + mandatory + + + diff --git a/cpp/common/test/guideline_recategorizations/valid/coding-standards.yml b/cpp/common/test/guideline_recategorizations/valid/coding-standards.yml new file mode 100644 index 0000000000..ed778a5cc1 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/valid/coding-standards.yml @@ -0,0 +1,9 @@ +guideline-recategorizations: + - rule-id: "A0-1-1" + category: "mandatory" + - rule-id: "A0-1-6" + category: "disapplied" + - rule-id: "A10-4-1" + category: "required" + - rule-id: "A11-0-1" + category: "mandatory" diff --git a/cpp/common/test/includes/custom-library/gtest/gtest-internal.h b/cpp/common/test/includes/custom-library/gtest/gtest-internal.h new file mode 100644 index 0000000000..31d47b714f --- /dev/null +++ b/cpp/common/test/includes/custom-library/gtest/gtest-internal.h @@ -0,0 +1,29 @@ +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +#define GTEST_TEST_(test_suite_name, test_name, parent_class) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &) = delete; /* NOLINT */ \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &&) noexcept = delete; /* NOLINT */ \ + \ + private: \ + void TestBody() override; \ + }; \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() \ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/cpp/common/test/includes/custom-library/gtest/gtest.h b/cpp/common/test/includes/custom-library/gtest/gtest.h new file mode 100644 index 0000000000..65fce9fc5a --- /dev/null +++ b/cpp/common/test/includes/custom-library/gtest/gtest.h @@ -0,0 +1,28 @@ +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include "gtest/gtest-internal.h" + +namespace testing { + +class Test +{ + public: + virtual ~Test(); + protected: + // Creates a Test object. + Test(); + private: + virtual void TestBody() = 0; + Test(const Test&) = delete; + Test& operator=(const Test&) = delete; +}; + +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test) + +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/cpp/common/test/includes/custom-library/macro_c_style_casts.h b/cpp/common/test/includes/custom-library/macro_c_style_casts.h new file mode 100644 index 0000000000..31af2a4ead --- /dev/null +++ b/cpp/common/test/includes/custom-library/macro_c_style_casts.h @@ -0,0 +1,4 @@ +// Macros used in test for A5-2-2 +#define LIBRARY_ADD_TWO(x) ((int)x) + 2 +#define LIBRARY_NESTED_ADD_TWO(x) LIBRARY_ADD_TWO(x) +#define LIBRARY_NO_CAST_ADD_TWO(x) x + 1 \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/array b/cpp/common/test/includes/standard-library/array index b77ca4ca99..9465ccba97 100644 --- a/cpp/common/test/includes/standard-library/array +++ b/cpp/common/test/includes/standard-library/array @@ -1,15 +1,15 @@ #ifndef _GHLIBCPP_ARRAY #define _GHLIBCPP_ARRAY -#include "iterator.h" -#include "string.h" +#include +#include // Note: a few features currently unused by tests are commented out namespace std { template struct array { typedef T &reference; typedef const T &const_reference; - typedef std::iterator iterator; - typedef std::iterator const_iterator; + typedef __iterator iterator; + typedef __iterator const_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; diff --git a/cpp/common/test/includes/standard-library/assert.h b/cpp/common/test/includes/standard-library/assert.h index e69de29bb2..e8ba88d635 100644 --- a/cpp/common/test/includes/standard-library/assert.h +++ b/cpp/common/test/includes/standard-library/assert.h @@ -0,0 +1,6 @@ +#ifndef _GHLIBCPP_ASSERT +#define _GHLIBCPP_ASSERT + +#define assert(x) (void)0 + +#endif // _GHLIBCPP_ASSERT \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cassert b/cpp/common/test/includes/standard-library/cassert index e69de29bb2..0477057664 100644 --- a/cpp/common/test/includes/standard-library/cassert +++ b/cpp/common/test/includes/standard-library/cassert @@ -0,0 +1 @@ +#include "assert.h" \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cerrno b/cpp/common/test/includes/standard-library/cerrno index f1957abe6a..679ce128ea 100644 --- a/cpp/common/test/includes/standard-library/cerrno +++ b/cpp/common/test/includes/standard-library/cerrno @@ -1,2 +1 @@ -int __errno; -#define errno __errno \ No newline at end of file +#include \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/clocale b/cpp/common/test/includes/standard-library/clocale index da05144f67..430c36daa0 100644 --- a/cpp/common/test/includes/standard-library/clocale +++ b/cpp/common/test/includes/standard-library/clocale @@ -8,13 +8,10 @@ #define LC_NUMERIC 0 #define LC_TIME 0 +#include + namespace std { -struct lconv; -char *setlocale(int, const char *); -lconv *localeconv(); +using ::lconv; +using ::localeconv; +using ::setlocale; } // namespace std - -// global namespace -struct lconv; -char *setlocale(int, const char *); -lconv *localeconv(); \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cmath b/cpp/common/test/includes/standard-library/cmath index e69de29bb2..9f3fffda8d 100644 --- a/cpp/common/test/includes/standard-library/cmath +++ b/cpp/common/test/includes/standard-library/cmath @@ -0,0 +1,19 @@ +namespace std { +#include + +int isinf(float x); +int isinf(double x); +int isinf(long double x); +bool isfinite(float x); +bool isfinite(double x); +bool isfinite(long double x); +bool isnan(float x); +bool isnan(double x); +bool isnan(long double x); +bool isnormal(float x); +bool isnormal(double x); +bool isnormal(long double x); +int fpclassify(float x); +int fpclassify(double x); +int fpclassify(long double x); +} \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/csignal b/cpp/common/test/includes/standard-library/csignal index 0cded25e15..4c3b565256 100644 --- a/cpp/common/test/includes/standard-library/csignal +++ b/cpp/common/test/includes/standard-library/csignal @@ -10,13 +10,10 @@ #define SIG_IGN 7 #define SIG_ERR 8 -namespace std { -typedef int sig_atomic_t; -void (*signal(int, void (*func)(int)))(int); -int raise(int sig); -} // namespace std +#include -// global namespace -typedef int sig_atomic_t; -void (*signal(int, void (*func)(int)))(int); -int raise(int); \ No newline at end of file +namespace std { +using ::raise; +using ::sig_atomic_t; +using ::signal; +} // namespace std \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cstdio b/cpp/common/test/includes/standard-library/cstdio index 65dde7279d..e40fc5458c 100644 --- a/cpp/common/test/includes/standard-library/cstdio +++ b/cpp/common/test/includes/standard-library/cstdio @@ -16,135 +16,55 @@ #define SEEK_END -1 #define stderr 0 -namespace std { -struct FILE; -typedef int fpos_t; - -// Operations on files -int remove(const char *filename); -int rename(const char *oldname, const char *newname); -FILE *tmpfile(void); -char *tmpnam(char *str); - -// File access -int fclose(FILE *stream); -int fflush(FILE *stream); -FILE *fopen(const char *filename, const char *mode); -FILE *freopen(const char *filename, const char *mode, FILE *stream); -void setbuf(FILE *stream, char *buffer); -int setvbuf(FILE *stream, char *buffer, int mode, size_t size); - -// Formatted input/output -int fprintf(FILE *stream, const char *format, ...); -int fscanf(FILE *stream, const char *format, ...); -int printf(const char *format, ...); -int scanf(const char *format, ...); -int snprintf(char *s, size_t n, const char *format, ...); -int sprintf(char *str, const char *format, ...); -int sscanf(const char *s, const char *format, ...); -/* -int vfprintf ( FILE * stream, const char * format, va_list arg ); -int vfscanf ( FILE * stream, const char * format, va_list arg ); -int vprintf ( const char * format, va_list arg ); -int vscanf ( const char * format, va_list arg ); -int vsnprintf (char * s, size_t n, const char * format, va_list arg ); -int vsprintf (char * s, const char * format, va_list arg ); -int vsscanf ( const char * s, const char * format, va_list arg ); -*/ - -// Character input/output -int fgetc(FILE *stream); -char *fgets(char *str, int num, FILE *stream); -int fputc(int character, FILE *stream); -int fputs(const char *str, FILE *stream); -int getc(FILE *stream); -int getchar(void); -char *gets(char *str); -int putc(int character, FILE *stream); -int putchar(int character); -int puts(const char *str); -int ungetc(int character, FILE *stream); - -// Direct input/output -size_t fread(void *ptr, size_t size, size_t count, FILE *stream); -size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream); - -// File positioning -int fgetpos(FILE *stream, fpos_t *pos); -int fseek(FILE *stream, long int offset, int origin); -int fsetpos(FILE *stream, const fpos_t *pos); -long int ftell(FILE *stream); -void rewind(FILE *stream); - -// Error-handling -void clearerr(FILE *stream); -int feof(FILE *stream); -int ferror(FILE *stream); -void perror(const char *str); -} // namespace std +#include -// global namespace -struct FILE; -typedef int fpos_t; - -// Operations on files -int remove(const char *filename); -int rename(const char *oldname, const char *newname); -FILE *tmpfile(void); -char *tmpnam(char *str); - -// File access -int fclose(FILE *stream); -int fflush(FILE *stream); -FILE *fopen(const char *filename, const char *mode); -FILE *freopen(const char *filename, const char *mode, FILE *stream); -void setbuf(FILE *stream, char *buffer); -int setvbuf(FILE *stream, char *buffer, int mode, size_t size); - -// Formatted input/output -int fprintf(FILE *stream, const char *format, ...); -int fscanf(FILE *stream, const char *format, ...); -int printf(const char *format, ...); -int scanf(const char *format, ...); -int snprintf(char *s, size_t n, const char *format, ...); -int sprintf(char *str, const char *format, ...); -int sscanf(const char *s, const char *format, ...); -/* -int vfprintf ( FILE * stream, const char * format, va_list arg ); -int vfscanf ( FILE * stream, const char * format, va_list arg ); -int vprintf ( const char * format, va_list arg ); -int vscanf ( const char * format, va_list arg ); -int vsnprintf (char * s, size_t n, const char * format, va_list arg ); -int vsprintf (char * s, const char * format, va_list arg ); -int vsscanf ( const char * s, const char * format, va_list arg ); -*/ - -// Character input/output -int fgetc(FILE *stream); -char *fgets(char *str, int num, FILE *stream); -int fputc(int character, FILE *stream); -int fputs(const char *str, FILE *stream); -int getc(FILE *stream); -int getchar(void); -char *gets(char *str); -int putc(int character, FILE *stream); -int putchar(int character); -int puts(const char *str); -int ungetc(int character, FILE *stream); - -// Direct input/output -size_t fread(void *ptr, size_t size, size_t count, FILE *stream); -size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream); - -// File positioning -int fgetpos(FILE *stream, fpos_t *pos); -int fseek(FILE *stream, long int offset, int origin); -int fsetpos(FILE *stream, const fpos_t *pos); -long int ftell(FILE *stream); -void rewind(FILE *stream); - -// Error-handling -void clearerr(FILE *stream); -int feof(FILE *stream); -int ferror(FILE *stream); -void perror(const char *str); \ No newline at end of file +namespace std { +using ::FILE; +using ::fpos_t; + +using ::remove; +using ::rename; +using ::tmpfile; +using ::tmpnam; + +using ::fclose; +using ::fflush; +using ::fopen; +using ::freopen; +using ::setbuf; +using ::setvbuf; + +using ::fprintf; +using ::fscanf; +using ::printf; +using ::scanf; +using ::snprintf; +using ::sprintf; +using ::sscanf; + +using ::fgetc; +using ::fgets; +using ::fputc; +using ::fputs; +using ::getc; +using ::getchar; +using ::gets; +using ::putc; +using ::putchar; +using ::puts; +using ::ungetc; + +using ::fread; +using ::fwrite; + +using ::fgetpos; +using ::fseek; +using ::fsetpos; +using ::ftell; +using ::rewind; + +using ::clearerr; +using ::feof; +using ::ferror; +using ::perror; +} // namespace std \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cstdlib.h b/cpp/common/test/includes/standard-library/cstdlib.h index b2c87ada66..4a2d0cd9ee 100644 --- a/cpp/common/test/includes/standard-library/cstdlib.h +++ b/cpp/common/test/includes/standard-library/cstdlib.h @@ -7,10 +7,10 @@ namespace std { [[noreturn]] void quick_exit(int status) noexcept; extern "C++" int atexit(void (*f)(void)) noexcept; extern "C++" int at_quick_exit(void (*f)(void)) noexcept; -extern "C++" long rand() noexcept; using ::atof; using ::atoi; using ::atol; using ::atoll; +using ::rand; } // namespace std #endif // _GHLIBCPP_CSTDLIB \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cstring.h b/cpp/common/test/includes/standard-library/cstring.h index 8c34428fa8..2f3ffd393e 100644 --- a/cpp/common/test/includes/standard-library/cstring.h +++ b/cpp/common/test/includes/standard-library/cstring.h @@ -1,8 +1,29 @@ #ifndef _GHLIBCPP_CSTRING #define _GHLIBCPP_CSTRING -typedef unsigned long size_t; + +#include + namespace std { -void *memcpy(void *, const void *, size_t); -size_t strlen(const char *); +using ::memcmp; +using ::memcpy; +using ::memmove; +using ::memset; +using ::size_t; +using ::strcat; +using ::strchr; +using ::strcmp; +using ::strcoll; +using ::strcpy; +using ::strcspn; +using ::strlen; +using ::strncat; +using ::strncmp; +using ::strncpy; +using ::strpbrk; +using ::strrchr; +using ::strspn; +using ::strstr; +using ::strtok; +using ::strxfrm; } // namespace std #endif // _GHLIBCPP_CSTRING diff --git a/cpp/common/test/includes/standard-library/ctime b/cpp/common/test/includes/standard-library/ctime index f99aab4fb3..9448e0615e 100644 --- a/cpp/common/test/includes/standard-library/ctime +++ b/cpp/common/test/includes/standard-library/ctime @@ -1,39 +1,17 @@ #ifndef _GHLIBCPP_CTIME #define _GHLIBCPP_CTIME - -namespace std -{ - typedef unsigned long clock_t; - typedef unsigned long time_t; - - typedef unsigned long size_t; - struct tm - { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; - }; - - - clock_t clock (void); - double difftime (clock_t end, clock_t beginning); - time_t mktime (struct tm * timeptr); - time_t time (time_t* timer); - char* asctime (const struct tm * timeptr); - - char* ctime (const time_t * timer); - struct tm * gmtime (const time_t * timer); - struct tm * localtime (const time_t * timer); - size_t strftime (char* ptr, size_t maxsize, const char* format, - const struct tm* timeptr ); - -} - - -#endif \ No newline at end of file +#include +namespace std { +using ::clock_t; +using ::clock; +using ::time_t; +using ::time; +using ::tm; +using ::difftime; +using ::asctime; +using ::ctime; +using ::localtime; +using ::gmtime; +using ::mktime; +} // namespace std +#endif // _GHLIBCPP_CTIME \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/deque.h b/cpp/common/test/includes/standard-library/deque.h index aee0573229..00b44b704a 100644 --- a/cpp/common/test/includes/standard-library/deque.h +++ b/cpp/common/test/includes/standard-library/deque.h @@ -1,7 +1,7 @@ #ifndef _GHLIBCPP_DEQUE #define _GHLIBCPP_DEQUE -#include "iterator.h" -#include "string.h" +#include +#include namespace std { template > class deque { diff --git a/cpp/common/test/includes/standard-library/errno.h b/cpp/common/test/includes/standard-library/errno.h index 96195e6e19..be17eeb0b5 100644 --- a/cpp/common/test/includes/standard-library/errno.h +++ b/cpp/common/test/includes/standard-library/errno.h @@ -1,3 +1,5 @@ -namespace std { -typedef int errno_t; -} // namespace std \ No newline at end of file +#ifndef _GHLIBCPP_ERRNO +#define _GHLIBCPP_ERRNO +int __errno; +#define errno __errno +#endif // _GHLIBCPP_ERRNO \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/istream.h b/cpp/common/test/includes/standard-library/istream.h index f1758e6930..f56e67b8c5 100644 --- a/cpp/common/test/includes/standard-library/istream.h +++ b/cpp/common/test/includes/standard-library/istream.h @@ -1,8 +1,8 @@ #ifndef _GHLIBCPP_ISTREAM #define _GHLIBCPP_ISTREAM -#include "ios.h" -#include "string.h" +#include +#include namespace std { template diff --git a/cpp/common/test/includes/standard-library/limits.h b/cpp/common/test/includes/standard-library/limits.h index 5317339525..f7d490c181 100644 --- a/cpp/common/test/includes/standard-library/limits.h +++ b/cpp/common/test/includes/standard-library/limits.h @@ -23,6 +23,8 @@ #define LLONG_MAX 9223372036854775807 #define ULLONG_MIN 0ULL #define ULLONG_MAX 0xffffffffffffffff +#define NAN (0.0f / 0.0f) +#define INFINITY 1e5000f namespace std { template class numeric_limits; diff --git a/cpp/common/test/includes/standard-library/locale.h b/cpp/common/test/includes/standard-library/locale.h index e69de29bb2..19a8905531 100644 --- a/cpp/common/test/includes/standard-library/locale.h +++ b/cpp/common/test/includes/standard-library/locale.h @@ -0,0 +1,38 @@ +#ifndef _GHLIBCPP_LOCALE +#define _GHLIBCPP_LOCALE + +#define LC_ALL 6 + +struct lconv { + char *decimal_point; + char *thousands_sep; + char *grouping; + + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; + char int_n_cs_precedes; + char int_n_sep_by_space; + char int_p_sign_posn; + char int_n_sign_posn; +}; + + +char *setlocale (int, const char *); +struct lconv *localeconv(void); + +#endif // _GHLIBCPP_LOCALE \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/math.h b/cpp/common/test/includes/standard-library/math.h index 563f6e7823..7ff0ef8357 100644 --- a/cpp/common/test/includes/standard-library/math.h +++ b/cpp/common/test/includes/standard-library/math.h @@ -1,5 +1,7 @@ #ifndef _GHLIBCPP_MATH #define _GHLIBCPP_MATH +int abs(int x); +long abs(long x); double acos(double x); float acosf(float x); long double acosl(long double x); @@ -15,9 +17,21 @@ long double acoshl(long double x); double atanh(double x); float atanhf(float x); long double atanhl(long double x); +float cos(float x); +double cos(double x); +long double cos(long double x); +double fmod(double x, double y); +float fmodf(float x, float y); +long double fmodl(long double x, long double y); +double frexp(double x, int *y); +float frexpf(float x, int *y); +long double frexpl(long double, int *); int ilogb(double x); int ilogbf(float x); int ilogbl(long double x); +double lgamma(double x); +float lgammaf(float x); +long double lgammal(long double x); double log(double x); float logf(float x); long double logl(long double x); @@ -36,6 +50,9 @@ long double logbl(long double x); double pow(double x, double y); float powf(float x, float y); long double powl(long double x, long double y); +float sin(float x); +double sin(double x); +long double sin(long double x); double sqrt(double x); float sqrtf(float x); long double sqrtl(long double x); diff --git a/cpp/common/test/includes/standard-library/memory.h b/cpp/common/test/includes/standard-library/memory.h index cf8be3fade..985ee41602 100644 --- a/cpp/common/test/includes/standard-library/memory.h +++ b/cpp/common/test/includes/standard-library/memory.h @@ -1,7 +1,7 @@ #ifndef _GHLIBCPP_MEMORY #define _GHLIBCPP_MEMORY -#include "stddef.h" #include "exception.h" +#include "stddef.h" namespace std { @@ -18,6 +18,7 @@ template struct default_delete { template > class unique_ptr { public: + typedef T *pointer; unique_ptr() {} unique_ptr(T *ptr) {} unique_ptr(const unique_ptr &t) = delete; @@ -27,9 +28,7 @@ class unique_ptr { T *operator->() const noexcept { return ptr; } T *get() const noexcept { return ptr; } T *release() { return ptr; } - void reset() {} - void reset(T *ptr) {} - void reset(T ptr) {} + void reset(pointer __p = pointer()) {} T *get() { return ptr; } unique_ptr &operator=(const unique_ptr &) = delete; unique_ptr &operator=(unique_ptr &&) { return *this; } @@ -58,35 +57,49 @@ template class unique_ptr { pointer get() const noexcept; explicit operator bool() const noexcept; - pointer release() noexcept; + pointer release() noexcept { + pointer __p = get(); + _M_p = pointer(); + return __p; + } void reset(pointer p = pointer()) noexcept; void reset(nullptr_t) noexcept; template void reset(U) = delete; void swap(unique_ptr &u) noexcept; unique_ptr(const unique_ptr &) = delete; unique_ptr &operator=(const unique_ptr &) = delete; + +private: + pointer _M_p; }; template unique_ptr make_unique(Args &&...args); template unique_ptr make_unique(size_t n); -template class shared_ptr { +template class __shared_ptr { +public: + void reset() noexcept; + template void reset(Y *p); + template void reset(Y *p, D d); + template void reset(Y *p, D d, A a); + + long use_count() const noexcept; + T *get() const noexcept; +}; + +template class shared_ptr : public __shared_ptr { public: - shared_ptr() {} - shared_ptr(T *ptr) {} + shared_ptr(); + shared_ptr(T *ptr); shared_ptr(const shared_ptr &r) noexcept; template shared_ptr(const shared_ptr &r) noexcept; shared_ptr(shared_ptr &&r) noexcept; template shared_ptr(shared_ptr &&r) noexcept; shared_ptr(unique_ptr &&t) {} ~shared_ptr() {} - T &operator*() const { return *ptr; } - T *operator->() const noexcept { return ptr; } - void reset() {} - void reset(T *pt) {} - void reset(T pt) {} - long use_count() const noexcept { return 0; } - T *get() { return ptr; } + T &operator*() const noexcept; + T *operator->() const noexcept; + shared_ptr &operator=(const shared_ptr &) {} shared_ptr &operator=(shared_ptr &&) { return *this; } template shared_ptr &operator=(shared_ptr &&) { diff --git a/cpp/common/test/includes/standard-library/mutex.h b/cpp/common/test/includes/standard-library/mutex.h index 4f16824861..4c49819ddd 100644 --- a/cpp/common/test/includes/standard-library/mutex.h +++ b/cpp/common/test/includes/standard-library/mutex.h @@ -23,7 +23,7 @@ constexpr try_to_lock_t try_to_lock{}; class mutex { public: constexpr mutex() noexcept; - ~mutex(); + ~mutex() = default; mutex(const mutex &) = delete; mutex &operator=(const mutex &) = delete; void lock(); @@ -62,15 +62,19 @@ template void swap(unique_lock &x, unique_lock &y) noexcept; template -void lock(_Lock0 &_Lk0, _Lock1 &_Lk1, _LockN &..._LkN) { } +void lock(_Lock0 &_Lk0, _Lock1 &_Lk1, _LockN &..._LkN) {} template class lock_guard { public: typedef Mutex mutex_type; - explicit lock_guard(mutex_type &__m); + explicit lock_guard(mutex_type &__m) : _m(__m) { _m.lock(); } lock_guard(const lock_guard &) = delete; lock_guard &operator=(const lock_guard &) = delete; + ~lock_guard() { _m.unlock(); } + +private: + mutex_type &_m; }; } // namespace std diff --git a/cpp/common/test/includes/standard-library/ostream.h b/cpp/common/test/includes/standard-library/ostream.h index bde2b7a53f..9f2c6d9069 100644 --- a/cpp/common/test/includes/standard-library/ostream.h +++ b/cpp/common/test/includes/standard-library/ostream.h @@ -1,7 +1,7 @@ #ifndef _GHLIBCPP_OSTREAM #define _GHLIBCPP_OSTREAM -#include "string.h" #include +#include namespace std { template diff --git a/cpp/common/test/includes/standard-library/random.h b/cpp/common/test/includes/standard-library/random.h index e3f3dcab0a..1a2b341226 100644 --- a/cpp/common/test/includes/standard-library/random.h +++ b/cpp/common/test/includes/standard-library/random.h @@ -2,7 +2,7 @@ #define _GHLIBCPP_RANDOM #include "cstdint.h" #include "stddef.h" -#include "string.h" +#include namespace std { template diff --git a/cpp/common/test/includes/standard-library/signal.h b/cpp/common/test/includes/standard-library/signal.h index e69de29bb2..b9ac81919a 100644 --- a/cpp/common/test/includes/standard-library/signal.h +++ b/cpp/common/test/includes/standard-library/signal.h @@ -0,0 +1,8 @@ +#ifndef _GHLIBCPP_SIGNAL +#define _GHLIBCPP_SIGNAL + +typedef int sig_atomic_t; +void (*signal(int, void (*func)(int)))(int); +int raise(int); + +#endif // _GHLIBCPP_SIGNAL \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/stddef.h b/cpp/common/test/includes/standard-library/stddef.h index 496de53167..96e9849973 100644 --- a/cpp/common/test/includes/standard-library/stddef.h +++ b/cpp/common/test/includes/standard-library/stddef.h @@ -17,5 +17,11 @@ using size_t = decltype(sizeof(char)); #define offsetof(t, d) __builtin_offsetof(t, d) /*implementation-defined*/ +#ifdef __cplusplus +#define NULL 0L +#else +#define NULL ((void*)0) +#endif + // namespace std #endif // _GHLIBCPP_STDDEF \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/stdexcept.h b/cpp/common/test/includes/standard-library/stdexcept.h index fd9f7f9e6d..d341738bfc 100644 --- a/cpp/common/test/includes/standard-library/stdexcept.h +++ b/cpp/common/test/includes/standard-library/stdexcept.h @@ -1,7 +1,7 @@ #ifndef _GHLIBCPP_STDEXCEPT #define _GHLIBCPP_STDEXCEPT -#include "exception.h" -#include "string.h" +#include +#include namespace std { class logic_error : public exception { @@ -28,5 +28,7 @@ class nested_exception { template [[noreturn]] void throw_with_nested(T &&t); template void rethrow_if_nested(E const &e); +class length_error : public logic_error{}; +class out_of_range: public logic_error{}; } // namespace std #endif // _GHLIBCPP_STDEXCEPT \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/stdio.h b/cpp/common/test/includes/standard-library/stdio.h index b1a36a4af0..30ca17bcaf 100644 --- a/cpp/common/test/includes/standard-library/stdio.h +++ b/cpp/common/test/includes/standard-library/stdio.h @@ -1,5 +1,84 @@ +#ifndef _GHLIBCPP_STDIO +#define _GHLIBCPP_STDIO + +#include + typedef void FILE; +typedef int fpos_t; + +// Operations on files +int remove(const char *filename); +int rename(const char *oldname, const char *newname); +FILE *tmpfile(void); +char *tmpnam(char *str); + +// File access +int fclose(FILE *stream); +int fflush(FILE *stream); +FILE *fopen(const char *filename, const char *mode); +FILE *freopen(const char *filename, const char *mode, FILE *stream); +void setbuf(FILE *stream, char *buffer); +int setvbuf(FILE *stream, char *buffer, int mode, size_t size); + +// Formatted input/output +int fprintf(FILE *stream, const char *format, ...); +int fscanf(FILE *stream, const char *format, ...); +int printf(const char *format, ...); +int scanf(const char *format, ...); +int snprintf(char *s, size_t n, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int sscanf(const char *s, const char *format, ...); +/* +int vfprintf ( FILE * stream, const char * format, va_list arg ); +int vfscanf ( FILE * stream, const char * format, va_list arg ); +int vprintf ( const char * format, va_list arg ); +int vscanf ( const char * format, va_list arg ); +int vsnprintf (char * s, size_t n, const char * format, va_list arg ); +int vsprintf (char * s, const char * format, va_list arg ); +int vsscanf ( const char * s, const char * format, va_list arg ); +*/ + +// Character input/output +int fgetc(FILE *stream); +char *fgets(char *str, int num, FILE *stream); +int fputc(int character, FILE *stream); +int fputs(const char *str, FILE *stream); +int getc(FILE *stream); +int getchar(void); +char *gets(char *str); +int putc(int character, FILE *stream); +int putchar(int character); +int puts(const char *str); +int ungetc(int character, FILE *stream); + +// Character input/output +int fgetc(FILE *stream); +char *fgets(char *str, int num, FILE *stream); +int fputc(int character, FILE *stream); +int fputs(const char *str, FILE *stream); +int getc(FILE *stream); +int getchar(void); +char *gets(char *str); +int putc(int character, FILE *stream); +int putchar(int character); +int puts(const char *str); +int ungetc(int character, FILE *stream); + +// Direct input/output +size_t fread(void *ptr, size_t size, size_t count, FILE *stream); +size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream); + +// File positioning +int fgetpos(FILE *stream, fpos_t *pos); +int fseek(FILE *stream, long int offset, int origin); +int fsetpos(FILE *stream, const fpos_t *pos); +long int ftell(FILE *stream); +void rewind(FILE *stream); + +// Error-handling +void clearerr(FILE *stream); +int feof(FILE *stream); +int ferror(FILE *stream); +void perror(const char *str); -FILE *fopen(const char *, const char *); -int fclose(FILE *); -int remove(const char *); \ No newline at end of file +#endif // _GHLIBCPP_STDIO \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/stdlib.h b/cpp/common/test/includes/standard-library/stdlib.h index 37902b52bb..67f1abd694 100644 --- a/cpp/common/test/includes/standard-library/stdlib.h +++ b/cpp/common/test/includes/standard-library/stdlib.h @@ -15,9 +15,13 @@ int system(const char *command); char *getenv(const char *name); +int setenv (const char *, const char *, int); + int atoi(const char *str); long int atol(const char *str); long long int atoll(const char *str); double atof(const char *str); +int rand(void); + #endif // _GHLIBCPP_STDLIB \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/string b/cpp/common/test/includes/standard-library/string index de6c8e145a..a3f22f5e80 100644 --- a/cpp/common/test/includes/standard-library/string +++ b/cpp/common/test/includes/standard-library/string @@ -1 +1,253 @@ -#include \ No newline at end of file +#ifndef _GHLIBCPP_STRING +#define _GHLIBCPP_STRING +#include "initializer_list" +#include "iosfwd.h" +#include "iterator.h" +#include "stddef.h" + +namespace std { +template struct char_traits; + +template class allocator { +public: + allocator() throw(); + typedef size_t size_type; +}; + +template , + class Allocator = allocator> +class basic_string { +public: + using value_type = charT; + using reference = value_type &; + using const_reference = const value_type &; + typedef typename Allocator::size_type size_type; + static const size_type npos = -1; + + basic_string() : basic_string(Allocator()) {} + explicit basic_string(const Allocator &a); + basic_string(const basic_string &str); + basic_string(basic_string &&str) noexcept; + basic_string(const charT *s, size_type n, const Allocator &a = Allocator()); + basic_string(const charT *s, const Allocator &a = Allocator()); + basic_string(size_type n, charT c, const Allocator &a = Allocator()); + template + basic_string(InputIterator begin, InputIterator end, + const Allocator &a = Allocator()); + + ~basic_string(); + basic_string &operator=(const basic_string &str); + basic_string &operator=(basic_string &&str) noexcept; + basic_string &operator=(const charT *s); + basic_string &operator=(charT c); + basic_string &operator=(initializer_list); + + const charT *c_str() const; + charT *data() noexcept; + size_type size() const noexcept; + size_type length() const noexcept; + + typedef __iterator iterator; + typedef __iterator __const_iterator; + typedef __const_iterator const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + const charT &front() const; + charT &front(); + const charT &back() const; + charT &back(); + + const_reference operator[](size_type pos) const; + reference operator[](size_type pos); + const_reference at(size_type n) const; + reference at(size_type n); + basic_string &operator+=(const basic_string &str); + basic_string &operator+=(const charT *s); + basic_string &operator+=(charT c); + basic_string &operator+=(initializer_list); + basic_string &append(const basic_string &str); + basic_string &append(const basic_string &str, size_type pos, + size_type n = npos); + basic_string &append(const charT *s, size_type n); + basic_string &append(const charT *s); + basic_string &append(size_type n, charT c); + template + basic_string &append(InputIterator first, InputIterator last); + basic_string &append(initializer_list); + void push_back(charT c); + basic_string &assign(const basic_string &str); + basic_string &assign(basic_string &&str) noexcept; + basic_string &assign(const basic_string &str, size_type pos, + size_type n = npos); + basic_string &assign(const charT *s, size_type n); + basic_string &assign(const charT *s); + basic_string &assign(size_type n, charT c); + template + basic_string &assign(InputIterator first, InputIterator last); + basic_string &assign(initializer_list); + basic_string &insert(size_type pos1, const basic_string &str); + basic_string &insert(size_type pos1, const basic_string &str, size_type pos2, + size_type n = npos); + basic_string &insert(size_type pos, const charT *s, size_type n); + basic_string &insert(size_type pos, const charT *s); + basic_string &insert(size_type pos, size_type n, charT c); + iterator insert(const_iterator p, charT c); + iterator insert(const_iterator p, size_type n, charT c); + template + iterator insert(const_iterator p, InputIterator first, InputIterator last); + iterator insert(const_iterator p, initializer_list); + basic_string &erase(size_type pos = 0, size_type n = npos); + iterator erase(const_iterator p); + iterator erase(const_iterator first, const_iterator last); + basic_string &replace(size_type pos1, size_type n1, const basic_string &str); + basic_string &replace(size_type pos1, size_type n1, const basic_string &str, + size_type pos2, size_type n2 = npos); + basic_string &replace(size_type pos, size_type n1, const charT *s, + size_type n2); + basic_string &replace(size_type pos, size_type n1, const charT *s); + basic_string &replace(size_type pos, size_type n1, size_type n2, charT c); + basic_string &replace(__const_iterator i1, __const_iterator i2, + const basic_string &str); + basic_string &replace(__const_iterator i1, __const_iterator i2, const charT *s, + size_type n); + basic_string &replace(__const_iterator i1, __const_iterator i2, const charT *s); + basic_string &replace(__const_iterator i1, __const_iterator i2, size_type n, + charT c); + template + basic_string &replace(__const_iterator i1, __const_iterator i2, InputIterator j1, + InputIterator j2); + basic_string &replace(__const_iterator, __const_iterator, + initializer_list); + + size_type copy(charT *s, size_type n, size_type pos = 0) const; + void clear() noexcept; + void swap(basic_string &s) noexcept; + + size_type find(const basic_string &str, size_type pos = 0) const noexcept; + size_type find(const charT *s, size_type pos, size_type n) const; + size_type find(const charT *s, size_type pos = 0) const; + size_type find(charT c, size_type pos = 0) const; + size_type rfind(const basic_string &str, size_type pos = npos) const noexcept; + size_type rfind(const charT *s, size_type pos, size_type n) const; + size_type rfind(const charT *s, size_type pos = npos) const; + size_type rfind(charT c, size_type pos = npos) const; + size_type find_first_of(const basic_string &str, + size_type pos = 0) const noexcept; + size_type find_first_of(const charT *s, size_type pos, size_type n) const; + size_type find_first_of(const charT *s, size_type pos = 0) const; + size_type find_first_of(charT c, size_type pos = 0) const; + size_type find_last_of(const basic_string &str, + size_type pos = npos) const noexcept; + size_type find_last_of(const charT *s, size_type pos, size_type n) const; + size_type find_last_of(const charT *s, size_type pos = npos) const; + size_type find_last_of(charT c, size_type pos = npos) const; + size_type find_first_not_of(const basic_string &str, + size_type pos = 0) const noexcept; + size_type find_first_not_of(const charT *s, size_type pos, size_type n) const; + size_type find_first_not_of(const charT *s, size_type pos = 0) const; + size_type find_first_not_of(charT c, size_type pos = 0) const; + size_type find_last_not_of(const basic_string &str, + + size_type pos = npos) const noexcept; + size_type find_last_not_of(const charT *s, size_type pos, size_type n) const; + size_type find_last_not_of(const charT *s, size_type pos = npos) const; + size_type find_last_not_of(charT c, size_type pos = npos) const; + basic_string substr(size_type pos = 0, size_type n = npos) const; + int compare(const basic_string &str) const noexcept; + int compare(size_type pos1, size_type n1, const basic_string &str) const; + int compare(size_type pos1, size_type n1, const basic_string &str, + size_type pos2, size_type n2 = npos) const; + int compare(const charT *s) const; + int compare(size_type pos1, size_type n1, const charT *s) const; + int compare(size_type pos1, size_type n1, const charT *s, size_type n2) const; + + void reserve(size_type new_cap = 0); +}; + +template +basic_string +operator+(const basic_string &lhs, + const basic_string &rhs); +template +basic_string +operator+(const basic_string &lhs, const charT *rhs); +template +basic_string +operator+(const charT *lhs, const basic_string &rhs); + +template +bool operator==(const basic_string &lhs, + const basic_string &rhs) noexcept; +template +bool operator==(const charT *lhs, + const basic_string &rhs); +template +bool operator==(const basic_string &lhs, + const charT *rhs); +template +bool operator!=(const basic_string &lhs, + const basic_string &rhs) noexcept; +template +bool operator!=(const charT *lhs, + const basic_string &rhs); +template +bool operator!=(const basic_string &lhs, + const charT *rhs); +template +bool operator<(const basic_string &lhs, + const basic_string &rhs) noexcept; +template +bool operator<(const basic_string &lhs, + const charT *rhs); +template +bool operator<(const charT *lhs, + const basic_string &rhs); +template +bool operator>(const basic_string &lhs, + const basic_string &rhs) noexcept; +template +bool operator>(const basic_string &lhs, + const charT *rhs); +template +bool operator>(const charT *lhs, + const basic_string &rhs); +template +bool operator<=(const basic_string &lhs, + const basic_string &rhs) noexcept; +template +bool operator<=(const basic_string &lhs, + const charT *rhs); +template +bool operator<=(const charT *lhs, + const basic_string &rhs); +template +bool operator>=(const basic_string &lhs, + const basic_string &rhs) noexcept; +template +bool operator>=(const basic_string &lhs, + const charT *rhs); +template +bool operator>=(const charT *lhs, + const basic_string &rhs); + +typedef basic_string string; + +int stoi(const string &str, size_t *idx = 0, int base = 10); +long stol(const string &str, size_t *idx = 0, int base = 10); +unsigned long stoul(const string &str, size_t *idx = 0, int base = 10); +long long stoll(const string &str, size_t *idx = 0, int base = 10); +unsigned long long stoull(const string &str, size_t *idx = 0, int base = 10); +float stof(const string &str, size_t *idx = 0); +double stod(const string &str, size_t *idx = 0); +long double stold(const string &str, size_t *idx = 0); + +std::string to_string(int value); +} // namespace std + +#endif // _GHLIBCPP_STRING \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/string.h b/cpp/common/test/includes/standard-library/string.h index f514394688..d94a186f0e 100644 --- a/cpp/common/test/includes/standard-library/string.h +++ b/cpp/common/test/includes/standard-library/string.h @@ -1,242 +1,10 @@ -#ifndef _GHLIBCPP_STRING -#define _GHLIBCPP_STRING -#include "errno.h" -#include "initializer_list" -#include "iosfwd.h" -#include "iterator.h" -#include "stddef.h" - -namespace std { -template struct char_traits; - -template class allocator { -public: - allocator() throw(); - typedef size_t size_type; -}; - -template , - class Allocator = allocator> -class basic_string { -public: - using value_type = charT; - using reference = value_type &; - using const_reference = const value_type &; - typedef typename Allocator::size_type size_type; - static const size_type npos = -1; - - basic_string() : basic_string(Allocator()) {} - explicit basic_string(const Allocator &a); - basic_string(const basic_string &str); - basic_string(basic_string &&str) noexcept; - basic_string(const charT *s, size_type n, const Allocator &a = Allocator()); - basic_string(const charT *s, const Allocator &a = Allocator()); - basic_string(size_type n, charT c, const Allocator &a = Allocator()); - template - basic_string(InputIterator begin, InputIterator end, - const Allocator &a = Allocator()); - - ~basic_string(); - basic_string &operator=(const basic_string &str); - basic_string &operator=(basic_string &&str) noexcept; - basic_string &operator=(const charT *s); - basic_string &operator=(charT c); - basic_string &operator=(initializer_list); - - const charT *c_str() const; - charT *data() noexcept; - size_type size() const noexcept; - size_type length() const noexcept; - - typedef __iterator iterator; - typedef __iterator const_iterator; - - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; - - const charT &front() const; - charT &front(); - const charT &back() const; - charT &back(); - - const_reference operator[](size_type pos) const; - reference operator[](size_type pos); - const_reference at(size_type n) const; - reference at(size_type n); - basic_string &operator+=(const basic_string &str); - basic_string &operator+=(const charT *s); - basic_string &operator+=(charT c); - basic_string &operator+=(initializer_list); - basic_string &append(const basic_string &str); - basic_string &append(const basic_string &str, size_type pos, - size_type n = npos); - basic_string &append(const charT *s, size_type n); - basic_string &append(const charT *s); - basic_string &append(size_type n, charT c); - template - basic_string &append(InputIterator first, InputIterator last); - basic_string &append(initializer_list); - void push_back(charT c); - basic_string &assign(const basic_string &str); - basic_string &assign(basic_string &&str) noexcept; - basic_string &assign(const basic_string &str, size_type pos, - size_type n = npos); - basic_string &assign(const charT *s, size_type n); - basic_string &assign(const charT *s); - basic_string &assign(size_type n, charT c); - template - basic_string &assign(InputIterator first, InputIterator last); - basic_string &assign(initializer_list); - basic_string &insert(size_type pos1, const basic_string &str); - basic_string &insert(size_type pos1, const basic_string &str, size_type pos2, - size_type n = npos); - basic_string &insert(size_type pos, const charT *s, size_type n); - basic_string &insert(size_type pos, const charT *s); - basic_string &insert(size_type pos, size_type n, charT c); - iterator insert(const_iterator p, charT c); - iterator insert(const_iterator p, size_type n, charT c); - template - iterator insert(const_iterator p, InputIterator first, InputIterator last); - iterator insert(const_iterator p, initializer_list); - basic_string &erase(size_type pos = 0, size_type n = npos); - iterator erase(const_iterator p); - iterator erase(const_iterator first, const_iterator last); - basic_string &replace(size_type pos1, size_type n1, const basic_string &str); - basic_string &replace(size_type pos1, size_type n1, const basic_string &str, - size_type pos2, size_type n2 = npos); - basic_string &replace(size_type pos, size_type n1, const charT *s, - size_type n2); - basic_string &replace(size_type pos, size_type n1, const charT *s); - basic_string &replace(size_type pos, size_type n1, size_type n2, charT c); - basic_string &replace(const_iterator i1, const_iterator i2, - const basic_string &str); - basic_string &replace(const_iterator i1, const_iterator i2, const charT *s, - size_type n); - basic_string &replace(const_iterator i1, const_iterator i2, const charT *s); - basic_string &replace(const_iterator i1, const_iterator i2, size_type n, - charT c); - template - basic_string &replace(const_iterator i1, const_iterator i2, InputIterator j1, - InputIterator j2); - basic_string &replace(const_iterator, const_iterator, - initializer_list); +#ifndef _GHLIBCPP_STRINGH +#define _GHLIBCPP_STRINGH - size_type copy(charT *s, size_type n, size_type pos = 0) const; - void clear() noexcept; - void swap(basic_string &s) noexcept; - - size_type find(const basic_string &str, size_type pos = 0) const noexcept; - size_type find(const charT *s, size_type pos, size_type n) const; - size_type find(const charT *s, size_type pos = 0) const; - size_type find(charT c, size_type pos = 0) const; - size_type rfind(const basic_string &str, size_type pos = npos) const noexcept; - size_type rfind(const charT *s, size_type pos, size_type n) const; - size_type rfind(const charT *s, size_type pos = npos) const; - size_type rfind(charT c, size_type pos = npos) const; - size_type find_first_of(const basic_string &str, - size_type pos = 0) const noexcept; - size_type find_first_of(const charT *s, size_type pos, size_type n) const; - size_type find_first_of(const charT *s, size_type pos = 0) const; - size_type find_first_of(charT c, size_type pos = 0) const; - size_type find_last_of(const basic_string &str, - size_type pos = npos) const noexcept; - size_type find_last_of(const charT *s, size_type pos, size_type n) const; - size_type find_last_of(const charT *s, size_type pos = npos) const; - size_type find_last_of(charT c, size_type pos = npos) const; - size_type find_first_not_of(const basic_string &str, - size_type pos = 0) const noexcept; - size_type find_first_not_of(const charT *s, size_type pos, size_type n) const; - size_type find_first_not_of(const charT *s, size_type pos = 0) const; - size_type find_first_not_of(charT c, size_type pos = 0) const; - size_type find_last_not_of(const basic_string &str, - - size_type pos = npos) const noexcept; - size_type find_last_not_of(const charT *s, size_type pos, size_type n) const; - size_type find_last_not_of(const charT *s, size_type pos = npos) const; - size_type find_last_not_of(charT c, size_type pos = npos) const; - basic_string substr(size_type pos = 0, size_type n = npos) const; - int compare(const basic_string &str) const noexcept; - int compare(size_type pos1, size_type n1, const basic_string &str) const; - int compare(size_type pos1, size_type n1, const basic_string &str, - size_type pos2, size_type n2 = npos) const; - int compare(const charT *s) const; - int compare(size_type pos1, size_type n1, const charT *s) const; - int compare(size_type pos1, size_type n1, const charT *s, size_type n2) const; -}; - -template -basic_string -operator+(const basic_string &lhs, - const basic_string &rhs); -template -basic_string -operator+(const basic_string &lhs, const charT *rhs); -template -basic_string -operator+(const charT *lhs, const basic_string &rhs); - -template -bool operator==(const basic_string &lhs, - const basic_string &rhs) noexcept; -template -bool operator==(const charT *lhs, - const basic_string &rhs); -template -bool operator==(const basic_string &lhs, - const charT *rhs); -template -bool operator!=(const basic_string &lhs, - const basic_string &rhs) noexcept; -template -bool operator!=(const charT *lhs, - const basic_string &rhs); -template -bool operator!=(const basic_string &lhs, - const charT *rhs); -template -bool operator<(const basic_string &lhs, - const basic_string &rhs) noexcept; -template -bool operator<(const basic_string &lhs, - const charT *rhs); -template -bool operator<(const charT *lhs, - const basic_string &rhs); -template -bool operator>(const basic_string &lhs, - const basic_string &rhs) noexcept; -template -bool operator>(const basic_string &lhs, - const charT *rhs); -template -bool operator>(const charT *lhs, - const basic_string &rhs); -template -bool operator<=(const basic_string &lhs, - const basic_string &rhs) noexcept; -template -bool operator<=(const basic_string &lhs, - const charT *rhs); -template -bool operator<=(const charT *lhs, - const basic_string &rhs); -template -bool operator>=(const basic_string &lhs, - const basic_string &rhs) noexcept; -template -bool operator>=(const basic_string &lhs, - const charT *rhs); -template -bool operator>=(const charT *lhs, - const basic_string &rhs); - -typedef basic_string string; +#include "errno.h" typedef unsigned long size_t; + size_t strlen(const char *str); char *strcpy(char *destination, const char *source); char *strncpy(char *destination, const char *source, size_t num); @@ -268,27 +36,13 @@ char *strstr(char *str1, const char *str2); char *strtok(char *str, const char *delimiters); +char *strdup (const char *); + void *memcpy(void *dest, const void *src, size_t count); void *memset(void *dest, int ch, size_t count); void *memmove(void *dest, const void *src, size_t count); int memcmp(const void *lhs, const void *rhs, size_t count); -errno_t memcpy_s(void *dest, rsize_t destsz, const void *src, rsize_t count); -errno_t memmove_s(void *dest, rsize_t destsz, const void *src, rsize_t count); - -int stoi(const string &str, size_t *idx = 0, int base = 10); -long stol(const string &str, size_t *idx = 0, int base = 10); -unsigned long stoul(const string &str, size_t *idx = 0, int base = 10); -long long stoll(const string &str, size_t *idx = 0, int base = 10); -unsigned long long stoull(const string &str, size_t *idx = 0, int base = 10); -float stof(const string &str, size_t *idx = 0); -double stod(const string &str, size_t *idx = 0); -long double stold(const string &str, size_t *idx = 0); - -std::string to_string(int value); -} // namespace std - -std::errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count); size_t strlen(const char *str); -#endif // _GHLIBCPP_STRING \ No newline at end of file +#endif // _GHLIBCPP_STRINGH \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/time.h b/cpp/common/test/includes/standard-library/time.h index e69de29bb2..cc7ff1673a 100644 --- a/cpp/common/test/includes/standard-library/time.h +++ b/cpp/common/test/includes/standard-library/time.h @@ -0,0 +1,32 @@ +#ifndef _GHLIBCPP_TIME +#define _GHLIBCPP_TIME + +typedef unsigned long clock_t; +typedef unsigned long time_t; + +typedef unsigned long size_t; +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +clock_t clock(void); +double difftime(clock_t end, clock_t beginning); +time_t mktime(struct tm *timeptr); +time_t time(time_t *timer); +char *asctime(const struct tm *timeptr); + +char *ctime(const time_t *timer); +struct tm *gmtime(const time_t *timer); +struct tm *localtime(const time_t *timer); +size_t strftime(char *ptr, size_t maxsize, const char *format, + const struct tm *timeptr); + +#endif // _GHLIBCPP_TIME \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/tuple.h b/cpp/common/test/includes/standard-library/tuple.h index 8602d4ca1e..e4ab473488 100644 --- a/cpp/common/test/includes/standard-library/tuple.h +++ b/cpp/common/test/includes/standard-library/tuple.h @@ -1,4 +1,11 @@ namespace std { template class tuple {}; template std::tuple make_tuple(Types &&...args); +struct ignore_t { + template + constexpr // required since C++14 + void + operator=(T &&) const noexcept {} +}; +inline const std::ignore_t ignore; // 'const' only until C++17 } // namespace std diff --git a/cpp/common/test/includes/standard-library/typeinfo.h b/cpp/common/test/includes/standard-library/typeinfo.h index dbe2d07503..6be058f090 100644 --- a/cpp/common/test/includes/standard-library/typeinfo.h +++ b/cpp/common/test/includes/standard-library/typeinfo.h @@ -4,5 +4,6 @@ namespace std { struct type_info { const char *name() const noexcept; std::size_t hash_code() const noexcept; + bool operator==(const type_info &rhs) const; }; } // namespace std \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/vector.h b/cpp/common/test/includes/standard-library/vector.h index caa443f641..6d0293f8f5 100644 --- a/cpp/common/test/includes/standard-library/vector.h +++ b/cpp/common/test/includes/standard-library/vector.h @@ -1,7 +1,7 @@ #ifndef _GHLIBCPP_VECTOR #define _GHLIBCPP_VECTOR -#include "iterator.h" -#include "string.h" +#include +#include namespace std { diff --git a/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected new file mode 100644 index 0000000000..c273346e3b --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected @@ -0,0 +1,7 @@ +| deduplicatemacroresults.cpp:4:8:4:9 | definition of g1 | Findme var 'g1'. | deduplicatemacroresults.cpp:4:8:4:9 | definition of g1 | | +| deduplicatemacroresults.cpp:10:1:10:34 | SOMETIMES_HAS_RESULTS1(type,name) | Invocation of macro $@ has findme var 'g3'. | deduplicatemacroresults.cpp:6:1:6:52 | #define SOMETIMES_HAS_RESULTS1(type,name) type name | SOMETIMES_HAS_RESULTS1 | +| deduplicatemacroresults.cpp:13:1:13:34 | SOMETIMES_HAS_RESULTS2(type,name) | Invocation of macro $@ has findme var 'g5'. | deduplicatemacroresults.cpp:7:1:7:53 | #define SOMETIMES_HAS_RESULTS2(type,name) type name; | SOMETIMES_HAS_RESULTS2 | +| deduplicatemacroresults.cpp:15:1:15:50 | #define ALWAYS_HAS_SAME_RESULT() extern findme g6; | Macro ALWAYS_HAS_SAME_RESULT always has findme var named g6 | deduplicatemacroresults.cpp:15:1:15:50 | #define ALWAYS_HAS_SAME_RESULT() extern findme g6; | (ignored) | +| deduplicatemacroresults.cpp:21:1:21:70 | #define ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) extern findme name; | Macro ALWAYS_HAS_RESULT_VARIED_DESCRIPTION always has findme var, for example '$@'. | deduplicatemacroresults.cpp:23:38:23:39 | declaration of g7 | g7 | +| deduplicatemacroresults.cpp:30:1:31:50 | #define OUTER_ALWAYS_HAS_SAME_RESULT() extern INNER_SOMETIMES_HAS_RESULTS(findme, g10); | Macro OUTER_ALWAYS_HAS_SAME_RESULT always has findme var named g10 | deduplicatemacroresults.cpp:30:1:31:50 | #define OUTER_ALWAYS_HAS_SAME_RESULT() extern INNER_SOMETIMES_HAS_RESULTS(findme, g10); | (ignored) | +| deduplicatemacroresults.cpp:37:1:38:52 | #define OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) INNER_SOMETIMES_HAS_RESULTS(findme, name ## suffix); | Macro OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION always has findme var, for example '$@'. | deduplicatemacroresults.cpp:40:44:40:47 | definition of g11suffix | g11suffix | diff --git a/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql new file mode 100644 index 0000000000..68cfc9a78f --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql @@ -0,0 +1,36 @@ +import cpp +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +class FindMe extends VariableDeclarationEntry { + FindMe() { getType().toString() = "findme" } +} + +module FindMeDedupeConfig implements DeduplicateMacroConfigSig { + string describe(FindMe def) { result = def.getName() } +} + +module FindMeReportConfig implements MacroReportConfigSig { + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = "Macro " + m.getName() + " always has findme var named " + description + } + + string getMessageVariedResultInAllExpansions(Macro m) { + result = "Macro " + m.getName() + " always has findme var, for example '$@'." + } + + string getMessageResultInIsolatedExpansion(FindMe f) { + result = "Invocation of macro $@ has findme var '" + f.getName() + "'." + } + + string getMessageNotInMacro(FindMe f, Locatable extra, string extraString) { + result = "Findme var '" + f.getName() + "'." and extra = f and extraString = "" + } +} + +import DeduplicateMacroResults +import DeduplicateMacroResults::Report + +from ReportResult report +select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), + report.getOptionalPlaceholderMessage() diff --git a/cpp/common/test/library/codingstandards/cpp/alertreporting/deduplicatemacroresults.cpp b/cpp/common/test/library/codingstandards/cpp/alertreporting/deduplicatemacroresults.cpp new file mode 100644 index 0000000000..d9b3659bf6 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/alertreporting/deduplicatemacroresults.cpp @@ -0,0 +1,53 @@ +typedef struct { +} findme; + +findme g1; // baseline report, not in a macro + +#define SOMETIMES_HAS_RESULTS1(type, name) type name // ignore +#define SOMETIMES_HAS_RESULTS2(type, name) type name; // ignore + +SOMETIMES_HAS_RESULTS1(int, g2); // ignore +SOMETIMES_HAS_RESULTS1(findme, g3); // RESULT + +SOMETIMES_HAS_RESULTS2(int, g4) // ignore +SOMETIMES_HAS_RESULTS2(findme, g5) // RESULT + +#define ALWAYS_HAS_SAME_RESULT() extern findme g6; // RESULT + +ALWAYS_HAS_SAME_RESULT() // ignore +ALWAYS_HAS_SAME_RESULT() // ignore +ALWAYS_HAS_SAME_RESULT() // ignore + +#define ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) extern findme name; // RESULT + +ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g7) // ignore +ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g8) // ignore +ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g9) // ignore +ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g9) // ignore +ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g9) // ignore + +#define INNER_SOMETIMES_HAS_RESULTS(type, name) type name; // ignore +#define OUTER_ALWAYS_HAS_SAME_RESULT() \ + extern INNER_SOMETIMES_HAS_RESULTS(findme, g10); // RESULT + +OUTER_ALWAYS_HAS_SAME_RESULT() // ignore +OUTER_ALWAYS_HAS_SAME_RESULT() // ignore + +// 'name ## suffix' required to work around extractor bug. +#define OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) \ + INNER_SOMETIMES_HAS_RESULTS(findme, name##suffix); // RESULT + +OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g11) // ignore +OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g12) // ignore + +#define OUTER_OUTER_ALWAYS_HAS_SAME_RESULT() \ + OUTER_ALWAYS_HAS_SAME_RESULT(); // ignore +OUTER_OUTER_ALWAYS_HAS_SAME_RESULT() // ignore +OUTER_OUTER_ALWAYS_HAS_SAME_RESULT() // ignore + +// 'name ## suffix' required to work around extractor bug. +#define OUTER_OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) \ + OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name##suffix); // ignore + +OUTER_OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g13) // ignore +OUTER_OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(g14) // ignore \ No newline at end of file diff --git a/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/MainLikeFunction.expected b/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/MainLikeFunction.expected new file mode 100644 index 0000000000..fa98ca7648 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/MainLikeFunction.expected @@ -0,0 +1 @@ +| test.cpp:5:9:5:12 | main | diff --git a/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/MainLikeFunction.ql b/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/MainLikeFunction.ql new file mode 100644 index 0000000000..ed1757631e --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/MainLikeFunction.ql @@ -0,0 +1,5 @@ +import cpp +import codingstandards.cpp.EncapsulatingFunctions + +from MainFunction m +select m diff --git a/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/test.cpp b/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/test.cpp new file mode 100644 index 0000000000..7b514505f1 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/mainlikefunctions/typedefint/test.cpp @@ -0,0 +1,8 @@ +typedef signed int int32_t; + +// @brief main +// @return exit code +int32_t main(void) { + int32_t ret{0}; + return ret; +} diff --git a/cpp/common/test/library/codingstandards/cpp/scope/ParentScope.expected b/cpp/common/test/library/codingstandards/cpp/scope/ParentScope.expected new file mode 100644 index 0000000000..90aa3b30c8 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/scope/ParentScope.expected @@ -0,0 +1,91 @@ +| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:8:7:8:7 | operator= | +| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:8:7:8:7 | operator= | +| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:27:28:27:28 | (unnamed constructor) | +| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:27:28:27:28 | (unnamed constructor) | +| file://:0:0:0:0 | (unnamed parameter 0) | test.cpp:27:28:27:28 | operator= | +| file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | (global namespace) | +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | operator= | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | operator= | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | __va_list_tag | +| test.cpp:1:5:1:7 | id1 | file://:0:0:0:0 | (global namespace) | +| test.cpp:3:11:3:13 | ns1 | file://:0:0:0:0 | (global namespace) | +| test.cpp:4:5:4:7 | id1 | test.cpp:3:11:3:13 | ns1 | +| test.cpp:6:11:6:13 | ns1::ns2 | test.cpp:3:11:3:13 | ns1 | +| test.cpp:7:5:7:7 | id1 | test.cpp:6:11:6:13 | ns1::ns2 | +| test.cpp:8:7:8:7 | C1 | test.cpp:8:7:8:8 | C1 | +| test.cpp:8:7:8:7 | operator= | test.cpp:8:7:8:8 | C1 | +| test.cpp:8:7:8:7 | operator= | test.cpp:8:7:8:8 | C1 | +| test.cpp:8:7:8:8 | C1 | test.cpp:6:11:6:13 | ns1::ns2 | +| test.cpp:9:7:9:9 | id1 | test.cpp:8:7:8:8 | C1 | +| test.cpp:10:8:10:17 | test_scope | test.cpp:8:7:8:8 | C1 | +| test.cpp:10:23:10:25 | id1 | test.cpp:10:8:10:17 | test_scope | +| test.cpp:10:28:34:3 | { ... } | test.cpp:10:8:10:17 | test_scope | +| test.cpp:11:5:33:5 | for(...;...;...) ... | test.cpp:10:28:34:3 | { ... } | +| test.cpp:11:10:11:17 | declaration | test.cpp:11:5:33:5 | for(...;...;...) ... | +| test.cpp:11:14:11:16 | id1 | test.cpp:11:5:33:5 | for(...;...;...) ... | +| test.cpp:11:19:11:21 | id1 | test.cpp:10:28:34:3 | { ... } | +| test.cpp:11:19:11:25 | ... < ... | test.cpp:11:5:33:5 | for(...;...;...) ... | +| test.cpp:11:25:11:25 | 1 | test.cpp:10:28:34:3 | { ... } | +| test.cpp:11:28:11:30 | id1 | test.cpp:10:28:34:3 | { ... } | +| test.cpp:11:28:11:32 | ... ++ | test.cpp:11:5:33:5 | for(...;...;...) ... | +| test.cpp:11:35:33:5 | { ... } | test.cpp:11:5:33:5 | for(...;...;...) ... | +| test.cpp:12:7:32:7 | for(...;...;...) ... | test.cpp:11:35:33:5 | { ... } | +| test.cpp:12:12:12:19 | declaration | test.cpp:12:7:32:7 | for(...;...;...) ... | +| test.cpp:12:16:12:18 | id1 | test.cpp:12:7:32:7 | for(...;...;...) ... | +| test.cpp:12:21:12:23 | id1 | test.cpp:11:35:33:5 | { ... } | +| test.cpp:12:21:12:27 | ... < ... | test.cpp:12:7:32:7 | for(...;...;...) ... | +| test.cpp:12:27:12:27 | 1 | test.cpp:11:35:33:5 | { ... } | +| test.cpp:12:30:12:32 | id1 | test.cpp:11:35:33:5 | { ... } | +| test.cpp:12:30:12:34 | ... ++ | test.cpp:12:7:32:7 | for(...;...;...) ... | +| test.cpp:12:37:32:7 | { ... } | test.cpp:12:7:32:7 | for(...;...;...) ... | +| test.cpp:13:9:31:9 | { ... } | test.cpp:12:37:32:7 | { ... } | +| test.cpp:14:11:14:18 | declaration | test.cpp:13:9:31:9 | { ... } | +| test.cpp:14:15:14:17 | id1 | test.cpp:13:9:31:9 | { ... } | +| test.cpp:16:11:20:11 | if (...) ... | test.cpp:13:9:31:9 | { ... } | +| test.cpp:16:15:16:17 | id1 | test.cpp:13:9:31:9 | { ... } | +| test.cpp:16:15:16:22 | ... == ... | test.cpp:13:9:31:9 | { ... } | +| test.cpp:16:22:16:22 | 0 | test.cpp:13:9:31:9 | { ... } | +| test.cpp:16:25:18:11 | { ... } | test.cpp:16:11:20:11 | if (...) ... | +| test.cpp:17:13:17:20 | declaration | test.cpp:16:25:18:11 | { ... } | +| test.cpp:17:17:17:19 | id1 | test.cpp:16:25:18:11 | { ... } | +| test.cpp:18:18:20:11 | { ... } | test.cpp:16:11:20:11 | if (...) ... | +| test.cpp:19:13:19:20 | declaration | test.cpp:18:18:20:11 | { ... } | +| test.cpp:19:17:19:19 | id1 | test.cpp:18:18:20:11 | { ... } | +| test.cpp:21:11:25:11 | switch (...) ... | test.cpp:13:9:31:9 | { ... } | +| test.cpp:21:19:21:21 | id1 | test.cpp:13:9:31:9 | { ... } | +| test.cpp:21:24:25:11 | { ... } | test.cpp:21:11:25:11 | switch (...) ... | +| test.cpp:22:11:22:17 | case ...: | test.cpp:21:24:25:11 | { ... } | +| test.cpp:22:16:22:16 | 0 | test.cpp:21:24:25:11 | { ... } | +| test.cpp:23:13:23:20 | declaration | test.cpp:21:24:25:11 | { ... } | +| test.cpp:23:17:23:19 | id1 | test.cpp:21:24:25:11 | { ... } | +| test.cpp:24:13:24:18 | break; | test.cpp:21:24:25:11 | { ... } | +| test.cpp:25:11:25:11 | label ...: | test.cpp:13:9:31:9 | { ... } | +| test.cpp:26:11:28:11 | try { ... } | test.cpp:13:9:31:9 | { ... } | +| test.cpp:26:15:28:11 | { ... } | test.cpp:13:9:31:9 | { ... } | +| test.cpp:27:13:27:53 | declaration | test.cpp:26:15:28:11 | { ... } | +| test.cpp:27:18:27:24 | lambda1 | test.cpp:26:15:28:11 | { ... } | +| test.cpp:27:27:27:52 | [...](...){...} | test.cpp:26:15:28:11 | { ... } | +| test.cpp:27:27:27:52 | {...} | test.cpp:26:15:28:11 | { ... } | +| test.cpp:27:28:27:28 | (unnamed constructor) | file://:0:0:0:0 | decltype([...](...){...}) | +| test.cpp:27:28:27:28 | (unnamed constructor) | file://:0:0:0:0 | decltype([...](...){...}) | +| test.cpp:27:28:27:28 | (unnamed constructor) | file://:0:0:0:0 | decltype([...](...){...}) | +| test.cpp:27:28:27:28 | operator= | file://:0:0:0:0 | decltype([...](...){...}) | +| test.cpp:27:29:27:29 | id1 | file://:0:0:0:0 | decltype([...](...){...}) | +| test.cpp:27:29:27:31 | id1 | test.cpp:26:15:28:11 | { ... } | +| test.cpp:27:33:27:33 | operator() | test.cpp:27:13:27:53 | declaration | +| test.cpp:27:36:27:52 | { ... } | test.cpp:27:33:27:33 | operator() | +| test.cpp:27:38:27:50 | declaration | test.cpp:27:36:27:52 | { ... } | +| test.cpp:27:42:27:44 | id1 | test.cpp:27:36:27:52 | { ... } | +| test.cpp:27:47:27:49 | 10 | test.cpp:27:36:27:52 | { ... } | +| test.cpp:27:52:27:52 | return ... | test.cpp:27:36:27:52 | { ... } | +| test.cpp:28:24:28:26 | id1 | test.cpp:28:29:30:11 | | +| test.cpp:28:29:30:11 | | test.cpp:26:11:28:11 | try { ... } | +| test.cpp:28:29:30:11 | { ... } | test.cpp:13:9:31:9 | { ... } | +| test.cpp:29:13:29:20 | declaration | test.cpp:28:29:30:11 | { ... } | +| test.cpp:29:17:29:19 | id1 | test.cpp:28:29:30:11 | { ... } | +| test.cpp:34:3:34:3 | return ... | test.cpp:10:28:34:3 | { ... } | diff --git a/cpp/common/test/library/codingstandards/cpp/scope/ParentScope.ql b/cpp/common/test/library/codingstandards/cpp/scope/ParentScope.ql new file mode 100644 index 0000000000..47d27fb0f0 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/scope/ParentScope.ql @@ -0,0 +1,5 @@ +import codingstandards.cpp.Scope + +from Element e, Element parent +where Internal::getParentScope(e) = parent +select e, parent diff --git a/cpp/common/test/library/codingstandards/cpp/scope/test.cpp b/cpp/common/test/library/codingstandards/cpp/scope/test.cpp new file mode 100644 index 0000000000..a0b617916d --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/scope/test.cpp @@ -0,0 +1,37 @@ +int id1; + +namespace ns1 { +int id1; // COMPLIANT + +namespace ns2 { +int id1; // COMPLIANT +class C1 { + int id1; + void test_scope(int id1) { + for (int id1; id1 < 1; id1++) { + for (int id1; id1 < 1; id1++) { + { + int id1; + + if (id1 == 0) { + int id1; + } else { + int id1; + } + switch (id1) { + case 0: + int id1; + break; + } + try { + auto lambda1 = [id1]() { int id1 = 10; }; + } catch (int id1) { + int id1; + } + } + } + } + } +}; +} // namespace ns2 +} // namespace ns1 diff --git a/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql b/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql index 4d0f8567d0..e7a1d5ebd3 100644 --- a/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql +++ b/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType from Type t where diff --git a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql index a85fcf5676..edbfbe5303 100644 --- a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql +++ b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType from Type t where diff --git a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql index 3b3f498796..1667372f4b 100644 --- a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql +++ b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType from TriviallyCopyableClass t select t diff --git a/cpp/common/test/options b/cpp/common/test/options new file mode 100644 index 0000000000..59fc70d386 --- /dev/null +++ b/cpp/common/test/options @@ -0,0 +1 @@ +semmle-extractor-options:--clang -std=c++17 -nostdinc++ -I../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/common/test/qlpack.yml b/cpp/common/test/qlpack.yml index 8118155fe2..cb54217f76 100644 --- a/cpp/common/test/qlpack.yml +++ b/cpp/common/test/qlpack.yml @@ -1,4 +1,6 @@ -name: common-cpp-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: common-cpp-coding-standards +name: codeql/common-cpp-coding-standards-tests +version: 2.49.0-dev extractor: cpp +license: MIT +dependencies: + codeql/common-cpp-coding-standards: '*' diff --git a/cpp/common/test/rules/accessofnonexistingmemberthroughpointertomember/AccessOfNonExistingMemberThroughPointerToMember.ql b/cpp/common/test/rules/accessofnonexistingmemberthroughpointertomember/AccessOfNonExistingMemberThroughPointerToMember.ql index 06b77e4aae..3d65ee176a 100644 --- a/cpp/common/test/rules/accessofnonexistingmemberthroughpointertomember/AccessOfNonExistingMemberThroughPointerToMember.ql +++ b/cpp/common/test/rules/accessofnonexistingmemberthroughpointertomember/AccessOfNonExistingMemberThroughPointerToMember.ql @@ -1,2 +1,5 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.accessofnonexistingmemberthroughpointertomember.AccessOfNonExistingMemberThroughPointerToMember + +class TestFileQuery extends AccessOfNonExistingMemberThroughPointerToMemberSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.expected b/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.expected index a4e40cc6cb..7a43b3757e 100644 --- a/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.expected +++ b/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.expected @@ -2,8 +2,8 @@ problems | test.cpp:10:3:10:13 | call to expression | test.cpp:8:22:8:28 | 0 | test.cpp:10:9:10:10 | l2 | A null pointer-to-member value from $@ is passed as the second operand to a pointer-to-member expression. | test.cpp:8:22:8:28 | test.cpp:8:22:8:28 | initialization | | test.cpp:11:8:11:9 | l3 | test.cpp:9:17:9:23 | 0 | test.cpp:11:8:11:9 | l3 | A null pointer-to-member value from $@ is passed as the second operand to a pointer-to-member expression. | test.cpp:9:17:9:23 | test.cpp:9:17:9:23 | initialization | edges -| test.cpp:8:22:8:28 | 0 | test.cpp:10:9:10:10 | l2 | -| test.cpp:9:17:9:23 | 0 | test.cpp:11:8:11:9 | l3 | +| test.cpp:8:22:8:28 | 0 | test.cpp:10:9:10:10 | l2 | provenance | | +| test.cpp:9:17:9:23 | 0 | test.cpp:11:8:11:9 | l3 | provenance | | nodes | test.cpp:8:22:8:28 | 0 | semmle.label | 0 | | test.cpp:9:17:9:23 | 0 | semmle.label | 0 | diff --git a/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.ql b/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.ql index 47e6b02835..a94e11dbf6 100644 --- a/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.ql +++ b/cpp/common/test/rules/accessofundefinedmemberthroughnullpointer/AccessOfUndefinedMemberThroughNullPointer.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.accessofundefinedmemberthroughnullpointer.AccessOfUndefinedMemberThroughNullPointer + +class TestFileQuery extends AccessOfUndefinedMemberThroughNullPointerSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.ql b/cpp/common/test/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.ql index bb0594e7ac..90d192e3d8 100644 --- a/cpp/common/test/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.ql +++ b/cpp/common/test/rules/accessofundefinedmemberthroughuninitializedstaticpointer/AccessOfUndefinedMemberThroughUninitializedStaticPointer.ql @@ -1,2 +1,6 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.accessofundefinedmemberthroughuninitializedstaticpointer.AccessOfUndefinedMemberThroughUninitializedStaticPointer + +class TestFileQuery extends AccessOfUndefinedMemberThroughUninitializedStaticPointerSharedQuery, + TestQuery +{ } diff --git a/cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.expected b/cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.expected new file mode 100644 index 0000000000..f4129dd95c --- /dev/null +++ b/cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.expected @@ -0,0 +1,2 @@ +| test.cpp:2:5:2:13 | operator& | The unary & operator overloaded. | +| test.cpp:8:3:8:11 | operator& | The unary & operator overloaded. | diff --git a/cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.ql b/cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.ql new file mode 100644 index 0000000000..ee8ba0d5d5 --- /dev/null +++ b/cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.addressofoperatoroverloaded.AddressOfOperatorOverloaded + +class TestFileQuery extends AddressOfOperatorOverloadedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/addressofoperatoroverloaded/test.cpp b/cpp/common/test/rules/addressofoperatoroverloaded/test.cpp new file mode 100644 index 0000000000..5cd6b8b2d8 --- /dev/null +++ b/cpp/common/test/rules/addressofoperatoroverloaded/test.cpp @@ -0,0 +1,9 @@ +class A { + A operator&(); // NON_COMPLIANT - unary + constexpr A operator&(const A rhs); // COMPLIANT - binary +}; + +class B {}; + +B operator&(B b); // NON_COMPLIANT - unary +constexpr B operator&(const B lhs, const B rhs); // COMPLIANT - binary \ No newline at end of file diff --git a/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.expected b/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.expected new file mode 100644 index 0000000000..71355bf4cc --- /dev/null +++ b/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.expected @@ -0,0 +1,2 @@ +| test.cpp:5:1:5:41 | #define BAD_MACRO_WITH_ARG(x) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG contains use of parameter x used in multiple contexts. | +| test.cpp:6:1:6:48 | #define BAD_MACRO_WITH_ARG_TWO(x,y) (x) + wow ## x | Macro BAD_MACRO_WITH_ARG_TWO contains use of parameter x used in multiple contexts. | diff --git a/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql b/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql new file mode 100644 index 0000000000..5aa514e86d --- /dev/null +++ b/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.amixedusemacroargumentsubjecttoexpansion.AMixedUseMacroArgumentSubjectToExpansion + +class TestFileQuery extends AMixedUseMacroArgumentSubjectToExpansionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/test.cpp b/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/test.cpp new file mode 100644 index 0000000000..e96e2f7414 --- /dev/null +++ b/cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/test.cpp @@ -0,0 +1,26 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#define GOOD_MACRO_WITH_ARG(X) ((X)*X##_scale) // COMPLIANT +#define MACRO 1 +#define BAD_MACRO_WITH_ARG(x) (x) + wow##x // NON_COMPLIANT +#define BAD_MACRO_WITH_ARG_TWO(x, y) (x) + wow##x // NON_COMPLIANT +#define MACROONE(x) #x // COMPLIANT +#define MACROTWO(x) x *x // COMPLIANT +#define MACROTHREE(x) "##\"\"'" + (x) // COMPLIANT +#define FOO(x) #x MACROONE(x) // COMPLIANT - no further arg expansion + +void f() { + + int x; + int x_scale; + int y; + int wowMACRO = 0; + + y = GOOD_MACRO_WITH_ARG(x); + wowMACRO = BAD_MACRO_WITH_ARG(MACRO); + wowMACRO = BAD_MACRO_WITH_ARG_TWO(MACRO, 1); + char s[] = MACROONE(MACRO); + y = MACROTWO(MACRO); + MACROTHREE(MACRO); + FOO(x); +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.expected b/cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.expected similarity index 100% rename from cpp/autosar/test/rules/M5-2-12/IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer.expected rename to cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.expected diff --git a/cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.ql b/cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.ql new file mode 100644 index 0000000000..929e5affdf --- /dev/null +++ b/cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.arraypassedasfunctionargumentdecaytoapointer.ArrayPassedAsFunctionArgumentDecayToAPointer + +class TestFileQuery extends ArrayPassedAsFunctionArgumentDecayToAPointerSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M5-2-12/test.cpp b/cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M5-2-12/test.cpp rename to cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/test.cpp diff --git a/cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.expected b/cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.expected similarity index 100% rename from cpp/autosar/test/rules/A7-4-1/AsmDeclarationUsed.expected rename to cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.expected diff --git a/cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.ql b/cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.ql new file mode 100644 index 0000000000..5e60570f5a --- /dev/null +++ b/cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.asmdeclarationused.AsmDeclarationUsed + +class TestFileQuery extends AsmDeclarationUsedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A7-4-1/test.cpp b/cpp/common/test/rules/asmdeclarationused/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A7-4-1/test.cpp rename to cpp/common/test/rules/asmdeclarationused/test.cpp diff --git a/cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.expected b/cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.expected new file mode 100644 index 0000000000..9a849af3f4 --- /dev/null +++ b/cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.expected @@ -0,0 +1,4 @@ +| test.cpp:8:14:8:17 | call to atof | Call to banned function atof. | +| test.cpp:9:12:9:15 | call to atoi | Call to banned function atoi. | +| test.cpp:10:13:10:16 | call to atol | Call to banned function atol. | +| test.cpp:11:18:11:22 | call to atoll | Call to banned function atoll. | diff --git a/cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql b/cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql new file mode 100644 index 0000000000..6da5fe6097 --- /dev/null +++ b/cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.atofatoiatolandatollused.AtofAtoiAtolAndAtollUsed + +class TestFileQuery extends AtofAtoiAtolAndAtollUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/atofatoiatolandatollused/test.cpp b/cpp/common/test/rules/atofatoiatolandatollused/test.cpp new file mode 100644 index 0000000000..c995df6aad --- /dev/null +++ b/cpp/common/test/rules/atofatoiatolandatollused/test.cpp @@ -0,0 +1,13 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +#include +void f2(); +void f1() { + char l1[5] = "abcd"; + float l2 = atof(l1); // NON_COMLIANT + int l3 = atoi(l1); // NON_COMPLIANT + long l4 = atol(l1); // NON_COMPLIANT + long long l5 = atoll(l1); // NON_COMPLIANT + f2(); // COMPLIANT +} diff --git a/cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.expected b/cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.expected similarity index 100% rename from cpp/autosar/test/rules/A2-13-1/EscapeSequenceOutsideISO.expected rename to cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.expected diff --git a/cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.ql b/cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.ql new file mode 100644 index 0000000000..aa32fa3096 --- /dev/null +++ b/cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.backslashcharactermisuse.BackslashCharacterMisuse + +class TestFileQuery extends BackslashCharacterMisuseSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A2-13-1/test.cpp b/cpp/common/test/rules/backslashcharactermisuse/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A2-13-1/test.cpp rename to cpp/common/test/rules/backslashcharactermisuse/test.cpp diff --git a/cpp/common/test/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.ql b/cpp/common/test/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.ql index e1ba81c1f7..c2c4fe7906 100644 --- a/cpp/common/test/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.ql +++ b/cpp/common/test/rules/basicstringmaynotbenullterminated/BasicStringMayNotBeNullTerminated.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.basicstringmaynotbenullterminated.BasicStringMayNotBeNullTerminated + +class TestFileQuery extends BasicStringMayNotBeNullTerminatedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/basicstringmaynotbenullterminated/test.cpp b/cpp/common/test/rules/basicstringmaynotbenullterminated/test.cpp index e903a933af..973a7a4ad1 100644 --- a/cpp/common/test/rules/basicstringmaynotbenullterminated/test.cpp +++ b/cpp/common/test/rules/basicstringmaynotbenullterminated/test.cpp @@ -1,6 +1,6 @@ #include +#include #include -#include void f1() { char a1[7] = "CodeQL"; diff --git a/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.expected b/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.expected new file mode 100644 index 0000000000..346a557e32 --- /dev/null +++ b/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.expected @@ -0,0 +1,4 @@ +| test.cpp:9:7:9:8 | x1 | Bit-field 'x1' is declared on type 'int'. | +| test.cpp:13:15:13:16 | x5 | Bit-field 'x5' is declared on type 'signed long'. | +| test.cpp:15:15:15:16 | x6 | Bit-field 'x6' is declared on type 'signed char'. | +| test.cpp:17:14:17:15 | x7 | Bit-field 'x7' is declared on type 'Color'. | diff --git a/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql b/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql new file mode 100644 index 0000000000..a3e1ecc76c --- /dev/null +++ b/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.bitfieldshallhaveanappropriatetype.BitFieldShallHaveAnAppropriateType + +class TestFileQuery extends BitFieldShallHaveAnAppropriateTypeSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/test.cpp b/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/test.cpp new file mode 100644 index 0000000000..96b28997c4 --- /dev/null +++ b/cpp/common/test/rules/bitfieldshallhaveanappropriatetype/test.cpp @@ -0,0 +1,18 @@ + +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +typedef unsigned int UINT16; + +enum Color { R, G, B }; + +struct SampleStruct { + int x1 : 2; // NON_COMPLIANT - not explicitly signed or unsigned + unsigned int x2 : 2; // COMPLIANT - explicitly unsigned + signed int x3 : 2; // COMPLIANT - explicitly signed + UINT16 x4 : 2; // COMPLIANT - type alias resolves to a compliant type + signed long x5 : 2; // NON_COMPLIANT - cannot declare bit field for long, even + // if it's signed + signed char x6 : 2; // NON_COMPLIANT - cannot declare bit field for char, even + // if it's signed + enum Color x7 : 3; // NON_COMPLIANT - cannot declare bit field for enum +} sample_struct; diff --git a/cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.expected b/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.expected similarity index 100% rename from cpp/autosar/test/rules/M5-3-2/UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned.expected rename to cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.expected diff --git a/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql b/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql new file mode 100644 index 0000000000..3f5110e299 --- /dev/null +++ b/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.builtinunaryoperatorappliedtounsignedexpression.BuiltInUnaryOperatorAppliedToUnsignedExpression + +class TestFileQuery extends BuiltInUnaryOperatorAppliedToUnsignedExpressionSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/test.cpp b/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/test.cpp new file mode 100644 index 0000000000..530e46d567 --- /dev/null +++ b/cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/test.cpp @@ -0,0 +1,13 @@ +/* The unary minus operator shall not be applied to an expression +whose underlying type is unsigned +*/ +#include + +int K; +void f() { + + std::uint16_t a = -K; // COMPLIANT + std::int16_t b = -a; // NON_COMPLIANT + std::uint64_t c = K; // COMPLIANT + std::int64_t d = -c; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected b/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected new file mode 100644 index 0000000000..886d03ddac --- /dev/null +++ b/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.expected @@ -0,0 +1,2 @@ +| test.cpp:11:9:11:9 | (int32_t)... | Expression not converted to `unsigned char` before converting to a larger integer type. | +| test.cpp:12:41:12:41 | (signed int)... | Expression not converted to `unsigned char` before converting to a larger integer type. | diff --git a/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql b/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql new file mode 100644 index 0000000000..2a1e49774f --- /dev/null +++ b/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/CastCharBeforeConvertingToLargerSizes.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.castcharbeforeconvertingtolargersizes.CastCharBeforeConvertingToLargerSizes + +class TestFileQuery extends CastCharBeforeConvertingToLargerSizesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/test.cpp b/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/test.cpp new file mode 100644 index 0000000000..4e5d90e714 --- /dev/null +++ b/cpp/common/test/rules/castcharbeforeconvertingtolargersizes/test.cpp @@ -0,0 +1,17 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +template S get(T t) { + S s = t; // COMPLIANT + return s; +} + +void test(std::int32_t i32, std::int8_t i8, char c) { + i32 = c; // NON_COMPLIANT + i32 = get(c); // NON_COMPLIANT + i32 = get(c); // COMPLIANT + i32 = i8; // COMPLIANT + i32 = get(i8); // COMPLIANT + i32 = get(i8); // COMPLIANT +} diff --git a/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.expected b/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.expected new file mode 100644 index 0000000000..24493879f0 --- /dev/null +++ b/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.expected @@ -0,0 +1,2 @@ +| test.cpp:3:3:3:34 | reinterpret_cast<..(*)(..)>... | Cast converting a pointer to function. | +| test.cpp:4:3:4:30 | reinterpret_cast... | Cast converting a pointer to function. | diff --git a/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.ql b/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.ql new file mode 100644 index 0000000000..fd716b8570 --- /dev/null +++ b/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.castsbetweenapointertofunctionandanyothertype.CastsBetweenAPointerToFunctionAndAnyOtherType + +class TestFileQuery extends CastsBetweenAPointerToFunctionAndAnyOtherTypeSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/test.cpp b/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/test.cpp new file mode 100644 index 0000000000..aad03a054e --- /dev/null +++ b/cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/test.cpp @@ -0,0 +1,5 @@ + +void f(int) { + reinterpret_cast(&f); // NON_COMPLIANT + reinterpret_cast(&f); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/catchblockshadowing/CatchBlockShadowing.ql b/cpp/common/test/rules/catchblockshadowing/CatchBlockShadowing.ql index 197c55cbff..76b7123d99 100644 --- a/cpp/common/test/rules/catchblockshadowing/CatchBlockShadowing.ql +++ b/cpp/common/test/rules/catchblockshadowing/CatchBlockShadowing.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.catchblockshadowing.CatchBlockShadowing + +class TestFileQuery extends CatchBlockShadowingSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.ql b/cpp/common/test/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.ql index c6d1b86d3b..30d6d30c47 100644 --- a/cpp/common/test/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.ql +++ b/cpp/common/test/rules/catchexceptionsbylvaluereference/CatchExceptionsByLvalueReference.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.catchexceptionsbylvaluereference.CatchExceptionsByLvalueReference + +class TestFileQuery extends CatchExceptionsByLvalueReferenceSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.expected b/cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.expected similarity index 100% rename from cpp/autosar/test/rules/M2-7-1/SlashStarUsedWithinACStyleComment.expected rename to cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.expected diff --git a/cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.ql b/cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.ql new file mode 100644 index 0000000000..3fd1cf77ba --- /dev/null +++ b/cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.charactersequenceusedwithinacstylecomment.CharacterSequenceUsedWithinACStyleComment + +class TestFileQuery extends CharacterSequenceUsedWithinACStyleCommentSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M2-7-1/test.cpp b/cpp/common/test/rules/charactersequenceusedwithinacstylecomment/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M2-7-1/test.cpp rename to cpp/common/test/rules/charactersequenceusedwithinacstylecomment/test.cpp diff --git a/cpp/common/test/rules/commaoperatorused/CommaOperatorUsed.ql b/cpp/common/test/rules/commaoperatorused/CommaOperatorUsed.ql index 53d559ae8b..2fe294762e 100644 --- a/cpp/common/test/rules/commaoperatorused/CommaOperatorUsed.ql +++ b/cpp/common/test/rules/commaoperatorused/CommaOperatorUsed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.commaoperatorused.CommaOperatorUsed + +class TestFileQuery extends CommaOperatorUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/conditionvariablepostconditionfailed/ConditionVariablePostConditionFailed.ql b/cpp/common/test/rules/conditionvariablepostconditionfailed/ConditionVariablePostConditionFailed.ql index ab0dc41229..e990e23e2e 100644 --- a/cpp/common/test/rules/conditionvariablepostconditionfailed/ConditionVariablePostConditionFailed.ql +++ b/cpp/common/test/rules/conditionvariablepostconditionfailed/ConditionVariablePostConditionFailed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.conditionvariablepostconditionfailed.ConditionVariablePostConditionFailed + +class TestFileQuery extends ConditionVariablePostConditionFailedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.expected b/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.expected new file mode 100644 index 0000000000..9b9718a09a --- /dev/null +++ b/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.expected @@ -0,0 +1,17 @@ +| test.cpp:16:7:16:50 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:17:7:17:18 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:23:7:23:19 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:24:7:24:19 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:30:7:30:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:31:7:31:17 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:35:7:35:39 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:36:7:36:39 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:43:7:43:56 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:44:7:44:20 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:51:7:51:19 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:52:7:52:19 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:58:7:58:18 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:59:7:59:17 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:63:7:63:45 | ... - ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:64:7:64:45 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | +| test.cpp:69:20:69:31 | ... + ... | Use of a constant, unsigned, integer expression that over- or under-flows. | diff --git a/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql b/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql new file mode 100644 index 0000000000..c77ee1c66a --- /dev/null +++ b/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/ConstantUnsignedIntegerExpressionsWrapAround.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.constantunsignedintegerexpressionswraparound.ConstantUnsignedIntegerExpressionsWrapAround + +class TestFileQuery extends ConstantUnsignedIntegerExpressionsWrapAroundSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/test.cpp b/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/test.cpp new file mode 100644 index 0000000000..52e6e1ffa1 --- /dev/null +++ b/cpp/common/test/rules/constantunsignedintegerexpressionswraparound/test.cpp @@ -0,0 +1,71 @@ +#include +#include + +template constexpr T constexpr_min() { + return std::numeric_limits::min(); +} + +template constexpr T constexpr_max() { + return std::numeric_limits::max(); +} + +void test_unsigned_int() { + unsigned int a; + a = 1 + 1; // COMPLIANT + a = 0 - 1; // COMPLIANT + a = std::numeric_limits::min() - 1; // NON_COMPLIANT + a = UINT_MAX + 1; // NON_COMPLIANT + + const unsigned int const_min = std::numeric_limits::min(); + const unsigned int const_max = UINT_MAX; + a = const_min + 1; // COMPLIANT + a = const_max - 1; // COMPLIANT + a = const_min - 1; // NON_COMPLIANT + a = const_max + 1; // NON_COMPLIANT + +#define UNDERFLOW(x) (std::numeric_limits::min() - (x)) +#define OVERFLOW(x) (UINT_MAX + (x)) + a = UNDERFLOW(0); // COMPLIANT + a = OVERFLOW(0); // COMPLIANT + a = UNDERFLOW(1); // NON_COMPLIANT + a = OVERFLOW(1); // NON_COMPLIANT + + a = constexpr_min() + 1; // COMPLIANT + a = constexpr_max() - 1; // COMPLIANT + a = constexpr_min() - 1; // NON_COMPLIANT + a = constexpr_max() + 1; // NON_COMPLIANT +} + +void test_long_long() { + unsigned long long a; + a = 1 + 1; // COMPLIANT + a = 0 - 1; // COMPLIANT + a = std::numeric_limits::min() - 1; // NON_COMPLIANT + a = ULLONG_MAX + 1; // NON_COMPLIANT + + const unsigned long long const_min = + std::numeric_limits::min(); + const unsigned long long const_max = ULLONG_MAX; + a = const_min + 1; // COMPLIANT + a = const_max - 1; // COMPLIANT + a = const_min - 1; // NON_COMPLIANT + a = const_max + 1; // NON_COMPLIANT + +#define UNDERFLOW(x) (std::numeric_limits::min() - (x)) +#define OVERFLOW(x) (ULLONG_MAX + (x)) + a = UNDERFLOW(0); // COMPLIANT + a = OVERFLOW(0); // COMPLIANT + a = UNDERFLOW(1); // NON_COMPLIANT + a = OVERFLOW(1); // NON_COMPLIANT + + a = constexpr_min() + 1; // COMPLIANT + a = constexpr_max() - 1; // COMPLIANT + a = constexpr_min() - 1; // NON_COMPLIANT + a = constexpr_max() + 1; // NON_COMPLIANT +} + +void test_conversion() { + signed int a = + (signed int)(UINT_MAX + 1); // NON_COMPLIANT - still an unsigned integer + // constant expression +} \ No newline at end of file diff --git a/cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected b/cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected new file mode 100644 index 0000000000..2caa0d197c --- /dev/null +++ b/cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.expected @@ -0,0 +1,20 @@ +problems +| test.cpp:11:8:11:12 | c_str | test.cpp:18:16:18:21 | call to getenv | test.cpp:11:8:11:12 | c_str | The object returned by the function getenv should not be modified. | +| test.cpp:67:5:67:9 | conv4 | test.cpp:64:11:64:20 | call to localeconv | test.cpp:67:5:67:9 | conv4 | The object returned by the function localeconv should not be modified. | +| test.cpp:76:5:76:8 | conv | test.cpp:72:25:72:34 | call to localeconv | test.cpp:76:5:76:8 | conv | The object returned by the function localeconv should not be modified. | +edges +| test.cpp:8:18:8:22 | c_str | test.cpp:11:8:11:12 | c_str | provenance | | +| test.cpp:18:16:18:21 | call to getenv | test.cpp:24:9:24:12 | env1 | provenance | | +| test.cpp:24:9:24:12 | env1 | test.cpp:8:18:8:22 | c_str | provenance | | +| test.cpp:64:11:64:20 | call to localeconv | test.cpp:67:5:67:9 | conv4 | provenance | | +| test.cpp:72:25:72:34 | call to localeconv | test.cpp:76:5:76:8 | conv | provenance | | +nodes +| test.cpp:8:18:8:22 | c_str | semmle.label | c_str | +| test.cpp:11:8:11:12 | c_str | semmle.label | c_str | +| test.cpp:18:16:18:21 | call to getenv | semmle.label | call to getenv | +| test.cpp:24:9:24:12 | env1 | semmle.label | env1 | +| test.cpp:64:11:64:20 | call to localeconv | semmle.label | call to localeconv | +| test.cpp:67:5:67:9 | conv4 | semmle.label | conv4 | +| test.cpp:72:25:72:34 | call to localeconv | semmle.label | call to localeconv | +| test.cpp:76:5:76:8 | conv | semmle.label | conv | +subpaths diff --git a/cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql b/cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql new file mode 100644 index 0000000000..53c27eb3ce --- /dev/null +++ b/cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.constlikereturnvalue.ConstLikeReturnValue + +class TestFileQuery extends ConstLikeReturnValueSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/constlikereturnvalue/test.cpp b/cpp/common/test/rules/constlikereturnvalue/test.cpp new file mode 100644 index 0000000000..19db17faee --- /dev/null +++ b/cpp/common/test/rules/constlikereturnvalue/test.cpp @@ -0,0 +1,96 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +#include +#include +#include + +void trstr(char *c_str, char orig, char rep) { + while (*c_str != '\0') { + if (*c_str == orig) { + *c_str = rep; // NON_COMPLIANT + } + ++c_str; + } +} + +void f1(void) { + char *env1 = getenv("TEST_ENV"); + char *copy_of_env; + copy_of_env = env1; // COMPLIANT + + if (env1 == NULL) { + } + trstr(env1, '"', '_'); +} + +void f2(void) { + const char *env2; + char *copy_of_env; + + env2 = getenv("TEST_ENV"); + if (env2 == NULL) { + } + + copy_of_env = (char *)malloc(strlen(env2) + 1); + if (copy_of_env == NULL) { + } + + strcpy(copy_of_env, env2); + trstr(copy_of_env, '"', '_'); // COMPLIANT +} + +void f3(void) { + const char *env3; + char *copy_of_env; + + env3 = getenv("TEST_ENV"); + if (env3 == NULL) { + } + + copy_of_env = strdup(env3); + if (copy_of_env == NULL) { + } + + trstr(copy_of_env, '"', '_'); // COMPLIANT + if (setenv("TEST_ENV", copy_of_env, 1) != 0) { + } +} + +void f4(void) { + struct lconv *conv4 = localeconv(); + + setlocale(LC_ALL, "C"); // COMPLIANT + conv4 = localeconv(); // COMPLIANT + + if ('\0' == conv4->decimal_point[0]) { + conv4->decimal_point = "."; // NON_COMPLIANT + } +} + +void f4alias(void) { + struct lconv *conv4 = localeconv(); + struct lconv *conv = conv4; + + if ('\0' == conv4->decimal_point[0]) { + conv->decimal_point = "."; // NON_COMPLIANT + } +} + +void f5(void) { + const struct lconv *conv5 = localeconv(); + if (conv5 == NULL) { + } + + struct lconv *copy_of_conv = (struct lconv *)malloc(sizeof(struct lconv)); + if (copy_of_conv == NULL) { + } + + memcpy(copy_of_conv, conv5, sizeof(struct lconv)); + + if ('\0' == copy_of_conv->decimal_point[0]) { + copy_of_conv->decimal_point = "."; // COMPLIANT + } + + free(copy_of_conv); +} \ No newline at end of file diff --git a/cpp/common/test/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.ql b/cpp/common/test/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.ql index cf073f961c..be54f5a31c 100644 --- a/cpp/common/test/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.ql +++ b/cpp/common/test/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.containeraccesswithoutrangecheck.ContainerAccessWithoutRangeCheck + +class TestFileQuery extends ContainerAccessWithoutRangeCheckSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.expected b/cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.expected similarity index 100% rename from cpp/autosar/test/rules/A12-8-5/CopyAssignmentAndAMoveHandleSelfAssignment.expected rename to cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.expected diff --git a/cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql b/cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql new file mode 100644 index 0000000000..9e84431f65 --- /dev/null +++ b/cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.copyandmoveassignmentsshallhandleselfassignment.CopyAndMoveAssignmentsShallHandleSelfAssignment + +class TestFileQuery extends CopyAndMoveAssignmentsShallHandleSelfAssignmentSharedQuery, TestQuery { +} diff --git a/cpp/autosar/test/rules/A12-8-5/test.cpp b/cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A12-8-5/test.cpp rename to cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/test.cpp diff --git a/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.expected b/cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.expected similarity index 89% rename from cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.expected rename to cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.expected index 8806bacfe7..2cc8431463 100644 --- a/cpp/autosar/test/rules/M18-7-1/CsignalFunctionsUsed.expected +++ b/cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.expected @@ -1,4 +1,4 @@ -| test.cpp:7:3:7:13 | call to signal | Use of function 'std::signal'. | -| test.cpp:8:3:8:12 | call to raise | Use of function 'std::raise'. | +| test.cpp:7:3:7:13 | call to signal | Use of function 'signal'. | +| test.cpp:8:3:8:12 | call to raise | Use of function 'raise'. | | test.cpp:11:3:11:8 | call to signal | Use of function 'signal'. | | test.cpp:12:3:12:7 | call to raise | Use of function 'raise'. | diff --git a/cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.ql b/cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.ql new file mode 100644 index 0000000000..1d39069ae7 --- /dev/null +++ b/cpp/common/test/rules/csignalfunctionsused/CsignalFunctionsUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.csignalfunctionsused.CsignalFunctionsUsed + +class TestFileQuery extends CsignalFunctionsUsedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M18-7-1/test.cpp b/cpp/common/test/rules/csignalfunctionsused/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M18-7-1/test.cpp rename to cpp/common/test/rules/csignalfunctionsused/test.cpp diff --git a/cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.expected b/cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.expected new file mode 100644 index 0000000000..0ec502c3ac --- /dev/null +++ b/cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.expected @@ -0,0 +1,2 @@ +| test.cpp:6:8:6:19 | type mention | Use of type 'sig_atomic_t'. | +| test.cpp:10:3:10:14 | type mention | Use of type 'sig_atomic_t'. | diff --git a/cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.ql b/cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.ql new file mode 100644 index 0000000000..76cc8aad04 --- /dev/null +++ b/cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.csignaltypesused.CsignalTypesUsed + +class TestFileQuery extends CsignalTypesUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/csignaltypesused/test.cpp b/cpp/common/test/rules/csignaltypesused/test.cpp new file mode 100644 index 0000000000..e621160b81 --- /dev/null +++ b/cpp/common/test/rules/csignaltypesused/test.cpp @@ -0,0 +1,13 @@ +#include + +void signal_handler(int signal) {} + +void test_signal_is_used() { + std::sig_atomic_t atom; // NON_COMPLIANT + std::signal(SIGINT, signal_handler); // NON_COMPLIANT + std::raise(SIGINT); // NON_COMPLIANT + + sig_atomic_t atom1; // NON_COMPLIANT + signal(SIGINT, signal_handler); // NON_COMPLIANT + raise(SIGINT); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.expected b/cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.expected similarity index 88% rename from cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.expected rename to cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.expected index 241d2281ef..54f341887a 100644 --- a/cpp/autosar/test/rules/M27-0-1/CstdioFunctionsUsed.expected +++ b/cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.expected @@ -1,20 +1,20 @@ -| test.cpp:4:18:4:27 | call to fopen | Use of function 'std::fopen'. | -| test.cpp:7:3:7:14 | call to fgetpos | Use of function 'std::fgetpos'. | -| test.cpp:9:11:9:19 | call to feof | Use of function 'std::feof'. | -| test.cpp:10:14:10:23 | call to fgetc | Use of function 'std::fgetc'. | -| test.cpp:12:7:12:17 | call to rewind | Use of function 'std::rewind'. | -| test.cpp:14:7:14:17 | call to ferror | Use of function 'std::ferror'. | -| test.cpp:15:5:15:17 | call to clearerr | Use of function 'std::clearerr'. | -| test.cpp:16:5:16:15 | call to fclose | Use of function 'std::fclose'. | -| test.cpp:17:5:17:15 | call to perror | Use of function 'std::perror'. | -| test.cpp:20:3:20:12 | call to fseek | Use of function 'std::fseek'. | -| test.cpp:21:3:21:12 | call to fseek | Use of function 'std::fseek'. | -| test.cpp:23:3:23:12 | call to fread | Use of function 'std::fread'. | -| test.cpp:25:3:25:14 | call to fsetpos | Use of function 'std::fsetpos'. | -| test.cpp:26:3:26:13 | call to fflush | Use of function 'std::fflush'. | -| test.cpp:27:3:27:13 | call to fclose | Use of function 'std::fclose'. | -| test.cpp:29:3:29:13 | call to printf | Use of function 'std::printf'. | -| test.cpp:31:3:31:11 | call to puts | Use of function 'std::puts'. | +| test.cpp:4:18:4:27 | call to fopen | Use of function 'fopen'. | +| test.cpp:7:3:7:14 | call to fgetpos | Use of function 'fgetpos'. | +| test.cpp:9:11:9:19 | call to feof | Use of function 'feof'. | +| test.cpp:10:14:10:23 | call to fgetc | Use of function 'fgetc'. | +| test.cpp:12:7:12:17 | call to rewind | Use of function 'rewind'. | +| test.cpp:14:7:14:17 | call to ferror | Use of function 'ferror'. | +| test.cpp:15:5:15:17 | call to clearerr | Use of function 'clearerr'. | +| test.cpp:16:5:16:15 | call to fclose | Use of function 'fclose'. | +| test.cpp:17:5:17:15 | call to perror | Use of function 'perror'. | +| test.cpp:20:3:20:12 | call to fseek | Use of function 'fseek'. | +| test.cpp:21:3:21:12 | call to fseek | Use of function 'fseek'. | +| test.cpp:23:3:23:12 | call to fread | Use of function 'fread'. | +| test.cpp:25:3:25:14 | call to fsetpos | Use of function 'fsetpos'. | +| test.cpp:26:3:26:13 | call to fflush | Use of function 'fflush'. | +| test.cpp:27:3:27:13 | call to fclose | Use of function 'fclose'. | +| test.cpp:29:3:29:13 | call to printf | Use of function 'printf'. | +| test.cpp:31:3:31:11 | call to puts | Use of function 'puts'. | | test.cpp:34:14:34:18 | call to fopen | Use of function 'fopen'. | | test.cpp:37:3:37:9 | call to fgetpos | Use of function 'fgetpos'. | | test.cpp:39:11:39:14 | call to feof | Use of function 'feof'. | diff --git a/cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.ql b/cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.ql new file mode 100644 index 0000000000..16dbb974b6 --- /dev/null +++ b/cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.cstdiofunctionsused.CstdioFunctionsUsed + +class TestFileQuery extends CstdioFunctionsUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/cstdiofunctionsused/test.cpp b/cpp/common/test/rules/cstdiofunctionsused/test.cpp new file mode 100644 index 0000000000..27447ba06a --- /dev/null +++ b/cpp/common/test/rules/cstdiofunctionsused/test.cpp @@ -0,0 +1,61 @@ +#include +#include +void *test_cstdio_is_used() { + std::FILE *f = std::fopen("foo.txt", "r"); // NON_COMPLIANT + + std::fpos_t init_position; // NON_COMPLIANT + std::fgetpos(f, &init_position); // NON_COMPLIANT + + while (!std::feof(f)) { // NON_COMPLIANT + char c = std::fgetc(f); // NON_COMPLIANT + if (c == EOF) // NON_COMPLIANT + std::rewind(f); // NON_COMPLIANT + } + if (std::ferror(f)) { // NON_COMPLIANT + std::clearerr(f); // NON_COMPLIANT + std::fclose(f); // NON_COMPLIANT + std::perror("fgetc"); // NON_COMPLIANT + } + + std::fseek(f, (size_t)0, SEEK_SET); // NON_COMPLIANT + std::fseek(f, (size_t)0, SEEK_END); // NON_COMPLIANT + char buf[BUFSIZ]; // NON_COMPLIANT + std::fread(buf, 1, sizeof(buf), f); // NON_COMPLIANT + + std::fsetpos(f, &init_position); // NON_COMPLIANT + std::fflush(f); // NON_COMPLIANT + std::fclose(f); // NON_COMPLIANT + + std::printf("DEBUG: TMP_MAX=%d FILENAME_MAX=%d FOPEN_MAX=%d\n", TMP_MAX, + FILENAME_MAX, FOPEN_MAX); // NON_COMPLIANT + std::puts("all done!"); // NON_COMPLIANT + + // global namespace + FILE *f1 = fopen("foo.txt", "r"); // NON_COMPLIANT + + fpos_t init_position1; + fgetpos(f1, &init_position1); // NON_COMPLIANT + + while (!feof(f1)) { // NON_COMPLIANT + char c = fgetc(f1); // NON_COMPLIANT + if (c == EOF) // NON_COMPLIANT + rewind(f1); // NON_COMPLIANT + } + if (ferror(f1)) { // NON_COMPLIANT + clearerr(f1); // NON_COMPLIANT + fclose(f1); // NON_COMPLIANT + perror("fgetc"); // NON_COMPLIANT + } + + fseek(f1, (size_t)0, SEEK_SET); // NON_COMPLIANT + fread(buf, 1, sizeof(buf), f1); // NON_COMPLIANT + + fsetpos(f1, &init_position1); // NON_COMPLIANT + fflush(f1); // NON_COMPLIANT + fclose(f1); // NON_COMPLIANT + + printf("foo"); // NON_COMPLIANT + puts("all done!"); // NON_COMPLIANT + + return NULL; // COMPLIANT - NULL is not uniquely defined by cstdio +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.expected b/cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.expected similarity index 90% rename from cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.expected rename to cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.expected index 8a48b86902..af67e45903 100644 --- a/cpp/autosar/test/rules/M27-0-1/CstdioMacrosUsed.expected +++ b/cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.expected @@ -7,4 +7,3 @@ | test.cpp:30:29:30:37 | FOPEN_MAX | Use of macro 'FOPEN_MAX'. | | test.cpp:41:14:41:16 | EOF | Use of macro 'EOF'. | | test.cpp:50:24:50:31 | SEEK_SET | Use of macro 'SEEK_SET'. | -| test.cpp:60:10:60:13 | NULL | Use of macro 'NULL'. | diff --git a/cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.ql b/cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.ql new file mode 100644 index 0000000000..79ab6086b1 --- /dev/null +++ b/cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.cstdiomacrosused.CstdioMacrosUsed + +class TestFileQuery extends CstdioMacrosUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/cstdiomacrosused/test.cpp b/cpp/common/test/rules/cstdiomacrosused/test.cpp new file mode 100644 index 0000000000..27447ba06a --- /dev/null +++ b/cpp/common/test/rules/cstdiomacrosused/test.cpp @@ -0,0 +1,61 @@ +#include +#include +void *test_cstdio_is_used() { + std::FILE *f = std::fopen("foo.txt", "r"); // NON_COMPLIANT + + std::fpos_t init_position; // NON_COMPLIANT + std::fgetpos(f, &init_position); // NON_COMPLIANT + + while (!std::feof(f)) { // NON_COMPLIANT + char c = std::fgetc(f); // NON_COMPLIANT + if (c == EOF) // NON_COMPLIANT + std::rewind(f); // NON_COMPLIANT + } + if (std::ferror(f)) { // NON_COMPLIANT + std::clearerr(f); // NON_COMPLIANT + std::fclose(f); // NON_COMPLIANT + std::perror("fgetc"); // NON_COMPLIANT + } + + std::fseek(f, (size_t)0, SEEK_SET); // NON_COMPLIANT + std::fseek(f, (size_t)0, SEEK_END); // NON_COMPLIANT + char buf[BUFSIZ]; // NON_COMPLIANT + std::fread(buf, 1, sizeof(buf), f); // NON_COMPLIANT + + std::fsetpos(f, &init_position); // NON_COMPLIANT + std::fflush(f); // NON_COMPLIANT + std::fclose(f); // NON_COMPLIANT + + std::printf("DEBUG: TMP_MAX=%d FILENAME_MAX=%d FOPEN_MAX=%d\n", TMP_MAX, + FILENAME_MAX, FOPEN_MAX); // NON_COMPLIANT + std::puts("all done!"); // NON_COMPLIANT + + // global namespace + FILE *f1 = fopen("foo.txt", "r"); // NON_COMPLIANT + + fpos_t init_position1; + fgetpos(f1, &init_position1); // NON_COMPLIANT + + while (!feof(f1)) { // NON_COMPLIANT + char c = fgetc(f1); // NON_COMPLIANT + if (c == EOF) // NON_COMPLIANT + rewind(f1); // NON_COMPLIANT + } + if (ferror(f1)) { // NON_COMPLIANT + clearerr(f1); // NON_COMPLIANT + fclose(f1); // NON_COMPLIANT + perror("fgetc"); // NON_COMPLIANT + } + + fseek(f1, (size_t)0, SEEK_SET); // NON_COMPLIANT + fread(buf, 1, sizeof(buf), f1); // NON_COMPLIANT + + fsetpos(f1, &init_position1); // NON_COMPLIANT + fflush(f1); // NON_COMPLIANT + fclose(f1); // NON_COMPLIANT + + printf("foo"); // NON_COMPLIANT + puts("all done!"); // NON_COMPLIANT + + return NULL; // COMPLIANT - NULL is not uniquely defined by cstdio +} \ No newline at end of file diff --git a/cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.expected b/cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.expected new file mode 100644 index 0000000000..ed5a448ace --- /dev/null +++ b/cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.expected @@ -0,0 +1,4 @@ +| test.cpp:4:8:4:11 | type mention | Use of type 'FILE'. | +| test.cpp:6:8:6:13 | type mention | Use of type 'fpos_t'. | +| test.cpp:34:3:34:6 | type mention | Use of type 'FILE'. | +| test.cpp:36:3:36:8 | type mention | Use of type 'fpos_t'. | diff --git a/cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.ql b/cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.ql new file mode 100644 index 0000000000..c5bac15c65 --- /dev/null +++ b/cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.cstdiotypesused.CstdioTypesUsed + +class TestFileQuery extends CstdioTypesUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/cstdiotypesused/test.cpp b/cpp/common/test/rules/cstdiotypesused/test.cpp new file mode 100644 index 0000000000..27447ba06a --- /dev/null +++ b/cpp/common/test/rules/cstdiotypesused/test.cpp @@ -0,0 +1,61 @@ +#include +#include +void *test_cstdio_is_used() { + std::FILE *f = std::fopen("foo.txt", "r"); // NON_COMPLIANT + + std::fpos_t init_position; // NON_COMPLIANT + std::fgetpos(f, &init_position); // NON_COMPLIANT + + while (!std::feof(f)) { // NON_COMPLIANT + char c = std::fgetc(f); // NON_COMPLIANT + if (c == EOF) // NON_COMPLIANT + std::rewind(f); // NON_COMPLIANT + } + if (std::ferror(f)) { // NON_COMPLIANT + std::clearerr(f); // NON_COMPLIANT + std::fclose(f); // NON_COMPLIANT + std::perror("fgetc"); // NON_COMPLIANT + } + + std::fseek(f, (size_t)0, SEEK_SET); // NON_COMPLIANT + std::fseek(f, (size_t)0, SEEK_END); // NON_COMPLIANT + char buf[BUFSIZ]; // NON_COMPLIANT + std::fread(buf, 1, sizeof(buf), f); // NON_COMPLIANT + + std::fsetpos(f, &init_position); // NON_COMPLIANT + std::fflush(f); // NON_COMPLIANT + std::fclose(f); // NON_COMPLIANT + + std::printf("DEBUG: TMP_MAX=%d FILENAME_MAX=%d FOPEN_MAX=%d\n", TMP_MAX, + FILENAME_MAX, FOPEN_MAX); // NON_COMPLIANT + std::puts("all done!"); // NON_COMPLIANT + + // global namespace + FILE *f1 = fopen("foo.txt", "r"); // NON_COMPLIANT + + fpos_t init_position1; + fgetpos(f1, &init_position1); // NON_COMPLIANT + + while (!feof(f1)) { // NON_COMPLIANT + char c = fgetc(f1); // NON_COMPLIANT + if (c == EOF) // NON_COMPLIANT + rewind(f1); // NON_COMPLIANT + } + if (ferror(f1)) { // NON_COMPLIANT + clearerr(f1); // NON_COMPLIANT + fclose(f1); // NON_COMPLIANT + perror("fgetc"); // NON_COMPLIANT + } + + fseek(f1, (size_t)0, SEEK_SET); // NON_COMPLIANT + fread(buf, 1, sizeof(buf), f1); // NON_COMPLIANT + + fsetpos(f1, &init_position1); // NON_COMPLIANT + fflush(f1); // NON_COMPLIANT + fclose(f1); // NON_COMPLIANT + + printf("foo"); // NON_COMPLIANT + puts("all done!"); // NON_COMPLIANT + + return NULL; // COMPLIANT - NULL is not uniquely defined by cstdio +} \ No newline at end of file diff --git a/cpp/common/test/rules/danglingcapturewhenmovinglambdaobject/DanglingCaptureWhenMovingLambdaObject.ql b/cpp/common/test/rules/danglingcapturewhenmovinglambdaobject/DanglingCaptureWhenMovingLambdaObject.ql index 734de290a5..ba1f1efc2f 100644 --- a/cpp/common/test/rules/danglingcapturewhenmovinglambdaobject/DanglingCaptureWhenMovingLambdaObject.ql +++ b/cpp/common/test/rules/danglingcapturewhenmovinglambdaobject/DanglingCaptureWhenMovingLambdaObject.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.danglingcapturewhenmovinglambdaobject.DanglingCaptureWhenMovingLambdaObject + +class TestFileQuery extends DanglingCaptureWhenMovingLambdaObjectSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/danglingcapturewhenreturninglambdaobject/DanglingCaptureWhenReturningLambdaObject.ql b/cpp/common/test/rules/danglingcapturewhenreturninglambdaobject/DanglingCaptureWhenReturningLambdaObject.ql index 870784d97d..d95ba912fd 100644 --- a/cpp/common/test/rules/danglingcapturewhenreturninglambdaobject/DanglingCaptureWhenReturningLambdaObject.ql +++ b/cpp/common/test/rules/danglingcapturewhenreturninglambdaobject/DanglingCaptureWhenReturningLambdaObject.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.danglingcapturewhenreturninglambdaobject.DanglingCaptureWhenReturningLambdaObject + +class TestFileQuery extends DanglingCaptureWhenReturningLambdaObjectSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/deadcode/DeadCode.expected b/cpp/common/test/rules/deadcode/DeadCode.expected new file mode 100644 index 0000000000..1756231343 --- /dev/null +++ b/cpp/common/test/rules/deadcode/DeadCode.expected @@ -0,0 +1,21 @@ +| test.cpp:17:3:17:27 | declaration | This statement is dead code. | +| test.cpp:18:3:18:12 | ExprStmt | This statement is dead code. | +| test.cpp:19:3:19:12 | ExprStmt | This statement is dead code. | +| test.cpp:21:3:23:3 | if (...) ... | This statement is dead code. | +| test.cpp:33:3:34:3 | if (...) ... | This statement is dead code. | +| test.cpp:36:3:36:4 | { ... } | This statement is dead code. | +| test.cpp:37:3:39:3 | { ... } | This statement is dead code. | +| test.cpp:53:6:54:3 | { ... } | This statement is dead code. | +| test.cpp:64:46:65:3 | { ... } | This statement is dead code. | +| test.cpp:68:3:68:8 | ExprStmt | This statement is dead code. | +| test.cpp:70:3:70:21 | ExprStmt | This statement is dead code. | +| test.cpp:72:3:73:3 | try { ... } | This statement is dead code. | +| test.cpp:73:17:74:3 | { ... } | This statement is dead code. | +| test.cpp:79:17:80:3 | { ... } | This statement is dead code. | +| test.cpp:85:3:85:43 | declaration | This statement is dead code. | +| test.cpp:87:3:87:30 | declaration | This statement is dead code. | +| test.cpp:90:3:90:50 | declaration | This statement is dead code. | +| test.cpp:116:3:116:21 | ExprStmt | This statement is dead code. | +| test.cpp:117:3:117:27 | ExprStmt | This statement is dead code. | +| test.cpp:118:7:118:32 | ExprStmt | This statement is dead code. | +| test.cpp:139:3:139:35 | ExprStmt | This statement is dead code. | diff --git a/cpp/common/test/rules/deadcode/DeadCode.ql b/cpp/common/test/rules/deadcode/DeadCode.ql new file mode 100644 index 0000000000..dcd7fce840 --- /dev/null +++ b/cpp/common/test/rules/deadcode/DeadCode.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.deadcode.DeadCode + +class TestFileQuery extends DeadCodeSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/deadcode/test.cpp b/cpp/common/test/rules/deadcode/test.cpp new file mode 100644 index 0000000000..d40667539d --- /dev/null +++ b/cpp/common/test/rules/deadcode/test.cpp @@ -0,0 +1,141 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND CHANGES +// SHOULD BE REFLECTED THERE AS WELL. + +int may_have_side_effects(); +int no_side_effects(int x) { return 1 + 2; } +int no_side_effects_nondeterministic(); + +int test_dead_code(int x) { + int live1 = may_have_side_effects(), + live2 = may_have_side_effects(); // COMPLIANT + int live3 = 0, + live4 = may_have_side_effects(); // COMPLIANT + int live5 = 0, live6 = 0; // COMPLIANT + live5 = 1; // COMPLIANT + live6 = 2; // COMPLIANT + + int dead1 = 0, dead2 = 0; // NON_COMPLIANT + dead1 = 1; // NON_COMPLIANT - useless assignment + dead2 = 1; // NON_COMPLIANT - useless assignment + + if (false) { // NON_COMPLIANT + dead2 = 10; // Only used in dead or unreachable code + } + + if (true) { // COMPLIANT + may_have_side_effects(); + } + + if (may_have_side_effects()) { // COMPLIANT + may_have_side_effects(); + } + + if (true) { // NON_COMPLIANT + } + + {} // NON_COMPLIANT + { // NON_COMPLIANT + 1 + 2; + } + + { // COMPLIANT + may_have_side_effects(); + } + + do { // COMPLIANT + may_have_side_effects(); + } while (may_have_side_effects()); + + do { // COMPLIANT + may_have_side_effects(); + } while (may_have_side_effects()); + + do { // NON_COMPLIANT + } while (no_side_effects_nondeterministic()); + + while (may_have_side_effects()) { // COMPLIANT + may_have_side_effects(); + } + + while (may_have_side_effects()) { // COMPLIANT + may_have_side_effects(); + } + + while (no_side_effects_nondeterministic()) { // NON_COMPLIANT + } + + may_have_side_effects(); // COMPLIANT + 1 + 2; // NON_COMPLIANT + + no_side_effects(x); // NON_COMPLIANT + + try { // NON_COMPLIANT + } catch (...) { // NON_COMPLIANT + } + + try { + may_have_side_effects(); + } catch (int i) { // COMPLIANT + } catch (...) { // NON_COMPLIANT + } + + static_assert(1); // COMPLIANT + + constexpr int constexpr_array_size{6}; // COMPLIANT + int unused_array[constexpr_array_size]{}; // NON_COMPLIANT + + constexpr int unused_int{2}; // NON_COMPLIANT + + constexpr int constexpr_used_array[]{3, 4, 5}; // COMPLIANT + constexpr int constexpr_unused_array[]{0, 1, 2}; // NON_COMPLIANT + + return live5 + live6 + constexpr_used_array[1]; // COMPLIANT +} + +class Foo { +public: + void bar() { may_have_side_effects(); } +}; + +class Baz { +public: + void bar() {} // No side effects +}; + +#define FULL_STMT_NO_SIDE_EFFECTS no_side_effects(1); +#define PART_STMT_NO_SIDE_EFFECTS no_side_effects(1) +#define BLOCK_SOME_SIDE_EFFECTS \ + { \ + may_have_side_effects(); \ + no_side_effects(1); \ + } + +template void test_template() { + T t; + t.bar(); // COMPLIANT + no_side_effects(1); // NON_COMPLIANT + FULL_STMT_NO_SIDE_EFFECTS // NON_COMPLIANT + PART_STMT_NO_SIDE_EFFECTS; // NON_COMPLIANT + BLOCK_SOME_SIDE_EFFECTS; // COMPLIANT - cannot determine loc for + // no_side_effects(1) +} + +template void test_variant_side_effects() { + T t; + t.bar(); // COMPLIANT - not dead in at least one instance +} + +template void test_unused_template() { + T t; + t.bar(); // COMPLIANT + no_side_effects( + 1); // NON_COMPLIANT[FALSE_NEGATIVE] - unused templates are not extracted +} + +void test() { + test_template(); + test_template(); + test_variant_side_effects(); // COMPLIANT + test_variant_side_effects(); // NON_COMPLIANT - no effect in this + // instantiation +} \ No newline at end of file diff --git a/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.expected b/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.expected new file mode 100644 index 0000000000..1a1e0e5297 --- /dev/null +++ b/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.expected @@ -0,0 +1 @@ +| test.cpp:46:6:46:7 | declaration of f1 | Definition for 'f1' is not available for unqualified lookup because it is declared after $@ | test.cpp:43:12:43:13 | using f1 | using-declaration | diff --git a/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.ql b/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.ql new file mode 100644 index 0000000000..05457c997c --- /dev/null +++ b/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.definitionnotconsideredforunqualifiedlookup.DefinitionNotConsideredForUnqualifiedLookup + +class TestFileQuery extends DefinitionNotConsideredForUnqualifiedLookupSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/test.cpp b/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/test.cpp new file mode 100644 index 0000000000..c0904238c3 --- /dev/null +++ b/cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/test.cpp @@ -0,0 +1,94 @@ +struct S1 { + int i; +}; + +class C1 { +public: + void f1(int); + + virtual void f2(int); + virtual void f2(double); + virtual void f2(S1); + +private: + void f3(int); + void f4(int); +}; + +class C2 : public C1 { +public: + void f1(double); // NON_COMPLIANT + + void f2(double) override; // NON_COMPLIANT +}; + +class C3 : public C1 { +public: + void f2(char *); // NON_COMPLIANT +}; + +class C4 : public C1 { +public: + using C1::f1; + void f1(double); // COMPLIANT + + using C1::f2; + void f2(double) override; // COMPLIANT +}; + +namespace ns1 { +void f1(int); +} + +using ns1::f1; + +namespace ns1 { +void f1(double); // NON_COMPLIANT +} + +void f1() { + C2 l1; + l1.f1(0); // calls C2::f1(double) instead of C1::f1(int) + l1.f2(0); // calls C2::f2(double) instead of C1::f2(int) + // S1 s1; + // l1.f2(s1); Won't compile because there is no suitable conversion from S1 to + // double. + C1 &l2{l1}; + l2.f1(0); // calls C1::f1(int) + + C4 l3; + l3.f1(0); // calls C1::f1(int) + l3.f1(0.0); // calls C3::f1(double) + l3.f2(0); // calls C1::f2(int) + l3.f2(0.0); // calls C3::f2(double) + S1 l4; + l3.f2(l4); // calls C1:f2(S1) +} + +class C5 : public C1 { +public: + void f1(double); // COMPLIANT + using C1::f1; // order of using and f1 declaration is not relevant + + void f2(double) override; // COMPLIANT + using C1::f2; // order of using and f2 declaration is not relevant +}; + +void f2() { + C5 c5; + c5.f1(0); // calls C1::f1(int) + c5.f1(0.0); // calls C5::f1(double) + c5.f2(0); // calls C1::f2(int) + c5.f2(0.0); // calls C5::f2(double) +} + +class C6 : public C1 { +public: + C6 &operator=(const C6 &); // COMPLIANT +}; + +class C7 : public C1 { + void f3(int); // COMPLIANT + + void f4(int); // COMPLIANT +}; \ No newline at end of file diff --git a/cpp/common/test/rules/deleteofpointertoincompleteclass/DeleteOfPointerToIncompleteClass.ql b/cpp/common/test/rules/deleteofpointertoincompleteclass/DeleteOfPointerToIncompleteClass.ql index b6a13aa7cc..a589ae988e 100644 --- a/cpp/common/test/rules/deleteofpointertoincompleteclass/DeleteOfPointerToIncompleteClass.ql +++ b/cpp/common/test/rules/deleteofpointertoincompleteclass/DeleteOfPointerToIncompleteClass.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.deleteofpointertoincompleteclass.DeleteOfPointerToIncompleteClass + +class TestFileQuery extends DeleteOfPointerToIncompleteClassSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/deleteofpointertoincompleteclass/test.cpp b/cpp/common/test/rules/deleteofpointertoincompleteclass/test.cpp index 2a5f002c67..c659a87740 100644 --- a/cpp/common/test/rules/deleteofpointertoincompleteclass/test.cpp +++ b/cpp/common/test/rules/deleteofpointertoincompleteclass/test.cpp @@ -1,7 +1,7 @@ class A { class B *impl; - void test() { delete impl; } // NON_COMPLAINT + void test() { delete impl; } // NON_COMPLIANT }; class D {}; @@ -9,5 +9,5 @@ class D {}; class C { class D *impl1; - void test() { delete impl1; } // COMPLAINT + void test() { delete impl1; } // COMPLIANT }; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.expected b/cpp/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.expected similarity index 100% rename from cpp/autosar/test/rules/A5-3-2/NullPointersDereferenced.expected rename to cpp/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.expected diff --git a/cpp/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql b/cpp/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql new file mode 100644 index 0000000000..c8dc62e67c --- /dev/null +++ b/cpp/common/test/rules/dereferenceofnullpointer/DereferenceOfNullPointer.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.dereferenceofnullpointer.DereferenceOfNullPointer + +class TestFileQuery extends DereferenceOfNullPointerSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A5-3-2/test.cpp b/cpp/common/test/rules/dereferenceofnullpointer/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A5-3-2/test.cpp rename to cpp/common/test/rules/dereferenceofnullpointer/test.cpp diff --git a/cpp/common/test/rules/destroyedvaluereferencedindestructorcatchblock/DestroyedValueReferencedInDestructorCatchBlock.ql b/cpp/common/test/rules/destroyedvaluereferencedindestructorcatchblock/DestroyedValueReferencedInDestructorCatchBlock.ql index 4f02daf0c5..90c4ed602a 100644 --- a/cpp/common/test/rules/destroyedvaluereferencedindestructorcatchblock/DestroyedValueReferencedInDestructorCatchBlock.ql +++ b/cpp/common/test/rules/destroyedvaluereferencedindestructorcatchblock/DestroyedValueReferencedInDestructorCatchBlock.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.destroyedvaluereferencedindestructorcatchblock.DestroyedValueReferencedInDestructorCatchBlock + +class TestFileQuery extends DestroyedValueReferencedInDestructorCatchBlockSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected b/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected index 85fc9c161a..ba9963f735 100644 --- a/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected +++ b/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.expected @@ -1,31 +1,31 @@ -| test.cpp:2:5:2:13 | case1_FOO | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:2:5:2:13 | case1_FOO | case1_FOO | test.cpp:1:5:1:13 | case1_foo | case1_foo | -| test.cpp:2:5:2:13 | case1_FOO | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:2:5:2:13 | case1_FOO | case1_FOO | test.cpp:3:5:3:13 | case1_fOo | case1_fOo | -| test.cpp:3:5:3:13 | case1_fOo | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:3:5:3:13 | case1_fOo | case1_fOo | test.cpp:1:5:1:13 | case1_foo | case1_foo | -| test.cpp:6:5:6:15 | case2_f_o_o | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:6:5:6:15 | case2_f_o_o | case2_f_o_o | test.cpp:5:5:5:13 | case2_foo | case2_foo | -| test.cpp:9:5:9:13 | case3_fO0 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:9:5:9:13 | case3_fO0 | case3_fO0 | test.cpp:8:5:8:13 | case3_fOO | case3_fOO | -| test.cpp:11:5:11:12 | case4_II | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:11:5:11:12 | case4_II | case4_II | test.cpp:13:5:13:12 | case4_Il | case4_Il | -| test.cpp:12:5:12:12 | case4_I1 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:12:5:12:12 | case4_I1 | case4_I1 | test.cpp:11:5:11:12 | case4_II | case4_II | -| test.cpp:12:5:12:12 | case4_I1 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:12:5:12:12 | case4_I1 | case4_I1 | test.cpp:13:5:13:12 | case4_Il | case4_Il | -| test.cpp:16:5:16:11 | case5_5 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:16:5:16:11 | case5_5 | case5_5 | test.cpp:15:5:15:11 | case5_S | case5_S | -| test.cpp:19:5:19:11 | case6_2 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:19:5:19:11 | case6_2 | case6_2 | test.cpp:18:5:18:11 | case6_Z | case6_Z | -| test.cpp:22:5:22:11 | case7_h | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:22:5:22:11 | case7_h | case7_h | test.cpp:21:5:21:11 | case7_n | case7_n | -| test.cpp:25:5:25:11 | case8_8 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:25:5:25:11 | case8_8 | case8_8 | test.cpp:24:5:24:11 | case8_B | case8_B | -| test.cpp:28:5:28:11 | case9_m | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:28:5:28:11 | case9_m | case9_m | test.cpp:27:5:27:12 | case9_rn | case9_rn | -| test.cpp:29:5:29:12 | case9_rh | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:29:5:29:12 | case9_rh | case9_rh | test.cpp:27:5:27:12 | case9_rn | case9_rn | -| test.cpp:32:5:32:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:32:5:32:15 | case10_xmmx | case10_xmmx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:32:5:32:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:32:5:32:15 | case10_xmmx | case10_xmmx | test.cpp:33:5:33:16 | case10_xmrnx | case10_xmrnx | -| test.cpp:32:5:32:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:32:5:32:15 | case10_xmmx | case10_xmmx | test.cpp:34:5:34:16 | case10_xrnmx | case10_xrnmx | -| test.cpp:33:5:33:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:33:5:33:16 | case10_xmrnx | case10_xmrnx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:33:5:33:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:33:5:33:16 | case10_xmrnx | case10_xmrnx | test.cpp:34:5:34:16 | case10_xrnmx | case10_xrnmx | -| test.cpp:33:5:33:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:33:5:33:16 | case10_xmrnx | case10_xmrnx | test.cpp:35:5:35:17 | case10_xrnrhx | case10_xrnrhx | -| test.cpp:34:5:34:16 | case10_xrnmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:34:5:34:16 | case10_xrnmx | case10_xrnmx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:35:5:35:17 | case10_xrnrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:35:5:35:17 | case10_xrnrhx | case10_xrnrhx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:36:5:36:17 | case10_xrhrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:36:5:36:17 | case10_xrhrhx | case10_xrhrhx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:36:5:36:17 | case10_xrhrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:36:5:36:17 | case10_xrhrhx | case10_xrhrhx | test.cpp:35:5:35:17 | case10_xrnrhx | case10_xrnrhx | -| test.cpp:37:5:37:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:37:5:37:16 | case10_xmrhx | case10_xmrhx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:37:5:37:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:37:5:37:16 | case10_xmrhx | case10_xmrhx | test.cpp:33:5:33:16 | case10_xmrnx | case10_xmrnx | -| test.cpp:37:5:37:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:37:5:37:16 | case10_xmrhx | case10_xmrhx | test.cpp:35:5:35:17 | case10_xrnrhx | case10_xrnrhx | -| test.cpp:38:5:38:16 | case10_xrhmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:38:5:38:16 | case10_xrhmx | case10_xrhmx | test.cpp:31:5:31:17 | case10_xrnrnx | case10_xrnrnx | -| test.cpp:38:5:38:16 | case10_xrhmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:38:5:38:16 | case10_xrhmx | case10_xrhmx | test.cpp:34:5:34:16 | case10_xrnmx | case10_xrnmx | -| test.cpp:40:15:40:22 | case11_O | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:40:15:40:22 | case11_O | case11_O | test.cpp:40:5:40:12 | case11_o | case11_o | -| test.cpp:43:5:43:14 | case12_8bB | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:43:5:43:14 | case12_8bB | case12_8bB | test.cpp:42:5:42:14 | case12_BBb | case12_BBb | +| test.cpp:4:5:4:13 | case1_FOO | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:4:5:4:13 | case1_FOO | case1_FOO | test.cpp:3:5:3:13 | case1_foo | case1_foo | +| test.cpp:4:5:4:13 | case1_FOO | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:4:5:4:13 | case1_FOO | case1_FOO | test.cpp:5:5:5:13 | case1_fOo | case1_fOo | +| test.cpp:5:5:5:13 | case1_fOo | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:5:5:5:13 | case1_fOo | case1_fOo | test.cpp:3:5:3:13 | case1_foo | case1_foo | +| test.cpp:8:5:8:15 | case2_f_o_o | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:8:5:8:15 | case2_f_o_o | case2_f_o_o | test.cpp:7:5:7:13 | case2_foo | case2_foo | +| test.cpp:11:5:11:13 | case3_fO0 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:11:5:11:13 | case3_fO0 | case3_fO0 | test.cpp:10:5:10:13 | case3_fOO | case3_fOO | +| test.cpp:13:5:13:12 | case4_II | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:13:5:13:12 | case4_II | case4_II | test.cpp:15:5:15:12 | case4_Il | case4_Il | +| test.cpp:14:5:14:12 | case4_I1 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:14:5:14:12 | case4_I1 | case4_I1 | test.cpp:13:5:13:12 | case4_II | case4_II | +| test.cpp:14:5:14:12 | case4_I1 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:14:5:14:12 | case4_I1 | case4_I1 | test.cpp:15:5:15:12 | case4_Il | case4_Il | +| test.cpp:18:5:18:11 | case5_5 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:18:5:18:11 | case5_5 | case5_5 | test.cpp:17:5:17:11 | case5_S | case5_S | +| test.cpp:21:5:21:11 | case6_2 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:21:5:21:11 | case6_2 | case6_2 | test.cpp:20:5:20:11 | case6_Z | case6_Z | +| test.cpp:24:5:24:11 | case7_h | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:24:5:24:11 | case7_h | case7_h | test.cpp:23:5:23:11 | case7_n | case7_n | +| test.cpp:27:5:27:11 | case8_8 | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:27:5:27:11 | case8_8 | case8_8 | test.cpp:26:5:26:11 | case8_B | case8_B | +| test.cpp:30:5:30:11 | case9_m | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:30:5:30:11 | case9_m | case9_m | test.cpp:29:5:29:12 | case9_rn | case9_rn | +| test.cpp:31:5:31:12 | case9_rh | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:31:5:31:12 | case9_rh | case9_rh | test.cpp:29:5:29:12 | case9_rn | case9_rn | +| test.cpp:34:5:34:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:34:5:34:15 | case10_xmmx | case10_xmmx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:34:5:34:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:34:5:34:15 | case10_xmmx | case10_xmmx | test.cpp:35:5:35:16 | case10_xmrnx | case10_xmrnx | +| test.cpp:34:5:34:15 | case10_xmmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:34:5:34:15 | case10_xmmx | case10_xmmx | test.cpp:36:5:36:16 | case10_xrnmx | case10_xrnmx | +| test.cpp:35:5:35:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:35:5:35:16 | case10_xmrnx | case10_xmrnx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:35:5:35:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:35:5:35:16 | case10_xmrnx | case10_xmrnx | test.cpp:36:5:36:16 | case10_xrnmx | case10_xrnmx | +| test.cpp:35:5:35:16 | case10_xmrnx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:35:5:35:16 | case10_xmrnx | case10_xmrnx | test.cpp:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | +| test.cpp:36:5:36:16 | case10_xrnmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:36:5:36:16 | case10_xrnmx | case10_xrnmx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:37:5:37:17 | case10_xrnrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:38:5:38:17 | case10_xrhrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:38:5:38:17 | case10_xrhrhx | case10_xrhrhx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:38:5:38:17 | case10_xrhrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:38:5:38:17 | case10_xrhrhx | case10_xrhrhx | test.cpp:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | +| test.cpp:39:5:39:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:39:5:39:16 | case10_xmrhx | case10_xmrhx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:39:5:39:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:39:5:39:16 | case10_xmrhx | case10_xmrhx | test.cpp:35:5:35:16 | case10_xmrnx | case10_xmrnx | +| test.cpp:39:5:39:16 | case10_xmrhx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:39:5:39:16 | case10_xmrhx | case10_xmrhx | test.cpp:37:5:37:17 | case10_xrnrhx | case10_xrnrhx | +| test.cpp:40:5:40:16 | case10_xrhmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:40:5:40:16 | case10_xrhmx | case10_xrhmx | test.cpp:33:5:33:17 | case10_xrnrnx | case10_xrnrnx | +| test.cpp:40:5:40:16 | case10_xrhmx | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:40:5:40:16 | case10_xrhmx | case10_xrhmx | test.cpp:36:5:36:16 | case10_xrnmx | case10_xrnmx | +| test.cpp:42:15:42:22 | case11_O | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:42:15:42:22 | case11_O | case11_O | test.cpp:42:5:42:12 | case11_o | case11_o | +| test.cpp:45:5:45:14 | case12_8bB | The identifier $@ is not typographically unambiguous from the identifier $@ | test.cpp:45:5:45:14 | case12_8bB | case12_8bB | test.cpp:44:5:44:14 | case12_BBb | case12_BBb | diff --git a/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql b/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql index 17134b9eba..64357d95de 100644 --- a/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql +++ b/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/DifferentIdentifiersNotTypographicallyUnambiguous.ql @@ -1,2 +1,5 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.differentidentifiersnottypographicallyunambiguous.DifferentIdentifiersNotTypographicallyUnambiguous + +class TestFileQuery extends DifferentIdentifiersNotTypographicallyUnambiguousSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.cpp b/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.cpp index 390d59323f..e65d7f6e80 100644 --- a/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.cpp +++ b/cpp/common/test/rules/differentidentifiersnottypographicallyunambiguous/test.cpp @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. int case1_foo; int case1_FOO; // NON_COMPLIANT int case1_fOo; // NON_COMPLIANT diff --git a/cpp/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql b/cpp/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql index 43bc4286e6..ceae7e6a9e 100644 --- a/cpp/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql +++ b/cpp/common/test/rules/donotallowamutextogooutofscopewhilelocked/DoNotAllowAMutexToGoOutOfScopeWhileLocked.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotallowamutextogooutofscopewhilelocked.DoNotAllowAMutexToGoOutOfScopeWhileLocked + +class TestFileQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql b/cpp/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql index 9f0c40ef4c..9955572e73 100644 --- a/cpp/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql +++ b/cpp/common/test/rules/donotcopyaddressofautostorageobjecttootherobject/DoNotCopyAddressOfAutoStorageObjectToOtherObject.ql @@ -1,2 +1,5 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotcopyaddressofautostorageobjecttootherobject.DoNotCopyAddressOfAutoStorageObjectToOtherObject + +class TestFileQuery extends DoNotCopyAddressOfAutoStorageObjectToOtherObjectSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.expected b/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.expected index 0ae4aafa66..71ccc11ba7 100644 --- a/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.expected +++ b/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.expected @@ -1,6 +1,3 @@ -| test.cpp:4:18:4:33 | call to mutex | Mutex used by thread potentially $@ while in use. | test.cpp:11:18:11:26 | call to ~mutex | destroyed | | test.cpp:4:18:4:33 | call to mutex | Mutex used by thread potentially $@ while in use. | test.cpp:11:18:11:26 | delete | destroyed | -| test.cpp:16:14:16:15 | call to mutex | Mutex used by thread potentially $@ while in use. | test.cpp:21:1:21:1 | call to ~mutex | destroyed | | test.cpp:16:14:16:15 | call to mutex | Mutex used by thread potentially $@ while in use. | test.cpp:21:1:21:1 | return ... | destroyed | -| test.cpp:94:8:94:23 | call to mutex | Mutex used by thread potentially $@ while in use. | test.cpp:10:34:10:42 | call to ~mutex | destroyed | | test.cpp:94:8:94:23 | call to mutex | Mutex used by thread potentially $@ while in use. | test.cpp:10:34:10:42 | delete | destroyed | diff --git a/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql b/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql index f3ba1667a8..96ea58009e 100644 --- a/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql +++ b/cpp/common/test/rules/donotdestroyamutexwhileitislocked/DoNotDestroyAMutexWhileItIsLocked.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotdestroyamutexwhileitislocked.DoNotDestroyAMutexWhileItIsLocked + +class TestFileQuery extends DoNotDestroyAMutexWhileItIsLockedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.expected b/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.expected new file mode 100644 index 0000000000..f94246bc63 --- /dev/null +++ b/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.expected @@ -0,0 +1,2 @@ +| test.cpp:6:3:6:13 | call to memcpy | Call to 'memcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.cpp:6:22:6:26 | & ... | aliased pointer | test.cpp:6:15:6:19 | & ... | restrict-qualified parameter | test.cpp:6:15:6:19 | & ... | addressof1 | test.cpp:6:22:6:26 | & ... | addressof2 | +| test.cpp:8:3:8:13 | call to memcpy | Call to 'memcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.cpp:8:22:8:26 | & ... | aliased pointer | test.cpp:8:15:8:19 | & ... | restrict-qualified parameter | test.cpp:8:15:8:19 | & ... | addressof1 | test.cpp:8:22:8:26 | & ... | addressof2 | diff --git a/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql b/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql new file mode 100644 index 0000000000..dc3a521edf --- /dev/null +++ b/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/DoNotPassAliasedPointerToRestrictQualifiedParamShared.ql @@ -0,0 +1,6 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.donotpassaliasedpointertorestrictqualifiedparamshared.DoNotPassAliasedPointerToRestrictQualifiedParamShared + +class TestFileQuery extends DoNotPassAliasedPointerToRestrictQualifiedParamSharedSharedQuery, + TestQuery +{ } diff --git a/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/test.cpp b/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/test.cpp new file mode 100644 index 0000000000..42a35d0e92 --- /dev/null +++ b/cpp/common/test/rules/donotpassaliasedpointertorestrictqualifiedparamshared/test.cpp @@ -0,0 +1,10 @@ +#include + +int a[20]; + +void undefined_behaviour_fn_119(void) { + std::memcpy(&a[0], &a[1], 10u * sizeof(a[0])); // NON_COMPLIANT + std::memmove(&a[0], &a[1], 10u * sizeof(a[0])); // COMPLIANT + std::memcpy(&a[1], &a[0], 10u * sizeof(a[0])); // NON_COMPLIANT + std::memmove(&a[1], &a[0], 10u * sizeof(a[0])); // COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected b/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected index 21fe1e3ccd..2d293e6928 100644 --- a/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected +++ b/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.expected @@ -4,15 +4,19 @@ problems | test.cpp:13:10:13:11 | p4 | test.cpp:5:14:5:15 | l2 | test.cpp:13:10:13:11 | p4 | Subtraction between left operand pointing to array $@ and other operand pointing to array $@. | test.cpp:3:7:3:8 | l2 | l2 | test.cpp:2:7:2:8 | l1 | l1 | | test.cpp:13:15:13:16 | l1 | test.cpp:13:15:13:16 | l1 | test.cpp:13:15:13:16 | l1 | Subtraction between right operand pointing to array $@ and other operand pointing to array $@. | test.cpp:2:7:2:8 | l1 | l1 | test.cpp:3:7:3:8 | l2 | l2 | edges -| test.cpp:4:14:4:15 | l1 | test.cpp:10:10:10:11 | p1 | -| test.cpp:4:14:4:15 | l1 | test.cpp:12:10:12:11 | p1 | -| test.cpp:5:14:5:15 | l2 | test.cpp:11:10:11:11 | p2 | -| test.cpp:5:14:5:15 | l2 | test.cpp:12:15:12:16 | p2 | -| test.cpp:5:14:5:15 | l2 | test.cpp:13:10:13:11 | p4 | -| test.cpp:5:14:5:15 | l2 | test.cpp:14:10:14:11 | p4 | +| test.cpp:4:14:4:15 | l1 | test.cpp:4:14:4:18 | access to array | provenance | Config | +| test.cpp:4:14:4:18 | access to array | test.cpp:10:10:10:11 | p1 | provenance | | +| test.cpp:4:14:4:18 | access to array | test.cpp:12:10:12:11 | p1 | provenance | | +| test.cpp:5:14:5:15 | l2 | test.cpp:5:14:5:19 | access to array | provenance | Config | +| test.cpp:5:14:5:19 | access to array | test.cpp:11:10:11:11 | p2 | provenance | | +| test.cpp:5:14:5:19 | access to array | test.cpp:12:15:12:16 | p2 | provenance | | +| test.cpp:5:14:5:19 | access to array | test.cpp:13:10:13:11 | p4 | provenance | | +| test.cpp:5:14:5:19 | access to array | test.cpp:14:10:14:11 | p4 | provenance | | nodes | test.cpp:4:14:4:15 | l1 | semmle.label | l1 | +| test.cpp:4:14:4:18 | access to array | semmle.label | access to array | | test.cpp:5:14:5:15 | l2 | semmle.label | l2 | +| test.cpp:5:14:5:19 | access to array | semmle.label | access to array | | test.cpp:10:10:10:11 | p1 | semmle.label | p1 | | test.cpp:10:15:10:16 | l1 | semmle.label | l1 | | test.cpp:11:10:11:11 | p2 | semmle.label | p2 | diff --git a/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql b/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql index bf47bf28f1..374a6fc52b 100644 --- a/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql +++ b/cpp/common/test/rules/donotsubtractpointersaddressingdifferentarrays/DoNotSubtractPointersAddressingDifferentArrays.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotsubtractpointersaddressingdifferentarrays.DoNotSubtractPointersAddressingDifferentArrays + +class TestFileQuery extends DoNotSubtractPointersAddressingDifferentArraysSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql b/cpp/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql index 6fdfb9c928..edef2c1127 100644 --- a/cpp/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql +++ b/cpp/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotusemorethantwolevelsofpointerindirection.DoNotUseMoreThanTwoLevelsOfPointerIndirection + +class TestFileQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected b/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected index fa181755e8..31ff47e55c 100644 --- a/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected +++ b/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.expected @@ -1,5 +1,8 @@ -| test.cpp:4:13:4:18 | ... + ... | Array pointer p2 points 1 element passed the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | -| test.cpp:5:13:5:18 | ... + ... | Array pointer p3 points 1 element passed the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | -| test.cpp:6:13:6:18 | & ... | Array pointer p4 points 1 element passed the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | -| test.cpp:11:8:11:11 | ... -- | Array pointer p7 points 1 element passed the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | -| test.cpp:12:8:12:9 | p3 | Array pointer p8 points 1 element passed the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:4:13:4:18 | ... + ... | Array pointer p2 points 1 element past the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:5:13:5:18 | ... + ... | Array pointer p3 points 1 element past the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:6:13:6:18 | & ... | Array pointer p4 points 1 element past the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:11:8:11:11 | ... -- | Array pointer p7 points 1 element past the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:12:8:12:9 | p3 | Array pointer p8 points 1 element past the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:25:15:25:21 | & ... | Array pointer p14 points 1 element past the end of $@. | test.cpp:2:7:2:8 | l1 | l1 | +| test.cpp:30:15:30:21 | & ... | Array pointer p17 points 1 element past the end of $@. | test.cpp:28:24:28:42 | (unsigned char *)... | cast to byte pointer | +| test.cpp:35:15:35:21 | & ... | Array pointer p20 points 1 element past the end of $@. | test.cpp:33:24:33:43 | (unsigned char *)... | cast to byte pointer | diff --git a/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql b/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql index b06daa52b7..c0a3435e05 100644 --- a/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql +++ b/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/DoNotUsePointerArithmeticToAddressDifferentArrays.ql @@ -1,2 +1,5 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotusepointerarithmetictoaddressdifferentarrays.DoNotUsePointerArithmeticToAddressDifferentArrays + +class TestFileQuery extends DoNotUsePointerArithmeticToAddressDifferentArraysSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/test.cpp b/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/test.cpp index c1032ee735..d9874bfb29 100644 --- a/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/test.cpp +++ b/cpp/common/test/rules/donotusepointerarithmetictoaddressdifferentarrays/test.cpp @@ -17,4 +17,37 @@ void f1() { 3 + p1; // COMPLIANT - points to an element on beyond the end of the array int *p11 = &l1[3]; // COMPLIANT - points to an element on beyond the end of the array + + // Casting to a pointer to a type of the same size doesn't invalidate the + // analysis + unsigned int *p12 = (unsigned int *)l1; + void *p13 = &p12[3]; // COMPLIANT + void *p14 = &p12[4]; // NON_COMPLIANT + + // Casting to a char* is effectively a new array of length sizeof(T) + unsigned char *p15 = (unsigned char *)l1; + void *p16 = &p15[4]; // COMPLIANT + void *p17 = &p15[5]; // NON_COMPLIANT + + long l2[3]; + unsigned char *p18 = (unsigned char *)&l2; + void *p19 = &p18[8]; // COMPLIANT + void *p20 = &p18[9]; // NON_COMPLIANT + + // Casting to a pointer to a differently sized type that isn't char + // invalidates analysis + long *p21 = (long *)&l1; + void *p22 = &p21[0]; // COMPLIANT + void *p23 = &p21[100]; // NON_COMPLIANT[FALSE_NEGATIVE] + + // Casting a byte pointer to a differently sized type that isn't char + // invalidates analysis + long *p24 = (long *)p15; + void *p25 = &p24[0]; // COMPLIANT + void *p26 = &p24[100]; // NON_COMPLIANT[FALSE_NEGATIVE] + + // Void pointers have size zero and can't be analyzed. + void *p27 = 0; + unsigned char *p28 = (unsigned char *)p27; + void *p29 = &p28[100]; // COMPLIANT } \ No newline at end of file diff --git a/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected b/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected index 1a3344c361..eaefe65ca3 100644 --- a/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected +++ b/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.expected @@ -1 +1 @@ -| test.cpp:3:47:3:55 | call to rand | Use of banned function std::rand. | +| test.cpp:5:47:5:55 | call to rand | Use of banned function rand. | diff --git a/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql b/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql index 05388363d1..3ad5626256 100644 --- a/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql +++ b/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/DoNotUseRandForGeneratingPseudorandomNumbers.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotuserandforgeneratingpseudorandomnumbers.DoNotUseRandForGeneratingPseudorandomNumbers + +class TestFileQuery extends DoNotUseRandForGeneratingPseudorandomNumbersSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.cpp b/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.cpp index 03d820a15e..8fc837c4d2 100644 --- a/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.cpp +++ b/cpp/common/test/rules/donotuserandforgeneratingpseudorandomnumbers/test.cpp @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. #include void test_use_of_rand() { int random_number = std::rand() % 10; } \ No newline at end of file diff --git a/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected b/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected index 1b31174b2f..cab80e0fe0 100644 --- a/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected +++ b/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.expected @@ -10,21 +10,27 @@ problems | test.cpp:25:7:25:14 | ... >= ... | test.cpp:7:14:7:15 | l1 | test.cpp:25:7:25:8 | p1 | Compare operation >= comparing left operand pointing to array $@ and other operand pointing to array $@. | test.cpp:2:7:2:8 | l1 | l1 | test.cpp:4:7:4:8 | l3 | l3 | | test.cpp:25:7:25:14 | ... >= ... | test.cpp:25:13:25:14 | l3 | test.cpp:25:13:25:14 | l3 | Compare operation >= comparing right operand pointing to array $@ and other operand pointing to array $@. | test.cpp:4:7:4:8 | l3 | l3 | test.cpp:2:7:2:8 | l1 | l1 | edges -| test.cpp:6:13:6:14 | l1 | test.cpp:13:12:13:13 | p0 | -| test.cpp:7:14:7:15 | l1 | test.cpp:11:7:11:8 | p1 | -| test.cpp:7:14:7:15 | l1 | test.cpp:13:7:13:8 | p1 | -| test.cpp:7:14:7:15 | l1 | test.cpp:15:13:15:14 | p1 | -| test.cpp:7:14:7:15 | l1 | test.cpp:17:7:17:8 | p1 | -| test.cpp:7:14:7:15 | l1 | test.cpp:23:13:23:14 | p1 | -| test.cpp:7:14:7:15 | l1 | test.cpp:25:7:25:8 | p1 | -| test.cpp:8:14:8:15 | l1 | test.cpp:11:12:11:13 | p2 | -| test.cpp:8:14:8:15 | l1 | test.cpp:21:7:21:8 | p2 | -| test.cpp:9:14:9:15 | l2 | test.cpp:21:12:21:13 | p3 | +| test.cpp:6:13:6:14 | l1 | test.cpp:13:12:13:13 | p0 | provenance | | +| test.cpp:7:14:7:15 | l1 | test.cpp:7:14:7:18 | access to array | provenance | Config | +| test.cpp:7:14:7:18 | access to array | test.cpp:11:7:11:8 | p1 | provenance | | +| test.cpp:7:14:7:18 | access to array | test.cpp:13:7:13:8 | p1 | provenance | | +| test.cpp:7:14:7:18 | access to array | test.cpp:15:13:15:14 | p1 | provenance | | +| test.cpp:7:14:7:18 | access to array | test.cpp:17:7:17:8 | p1 | provenance | | +| test.cpp:7:14:7:18 | access to array | test.cpp:23:13:23:14 | p1 | provenance | | +| test.cpp:7:14:7:18 | access to array | test.cpp:25:7:25:8 | p1 | provenance | | +| test.cpp:8:14:8:15 | l1 | test.cpp:8:14:8:18 | access to array | provenance | Config | +| test.cpp:8:14:8:18 | access to array | test.cpp:11:12:11:13 | p2 | provenance | | +| test.cpp:8:14:8:18 | access to array | test.cpp:21:7:21:8 | p2 | provenance | | +| test.cpp:9:14:9:15 | l2 | test.cpp:9:14:9:18 | access to array | provenance | Config | +| test.cpp:9:14:9:18 | access to array | test.cpp:21:12:21:13 | p3 | provenance | | nodes | test.cpp:6:13:6:14 | l1 | semmle.label | l1 | | test.cpp:7:14:7:15 | l1 | semmle.label | l1 | +| test.cpp:7:14:7:18 | access to array | semmle.label | access to array | | test.cpp:8:14:8:15 | l1 | semmle.label | l1 | +| test.cpp:8:14:8:18 | access to array | semmle.label | access to array | | test.cpp:9:14:9:15 | l2 | semmle.label | l2 | +| test.cpp:9:14:9:18 | access to array | semmle.label | access to array | | test.cpp:11:7:11:8 | p1 | semmle.label | p1 | | test.cpp:11:12:11:13 | p2 | semmle.label | p2 | | test.cpp:13:7:13:8 | p1 | semmle.label | p1 | diff --git a/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql b/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql index c6cca37aa2..bceb46bf63 100644 --- a/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql +++ b/cpp/common/test/rules/donotuserelationaloperatorswithdifferingarrays/DoNotUseRelationalOperatorsWithDifferingArrays.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.donotuserelationaloperatorswithdifferingarrays.DoNotUseRelationalOperatorsWithDifferingArrays + +class TestFileQuery extends DoNotUseRelationalOperatorsWithDifferingArraysSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.expected b/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.expected similarity index 100% rename from cpp/autosar/test/rules/M17-0-5/SetjmpMacroAndTheLongjmpFunctionUsed.expected rename to cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.expected diff --git a/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.expected.qcc b/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.expected.qcc new file mode 100644 index 0000000000..02c560dd39 --- /dev/null +++ b/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.expected.qcc @@ -0,0 +1,4 @@ +| test.cpp:7:9:7:19 | setjmp(__env) | Use of banned setjmp macro. | +| test.cpp:12:3:12:19 | longjmp(__env,__val) | Use of banned longjmp function. | +| test.cpp:14:3:14:19 | longjmp(__env,__val) | Use of banned longjmp function. | +| test.cpp:16:9:16:19 | setjmp(__env) | Use of banned setjmp macro. | diff --git a/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.ql b/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.ql new file mode 100644 index 0000000000..e0026467ff --- /dev/null +++ b/cpp/common/test/rules/donotusesetjmporlongjmpshared/DoNotUseSetjmpOrLongjmpShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.donotusesetjmporlongjmpshared.DoNotUseSetjmpOrLongjmpShared + +class TestFileQuery extends DoNotUseSetjmpOrLongjmpSharedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/donotusesetjmporlongjmpshared/test.cpp b/cpp/common/test/rules/donotusesetjmporlongjmpshared/test.cpp new file mode 100644 index 0000000000..cb810aa3a5 --- /dev/null +++ b/cpp/common/test/rules/donotusesetjmporlongjmpshared/test.cpp @@ -0,0 +1,19 @@ +#include + +int test_jmps() { + jmp_buf env; + int val; + + val = setjmp(env); + if (val) { + return (val); + } + + longjmp(env, 101); + + longjmp(env, 102); + + val = setjmp(env); + + return 0; +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.expected b/cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.expected similarity index 100% rename from cpp/autosar/test/rules/M15-1-3/EmptyThrowOutsideCatch.expected rename to cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.expected diff --git a/cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.ql b/cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.ql new file mode 100644 index 0000000000..a07b861639 --- /dev/null +++ b/cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.emptythrowonlywithinacatchhandler.EmptyThrowOnlyWithinACatchHandler + +class TestFileQuery extends EmptyThrowOnlyWithinACatchHandlerSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M15-1-3/test.cpp b/cpp/common/test/rules/emptythrowonlywithinacatchhandler/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M15-1-3/test.cpp rename to cpp/common/test/rules/emptythrowonlywithinacatchhandler/test.cpp diff --git a/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.expected b/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.expected new file mode 100644 index 0000000000..4d4c944f3f --- /dev/null +++ b/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.expected @@ -0,0 +1,2 @@ +| test.cpp:1:6:1:6 | C | Base type of enumeration is not explicitly specified. | +| test.cpp:5:12:5:12 | A | Base type of enumeration is not explicitly specified. | diff --git a/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql b/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql new file mode 100644 index 0000000000..999f505c5f --- /dev/null +++ b/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.enumerationnotdefinedwithanexplicitunderlyingtype.EnumerationNotDefinedWithAnExplicitUnderlyingType + +class TestFileQuery extends EnumerationNotDefinedWithAnExplicitUnderlyingTypeSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/test.cpp b/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/test.cpp new file mode 100644 index 0000000000..5d0170bb93 --- /dev/null +++ b/cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/test.cpp @@ -0,0 +1,16 @@ +enum C // NON_COMPLIANT +{ + c1 +}; +enum class A // NON_COMPLIANT +{ + a1 +}; +enum class B : int // COMPLIANT +{ + b1 +}; +enum D : int // COMPLIANT +{ + d1 +}; diff --git a/cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.expected b/cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.expected similarity index 100% rename from cpp/autosar/test/rules/A15-1-2/PointerExceptionObject.expected rename to cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.expected diff --git a/cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.ql b/cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.ql new file mode 100644 index 0000000000..d0727790d3 --- /dev/null +++ b/cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.exceptionobjecthavepointertype.ExceptionObjectHavePointerType + +class TestFileQuery extends ExceptionObjectHavePointerTypeSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A15-1-2/test.cpp b/cpp/common/test/rules/exceptionobjecthavepointertype/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A15-1-2/test.cpp rename to cpp/common/test/rules/exceptionobjecthavepointertype/test.cpp diff --git a/cpp/common/test/rules/exceptionsafetyguarantees/ExceptionSafetyGuarantees.ql b/cpp/common/test/rules/exceptionsafetyguarantees/ExceptionSafetyGuarantees.ql index 8b623baeb5..bfa4a88318 100644 --- a/cpp/common/test/rules/exceptionsafetyguarantees/ExceptionSafetyGuarantees.ql +++ b/cpp/common/test/rules/exceptionsafetyguarantees/ExceptionSafetyGuarantees.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.exceptionsafetyguarantees.ExceptionSafetyGuarantees + +class TestFileQuery extends ExceptionSafetyGuaranteesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.expected b/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.expected index 18689c333b..b8a8c17435 100644 --- a/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.expected +++ b/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.expected @@ -6,7 +6,7 @@ | test.cpp:103:5:103:9 | re-throw exception | The $@ is not released explicitly before throwing an exception. | test.cpp:99:12:99:21 | new | allocated resource | | test.cpp:125:5:125:37 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:124:9:124:18 | new | allocated resource | | test.cpp:134:5:134:37 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:133:23:133:32 | new | allocated resource | -| test.cpp:142:3:142:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:141:3:141:4 | fs | allocated resource | -| test.cpp:154:3:154:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:152:3:152:4 | fs | allocated resource | -| test.cpp:160:3:160:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:159:3:159:5 | mtx | allocated resource | -| test.cpp:172:3:172:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:170:3:170:5 | mtx | allocated resource | +| test.cpp:142:3:142:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:141:6:141:9 | call to open | allocated resource | +| test.cpp:154:3:154:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:152:6:152:9 | call to open | allocated resource | +| test.cpp:160:3:160:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:159:7:159:10 | call to lock | allocated resource | +| test.cpp:172:3:172:35 | throw ... | The $@ is not released explicitly before throwing an exception. | test.cpp:170:7:170:10 | call to lock | allocated resource | diff --git a/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.ql b/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.ql index d70d72a24c..aa18543c36 100644 --- a/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.ql +++ b/cpp/common/test/rules/exceptionsafetyvalidstate/ExceptionSafetyValidState.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.exceptionsafetyvalidstate.ExceptionSafetyValidState + +class TestFileQuery extends ExceptionSafetyValidStateSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/exithandlerthrowsexception/ExitHandlerThrowsException.ql b/cpp/common/test/rules/exithandlerthrowsexception/ExitHandlerThrowsException.ql index 29e308f137..c61992b8b0 100644 --- a/cpp/common/test/rules/exithandlerthrowsexception/ExitHandlerThrowsException.ql +++ b/cpp/common/test/rules/exithandlerthrowsexception/ExitHandlerThrowsException.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.exithandlerthrowsexception.ExitHandlerThrowsException + +class TestFileQuery extends ExitHandlerThrowsExceptionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/explicitabrupttermination/ExplicitAbruptTermination.ql b/cpp/common/test/rules/explicitabrupttermination/ExplicitAbruptTermination.ql index d71219a14c..d7df643551 100644 --- a/cpp/common/test/rules/explicitabrupttermination/ExplicitAbruptTermination.ql +++ b/cpp/common/test/rules/explicitabrupttermination/ExplicitAbruptTermination.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.explicitabrupttermination.ExplicitAbruptTermination + +class TestFileQuery extends ExplicitAbruptTerminationSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.expected b/cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.expected similarity index 100% rename from cpp/autosar/test/rules/A18-9-2/ForwardingValuesToOtherFunctions.expected rename to cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.expected diff --git a/cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.ql b/cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.ql new file mode 100644 index 0000000000..4f08530f35 --- /dev/null +++ b/cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.forwardingreferencesandforwardnotusedtogether.ForwardingReferencesAndForwardNotUsedTogether + +class TestFileQuery extends ForwardingReferencesAndForwardNotUsedTogetherSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A18-9-2/test.cpp b/cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A18-9-2/test.cpp rename to cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/test.cpp diff --git a/cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.expected b/cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.expected new file mode 100644 index 0000000000..2f681c9210 --- /dev/null +++ b/cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.expected @@ -0,0 +1 @@ +| test.cpp:16:3:16:8 | call to remove | Return value from remove is not tested for errors. | diff --git a/cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql b/cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql new file mode 100644 index 0000000000..12c2196efd --- /dev/null +++ b/cpp/common/test/rules/functionerroneousreturnvaluenottested/FunctionErroneousReturnValueNotTested.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functionerroneousreturnvaluenottested.FunctionErroneousReturnValueNotTested + +class TestFileQuery extends FunctionErroneousReturnValueNotTestedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/functionerroneousreturnvaluenottested/test.cpp b/cpp/common/test/rules/functionerroneousreturnvaluenottested/test.cpp new file mode 100644 index 0000000000..08e2f23dec --- /dev/null +++ b/cpp/common/test/rules/functionerroneousreturnvaluenottested/test.cpp @@ -0,0 +1,17 @@ +#include + +void test_compliant() { + // Return value is passed to an lvalue and then tested. + FILE *fh = fopen("/etc/foo", "r"); + if (!fh) { // COMPLIANT + return; + } + + // Return value is tested immediately as an rvalue. + if (fclose(fh)) // COMPLIANT + return; +} + +void test_noncompliant() { + remove("/bin/bash"); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.expected b/cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.expected new file mode 100644 index 0000000000..62787cca0b --- /dev/null +++ b/cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.expected @@ -0,0 +1,2 @@ +| test.cpp:8:1:8:25 | #define MACRO4(x) (x + 1) | Macro used instead of a function. | +| test.cpp:13:1:13:48 | #define MACRO9() printf_custom("output = %d", 7) | Macro used instead of a function. | diff --git a/cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql b/cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql new file mode 100644 index 0000000000..29088c4458 --- /dev/null +++ b/cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functionlikemacrosdefined.FunctionLikeMacrosDefined + +class TestFileQuery extends FunctionLikeMacrosDefinedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/functionlikemacrosdefined/test.cpp b/cpp/common/test/rules/functionlikemacrosdefined/test.cpp new file mode 100644 index 0000000000..f39236ca3b --- /dev/null +++ b/cpp/common/test/rules/functionlikemacrosdefined/test.cpp @@ -0,0 +1,42 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +#define MACRO(OP, L, R) ((L)OP(R)) // COMPLIANT +#define MACRO2(L, R) (L + R) // COMPLIANT +#define MACRO3(L, R) (L " " R " " L) // COMPLIANT +#define MACRO4(x) (x + 1) // NON_COMPLIANT +#define MACRO5(L, LR) (LR + 1) // COMPLIANT +#define MACRO6(x) printf_custom("output = %d", test##x) // COMPLIANT +#define MACRO7(x) #x // COMPLIANT +#define MACRO8(x) "NOP" // COMPLIANT +#define MACRO9() printf_custom("output = %d", 7) // NON_COMPLIANT +#define MACRO10(x) // COMPLIANT +#define MY_ASSERT(X) assert(X) // NON_COMPLIANT[FALSE_NEGATIVE] + +char a1[MACRO2(1, 1) + 6]; +extern int printf_custom(char *, int); +int test1; + +void f() { + int i = MACRO(+, 1, 1); + int i2 = MACRO2(7, 10); + + static int i3 = MACRO2(1, 1); + + char *i4 = MACRO3("prefix", "suffix"); + + int i5 = MACRO4(1); + + int i6 = MACRO4(MACRO2(1, 1)); + + int i7 = MACRO5(1, 1); + + MACRO6(1); + + char *i10 = MACRO7("prefix"); + + asm(MACRO8(1)); + + MY_ASSERT(1); +} \ No newline at end of file diff --git a/cpp/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql b/cpp/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql index d8dcf68384..4af4aeceaf 100644 --- a/cpp/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql +++ b/cpp/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.functionnoreturnattributecondition.FunctionNoReturnAttributeCondition + +class TestFileQuery extends FunctionNoReturnAttributeConditionSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.expected b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.expected similarity index 100% rename from cpp/autosar/test/rules/A7-5-2/RecursiveFunctions.expected rename to cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.expected diff --git a/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql new file mode 100644 index 0000000000..e95ba9b7f7 --- /dev/null +++ b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functionscallthemselveseitherdirectlyorindirectly.FunctionsCallThemselvesEitherDirectlyOrIndirectly + +class TestFileQuery extends FunctionsCallThemselvesEitherDirectlyOrIndirectlySharedQuery, TestQuery { +} diff --git a/cpp/autosar/test/rules/A7-5-2/test.cpp b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A7-5-2/test.cpp rename to cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/test.cpp diff --git a/cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.expected b/cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.expected similarity index 100% rename from cpp/autosar/test/rules/A14-8-2/ExplicitSpecializationsOfFunctionTemplatesUsed.expected rename to cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.expected diff --git a/cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.ql b/cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.ql new file mode 100644 index 0000000000..a64a9786b6 --- /dev/null +++ b/cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.functiontemplatesexplicitlyspecialized.FunctionTemplatesExplicitlySpecialized + +class TestFileQuery extends FunctionTemplatesExplicitlySpecializedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A14-8-2/test.cpp b/cpp/common/test/rules/functiontemplatesexplicitlyspecialized/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A14-8-2/test.cpp rename to cpp/common/test/rules/functiontemplatesexplicitlyspecialized/test.cpp diff --git a/cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.expected b/cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.expected similarity index 100% rename from cpp/autosar/test/rules/M7-3-1/GlobalNamespaceMembershipViolation.expected rename to cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.expected diff --git a/cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.ql b/cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.ql new file mode 100644 index 0000000000..19482c5b09 --- /dev/null +++ b/cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.globalnamespacedeclarations.GlobalNamespaceDeclarations + +class TestFileQuery extends GlobalNamespaceDeclarationsSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M7-3-1/test.cpp b/cpp/common/test/rules/globalnamespacedeclarations/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M7-3-1/test.cpp rename to cpp/common/test/rules/globalnamespacedeclarations/test.cpp diff --git a/cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.expected b/cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.expected similarity index 100% rename from cpp/autosar/test/rules/A18-5-4/GlobalSizedOperatorDeleteNotDefined.expected rename to cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.expected diff --git a/cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.ql b/cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.ql new file mode 100644 index 0000000000..61d492f0c6 --- /dev/null +++ b/cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.globalsizedoperatordeletenotdefined.GlobalSizedOperatorDeleteNotDefined + +class TestFileQuery extends GlobalSizedOperatorDeleteNotDefinedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A18-5-4/test.cpp b/cpp/common/test/rules/globalsizedoperatordeletenotdefined/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A18-5-4/test.cpp rename to cpp/common/test/rules/globalsizedoperatordeletenotdefined/test.cpp diff --git a/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.expected b/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.ql b/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.ql new file mode 100644 index 0000000000..c415cbcd70 --- /dev/null +++ b/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.globalunsizedoperatordeletenotdefined.GlobalUnsizedOperatorDeleteNotDefined + +class TestFileQuery extends GlobalUnsizedOperatorDeleteNotDefinedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/test.cpp b/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/test.cpp new file mode 100644 index 0000000000..8f77a41637 --- /dev/null +++ b/cpp/common/test/rules/globalunsizedoperatordeletenotdefined/test.cpp @@ -0,0 +1,3 @@ + +void operator delete(void *ptr) {} // NON_COMPLIANT +// void operator delete(void *ptr, std::size_t sz) {} diff --git a/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.expected b/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.expected new file mode 100644 index 0000000000..416f949eaa --- /dev/null +++ b/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.expected @@ -0,0 +1,2 @@ +| test.cpp:42:3:42:10 | goto ... | The goto statement and its $@ are not declared or enclosed in the same block. | test.cpp:46:3:46:5 | label ...: | label | +| test.cpp:57:5:57:12 | goto ... | The goto statement and its $@ are not declared or enclosed in the same block. | test.cpp:60:3:60:5 | label ...: | label | diff --git a/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql b/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql new file mode 100644 index 0000000000..f553135683 --- /dev/null +++ b/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotoreferencealabelinsurroundingblock.GotoReferenceALabelInSurroundingBlock + +class TestFileQuery extends GotoReferenceALabelInSurroundingBlockSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/test.cpp b/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/test.cpp new file mode 100644 index 0000000000..07ebb4b13a --- /dev/null +++ b/cpp/common/test/rules/gotoreferencealabelinsurroundingblock/test.cpp @@ -0,0 +1,87 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +/*void f1() { + int i = 0; goto L1; + for (;i < 100; i++) { + L1: // NON_COMPLIANT - this is compiler checked + break; + } +}*/ + +void f2() { + int i = 0; + if (i >= 0) { + for (int j = 0; j < 10; j++) { + goto L2; + } + } +L2: // COMPLIANT + return; +} + +void f3() { + int i = 0; + if (i >= 0) { + for (int j = 0; j < 10; j++) { + goto L3; + L3: // COMPLIANT + break; + } + } +} + +void f4() { + int i = 0; +L4: // COMPLIANT + if (i >= 0) { + goto L4; + } +} + +void f5(int p) { + goto L1; + + switch (p) { + case 0: + L1:; // NON_COMPLIANT + break; + default: + break; + } +} + +void f6(int p) { + + switch (p) { + case 0: + goto L1; + break; + default: + L1: // NON_COMPLIANT + break; + } +} + +void f7(int p) { +L1: // COMPLIANT + switch (p) { + case 0: + goto L1; + break; + default: + break; + } +} + +void f8(int p) { + + switch (p) { + case 0: + goto L1; + ; + L1:; // COMPLIANT + break; + default: + break; + } +} diff --git a/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.expected b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.expected new file mode 100644 index 0000000000..9e9d81e62c --- /dev/null +++ b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.expected @@ -0,0 +1,4 @@ +| test.cpp:9:3:9:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:9:3:9:10 | goto ... | l1 | test.cpp:5:1:5:3 | label ...: | label ...: | +| test.cpp:21:3:21:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:21:3:21:10 | goto ... | l2 | test.cpp:17:1:17:3 | label ...: | label ...: | +| test.cpp:23:3:23:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:23:3:23:10 | goto ... | l1 | test.cpp:16:1:16:3 | label ...: | label ...: | +| test.cpp:28:3:28:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:28:3:28:10 | goto ... | l1 | test.cpp:27:1:27:3 | label ...: | label ...: | diff --git a/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql new file mode 100644 index 0000000000..89768a3022 --- /dev/null +++ b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition + +class TestFileQuery extends GotoStatementConditionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/gotostatementcondition/test.cpp b/cpp/common/test/rules/gotostatementcondition/test.cpp new file mode 100644 index 0000000000..5854b21983 --- /dev/null +++ b/cpp/common/test/rules/gotostatementcondition/test.cpp @@ -0,0 +1,29 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void f1(int p1) { + +l1: + if (p1) { + goto l2; // COMPLIANT + } + goto l1; // NON_COMPLIANT + +l2:; +} + +void f2(int p1) { + +l1:; +l2: + if (p1) { + goto l3; // COMPLIANT + } + goto l2; // NON_COMPLIANT +l3: + goto l1; // NON_COMPLIANT +} + +void f3() { +l1: + goto l1; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.expected b/cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.expected new file mode 100644 index 0000000000..48547e3cca --- /dev/null +++ b/cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.expected @@ -0,0 +1 @@ +| test.cpp:6:3:6:14 | goto ... | Use of goto. | diff --git a/cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql b/cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql new file mode 100644 index 0000000000..1a117d5ddd --- /dev/null +++ b/cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotostatementshouldnotbeused.GotoStatementShouldNotBeUsed + +class TestFileQuery extends GotoStatementShouldNotBeUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/gotostatementshouldnotbeused/test.cpp b/cpp/common/test/rules/gotostatementshouldnotbeused/test.cpp new file mode 100644 index 0000000000..0763208625 --- /dev/null +++ b/cpp/common/test/rules/gotostatementshouldnotbeused/test.cpp @@ -0,0 +1,11 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void test_goto() { + int x = 1; + + goto label1; // NON_COMPLIANT + +label1: + + x = 2; +} \ No newline at end of file diff --git a/cpp/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql b/cpp/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql index 693dae8f57..a0d83a59a6 100644 --- a/cpp/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql +++ b/cpp/common/test/rules/guardaccesstobitfields/GuardAccessToBitFields.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.guardaccesstobitfields.GuardAccessToBitFields + +class TestFileQuery extends GuardAccessToBitFieldsSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.ql b/cpp/common/test/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.ql index 067c567389..d366b0eb79 100644 --- a/cpp/common/test/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.ql +++ b/cpp/common/test/rules/handleallexceptionsduringstartup/HandleAllExceptionsDuringStartup.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.handleallexceptionsduringstartup.HandleAllExceptionsDuringStartup + +class TestFileQuery extends HandleAllExceptionsDuringStartupSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected b/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected index e36c372d5b..060be8a69d 100644 --- a/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected +++ b/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.expected @@ -1,4 +1,4 @@ -| test.cpp:9:1:9:22 | #define MACROFIVE(X) #X | Macro definition uses the # or ## operator. | -| test.cpp:11:1:11:26 | #define MACROSIX(X,Y) X ## Y | Macro definition uses the # or ## operator. | -| test.cpp:13:1:13:29 | #define MACROSEVEN "##'" #"#" | Macro definition uses the # or ## operator. | -| test.cpp:15:1:15:28 | #define MACROEIGHT '##' #"#" | Macro definition uses the # or ## operator. | +| test.cpp:11:1:11:22 | #define MACROFIVE(X) #X | Macro definition uses the # or ## operator. | +| test.cpp:13:1:13:26 | #define MACROSIX(X,Y) X ## Y | Macro definition uses the # or ## operator. | +| test.cpp:15:1:15:29 | #define MACROSEVEN "##'" #"#" | Macro definition uses the # or ## operator. | +| test.cpp:17:1:17:28 | #define MACROEIGHT '##' #"#" | Macro definition uses the # or ## operator. | diff --git a/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql b/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql index d0ead0b289..a61dc7860a 100644 --- a/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql +++ b/cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.hashoperatorsused.HashOperatorsUsed + +class TestFileQuery extends HashOperatorsUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/hashoperatorsused/test.cpp b/cpp/common/test/rules/hashoperatorsused/test.cpp index 422bde164c..1ce4fd9a72 100644 --- a/cpp/common/test/rules/hashoperatorsused/test.cpp +++ b/cpp/common/test/rules/hashoperatorsused/test.cpp @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. #define MACROONE 1 // COMPLIANT #define MACROTWO '#' // COMPLIANT diff --git a/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.expected b/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.expected new file mode 100644 index 0000000000..dc762e5a2d --- /dev/null +++ b/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.expected @@ -0,0 +1 @@ +| test.cpp:20:8:20:9 | f1 | Declaration for member 'f1' hides non-overridable inherited member function $@ | test.cpp:7:8:7:9 | f1 | f1 | diff --git a/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.ql b/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.ql new file mode 100644 index 0000000000..30953eacf3 --- /dev/null +++ b/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.hiddeninheritednonoverridablememberfunction.HiddenInheritedNonOverridableMemberFunction + +class TestFileQuery extends HiddenInheritedNonOverridableMemberFunctionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/test.cpp b/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/test.cpp new file mode 100644 index 0000000000..c0904238c3 --- /dev/null +++ b/cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/test.cpp @@ -0,0 +1,94 @@ +struct S1 { + int i; +}; + +class C1 { +public: + void f1(int); + + virtual void f2(int); + virtual void f2(double); + virtual void f2(S1); + +private: + void f3(int); + void f4(int); +}; + +class C2 : public C1 { +public: + void f1(double); // NON_COMPLIANT + + void f2(double) override; // NON_COMPLIANT +}; + +class C3 : public C1 { +public: + void f2(char *); // NON_COMPLIANT +}; + +class C4 : public C1 { +public: + using C1::f1; + void f1(double); // COMPLIANT + + using C1::f2; + void f2(double) override; // COMPLIANT +}; + +namespace ns1 { +void f1(int); +} + +using ns1::f1; + +namespace ns1 { +void f1(double); // NON_COMPLIANT +} + +void f1() { + C2 l1; + l1.f1(0); // calls C2::f1(double) instead of C1::f1(int) + l1.f2(0); // calls C2::f2(double) instead of C1::f2(int) + // S1 s1; + // l1.f2(s1); Won't compile because there is no suitable conversion from S1 to + // double. + C1 &l2{l1}; + l2.f1(0); // calls C1::f1(int) + + C4 l3; + l3.f1(0); // calls C1::f1(int) + l3.f1(0.0); // calls C3::f1(double) + l3.f2(0); // calls C1::f2(int) + l3.f2(0.0); // calls C3::f2(double) + S1 l4; + l3.f2(l4); // calls C1:f2(S1) +} + +class C5 : public C1 { +public: + void f1(double); // COMPLIANT + using C1::f1; // order of using and f1 declaration is not relevant + + void f2(double) override; // COMPLIANT + using C1::f2; // order of using and f2 declaration is not relevant +}; + +void f2() { + C5 c5; + c5.f1(0); // calls C1::f1(int) + c5.f1(0.0); // calls C5::f1(double) + c5.f2(0); // calls C1::f2(int) + c5.f2(0.0); // calls C5::f2(double) +} + +class C6 : public C1 { +public: + C6 &operator=(const C6 &); // COMPLIANT +}; + +class C7 : public C1 { + void f3(int); // COMPLIANT + + void f4(int); // COMPLIANT +}; \ No newline at end of file diff --git a/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.expected b/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.expected new file mode 100644 index 0000000000..2e0e8809e8 --- /dev/null +++ b/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.expected @@ -0,0 +1,5 @@ +| test.cpp:22:8:22:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:9:16:9:17 | declaration of f2 | f2 | +| test.cpp:22:8:22:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:11:16:11:17 | declaration of f2 | f2 | +| test.cpp:27:8:27:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:9:16:9:17 | declaration of f2 | f2 | +| test.cpp:27:8:27:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:10:16:10:17 | declaration of f2 | f2 | +| test.cpp:27:8:27:9 | declaration of f2 | Declaration for member 'f2' hides overridable inherited member function $@ | test.cpp:11:16:11:17 | declaration of f2 | f2 | diff --git a/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.ql b/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.ql new file mode 100644 index 0000000000..072f672efb --- /dev/null +++ b/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.hiddeninheritedoverridablememberfunction.HiddenInheritedOverridableMemberFunction + +class TestFileQuery extends HiddenInheritedOverridableMemberFunctionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/test.cpp b/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/test.cpp new file mode 100644 index 0000000000..c0904238c3 --- /dev/null +++ b/cpp/common/test/rules/hiddeninheritedoverridablememberfunction/test.cpp @@ -0,0 +1,94 @@ +struct S1 { + int i; +}; + +class C1 { +public: + void f1(int); + + virtual void f2(int); + virtual void f2(double); + virtual void f2(S1); + +private: + void f3(int); + void f4(int); +}; + +class C2 : public C1 { +public: + void f1(double); // NON_COMPLIANT + + void f2(double) override; // NON_COMPLIANT +}; + +class C3 : public C1 { +public: + void f2(char *); // NON_COMPLIANT +}; + +class C4 : public C1 { +public: + using C1::f1; + void f1(double); // COMPLIANT + + using C1::f2; + void f2(double) override; // COMPLIANT +}; + +namespace ns1 { +void f1(int); +} + +using ns1::f1; + +namespace ns1 { +void f1(double); // NON_COMPLIANT +} + +void f1() { + C2 l1; + l1.f1(0); // calls C2::f1(double) instead of C1::f1(int) + l1.f2(0); // calls C2::f2(double) instead of C1::f2(int) + // S1 s1; + // l1.f2(s1); Won't compile because there is no suitable conversion from S1 to + // double. + C1 &l2{l1}; + l2.f1(0); // calls C1::f1(int) + + C4 l3; + l3.f1(0); // calls C1::f1(int) + l3.f1(0.0); // calls C3::f1(double) + l3.f2(0); // calls C1::f2(int) + l3.f2(0.0); // calls C3::f2(double) + S1 l4; + l3.f2(l4); // calls C1:f2(S1) +} + +class C5 : public C1 { +public: + void f1(double); // COMPLIANT + using C1::f1; // order of using and f1 declaration is not relevant + + void f2(double) override; // COMPLIANT + using C1::f2; // order of using and f2 declaration is not relevant +}; + +void f2() { + C5 c5; + c5.f1(0); // calls C1::f1(int) + c5.f1(0.0); // calls C5::f1(double) + c5.f2(0); // calls C1::f2(int) + c5.f2(0.0); // calls C5::f2(double) +} + +class C6 : public C1 { +public: + C6 &operator=(const C6 &); // COMPLIANT +}; + +class C7 : public C1 { + void f3(int); // COMPLIANT + + void f4(int); // COMPLIANT +}; \ No newline at end of file diff --git a/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected b/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected index 613dd93f7b..fd657590ef 100644 --- a/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected +++ b/cpp/common/test/rules/identifierhidden/IdentifierHidden.expected @@ -1,7 +1,17 @@ | test.cpp:4:5:4:7 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | | test.cpp:8:5:8:7 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | -| test.cpp:11:5:11:7 | id1 | Variable is hiding variable $@. | test.cpp:8:5:8:7 | id1 | id1 | | test.cpp:20:7:20:9 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | | test.cpp:23:13:23:15 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | | test.cpp:26:12:26:14 | id1 | Variable is hiding variable $@. | test.cpp:1:5:1:7 | id1 | id1 | | test.cpp:27:14:27:16 | id1 | Variable is hiding variable $@. | test.cpp:26:12:26:14 | id1 | id1 | +| test.cpp:65:11:65:11 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:67:9:67:9 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:70:12:70:12 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:75:16:75:16 | i | Variable is hiding variable $@. | test.cpp:61:7:61:7 | i | i | +| test.cpp:86:9:86:9 | b | Variable is hiding variable $@. | test.cpp:80:11:80:11 | b | b | +| test.cpp:94:9:94:17 | globalvar | Variable is hiding variable $@. | test.cpp:91:5:91:13 | globalvar | globalvar | +| test.cpp:113:11:113:11 | b | Variable is hiding variable $@. | test.cpp:107:13:107:13 | b | b | +| test.cpp:122:9:122:10 | a1 | Variable is hiding variable $@. | test.cpp:120:14:120:15 | a1 | a1 | +| test.cpp:127:9:127:10 | a2 | Variable is hiding variable $@. | test.cpp:125:20:125:21 | a2 | a2 | +| test.cpp:132:9:132:10 | a3 | Variable is hiding variable $@. | test.cpp:130:17:130:18 | a3 | a3 | +| test.cpp:144:9:144:10 | a5 | Variable is hiding variable $@. | test.cpp:142:13:142:14 | a5 | a5 | diff --git a/cpp/common/test/rules/identifierhidden/IdentifierHidden.ql b/cpp/common/test/rules/identifierhidden/IdentifierHidden.ql index 62abdd2163..ba13b28bd4 100644 --- a/cpp/common/test/rules/identifierhidden/IdentifierHidden.ql +++ b/cpp/common/test/rules/identifierhidden/IdentifierHidden.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.identifierhidden.IdentifierHidden + +class TestFileQuery extends IdentifierHiddenSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/identifierhidden/test.cpp b/cpp/common/test/rules/identifierhidden/test.cpp index 233ae4c004..427afe15d9 100644 --- a/cpp/common/test/rules/identifierhidden/test.cpp +++ b/cpp/common/test/rules/identifierhidden/test.cpp @@ -27,4 +27,129 @@ void f3() { for (int id1; id1 < 1; id1++) { } // NON_COMPLIANT } +} + +template constexpr bool foo = false; // COMPLIANT + +namespace { +template bool foo = true; // COMPLIANT - omit variable templates +} + +template constexpr T foo1 = T(1.1L); + +template T f(T r) { + T v = foo1 * r * r; // COMPLIANT + T v1 = foo1 * r * r; // COMPLIANT +} + +void test_scope_order() { + { + { + int i; // COMPLIANT + } + int i; // COMPLIANT + } + + for (int i = 0; i < 10; i++) { // COMPLIANT + } + + try { + + } catch (int i) { // COMPLIANT + } + + int i; // COMPLIANT + + { + { + int i; // NON_COMPLIANT + } + int i; // NON_COMPLIANT + } + + for (int i = 0; i < 10; i++) { // NON_COMPLIANT + } + + try { + + } catch (int i) { // NON_COMPLIANT + } +} + +void f4() { + int a1, b; + auto lambda1 = [a1]() { + int b = 10; // COMPLIANT - exception - non captured variable b + }; + + auto lambda2 = [b]() { + int b = 10; // NON_COMPLIANT - not an exception - captured + // variable b + }; +} + +int globalvar = 0; +int f5() { + auto lambda_with_shadowing = []() { + int globalvar = 1; // NON_COMPLIANT - not an exception - not captured but + // still accessible + return globalvar + globalvar; + }; + + auto lambda_without_shadowing = []() { return globalvar + globalvar; }; + + return lambda_with_shadowing(); +} + +void f6(int p) { + // Introduce a nested scope to test scope comparison. + if (p != 0) { + int a1, b; + auto lambda1 = [a1]() { + int b = 10; // COMPLIANT - exception - non captured variable b + }; + + auto lambda2 = [b]() { + int b = 10; // NON_COMPLIANT - not an exception - captured + // variable b + }; + } +} + +void f7() { + static int a1; + auto lambda1 = []() { + int a1 = 10; // NON_COMPLIANT - Lambda can access static variable. + }; + + thread_local int a2; + auto lambda2 = []() { + int a2 = 10; // NON_COMPLIANT - Lambda can access thread local variable. + }; + + constexpr int a3 = 10; + auto lambda3 = []() { + int a3 = a3 + 1; // NON_COMPLIANT - Lambda can access const + // expression without mutable members. + }; + + const int &a4 = a3; + auto lambda4 = []() { + int a4 = a4 + 1; // NON_COMPLIANT[FALSE_NEGATIVE] - Lambda can access + // reference initialized with constant expression. + }; + + const int a5 = 10; + auto lambda5 = []() { + int a5 = a5 + 1; // NON_COMPLIANT - Lambda can access const + // non-volatile integral or enumeration type initialized + // with constant expression. + }; + + volatile const int a6 = 10; + auto lambda6 = []() { + int a6 = + a6 + 1; // COMPLIANT - Lambda cannot access const volatile integral or + // enumeration type initialized with constant expression. + }; } \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.expected b/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.expected similarity index 100% rename from cpp/autosar/test/rules/M3-2-4/IdentifierWithExternalLinkageShallHaveOneDefinition.expected rename to cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.expected diff --git a/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql b/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql new file mode 100644 index 0000000000..d50eb53652 --- /dev/null +++ b/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/IdentifierWithExternalLinkageOneDefinitionShared.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.identifierwithexternallinkageonedefinitionshared.IdentifierWithExternalLinkageOneDefinitionShared + +class TestFileQuery extends IdentifierWithExternalLinkageOneDefinitionSharedSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/test.cpp b/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/test.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/autosar/test/rules/M3-2-4/test1.cpp b/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/test1.cpp similarity index 100% rename from cpp/autosar/test/rules/M3-2-4/test1.cpp rename to cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/test1.cpp diff --git a/cpp/autosar/test/rules/M3-2-4/test2.cpp b/cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/test2.cpp similarity index 100% rename from cpp/autosar/test/rules/M3-2-4/test2.cpp rename to cpp/common/test/rules/identifierwithexternallinkageonedefinitionshared/test2.cpp diff --git a/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected new file mode 100644 index 0000000000..ffe7e08489 --- /dev/null +++ b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected @@ -0,0 +1,3 @@ +| test.cpp:21:3:25:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.cpp:21:3:25:3 | if (...) ... | `if...else` | +| test.cpp:41:5:45:5 | if (...) ... | The $@ construct does not terminate with else statement. | test.cpp:41:5:45:5 | if (...) ... | `if...else` | +| test.cpp:55:3:65:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.cpp:55:3:65:3 | if (...) ... | `if...else` | diff --git a/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql new file mode 100644 index 0000000000..acdd497be7 --- /dev/null +++ b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct + +class TestFileQuery extends IfElseTerminationConstructSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/ifelseterminationconstruct/test.cpp b/cpp/common/test/rules/ifelseterminationconstruct/test.cpp new file mode 100644 index 0000000000..c3433bee1a --- /dev/null +++ b/cpp/common/test/rules/ifelseterminationconstruct/test.cpp @@ -0,0 +1,66 @@ + +void test_ifelse_valid(int expression) { + int i = 3; + int j = 4; + int k; + if (expression > 0) { // GOOD + k = i + j; + } else if (expression < 100) { + k = i - j; + } else { + k = j * j; + } +} +void test_ifelse_mix_validity(int expression) { + int i = 4; + int j = 7; + int k; + if (expression > 0) { // GOOD + k = i * i; + } + if (expression > 10) { // BAD + k = i + j; + } else if (expression < 0) { + k = i * 2; + } +} + +void test_ifelse_nested_invalid(int expression) { + int i = 5; + int j = 7; + int k; + + if (expression > 0) { // GOOD + k = i * i * i; + } else { + k = i * j; + } + if (expression > 10) { // GOOD + k = i; + } else if (expression < 0) { + if (expression < -10) { // BAD + k = 5 + j; + } else if (expression < -20) { + k = i * 3; + } + } else { + k = 3; + } +} + +void test_ifelse_nested_valid(int expression) { + int i = 3; + int j = 1; + int k; + if (expression > 10) { // BAD + k = i + j; + } else if (expression < 0) { + if (i > 3) { // GOOD + k = j; + } else if (i < 10) { + k = i % 3; + } else { + i = i % 2; + } + } +} diff --git a/cpp/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql b/cpp/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql index 2fcfddeca9..13b07b4e90 100644 --- a/cpp/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql +++ b/cpp/common/test/rules/includeguardsnotused/IncludeGuardsNotUsed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.includeguardsnotused.IncludeGuardsNotUsed + +class TestFileQuery extends IncludeGuardsNotUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected b/cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected new file mode 100644 index 0000000000..d7a7659f07 --- /dev/null +++ b/cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected @@ -0,0 +1,20 @@ +| arrays.cpp:11:20:11:21 | wa | 'wa' may leak information from {elements of a[...] (arrays.cpp:7)}. Path: wa (arrays.cpp:11) --> & ... (arrays.cpp:12) | +| arrays.cpp:33:22:33:23 | wa | 'wa' may leak information from {elements of elements of a[...][...] (arrays.cpp:29)}. Path: wa (arrays.cpp:33) --> & ... (arrays.cpp:34) | +| arrays.cpp:57:22:57:23 | wa | 'wa' may leak information from {WithPointer (arrays.cpp:52)}. Path: wa (arrays.cpp:57) --> & ... (arrays.cpp:59) | +| inheritance.cpp:19:14:19:14 | s | 's' may leak information from {i (inheritance.cpp:7)}. Path: s (inheritance.cpp:19) --> & ... (inheritance.cpp:21) | +| inheritance.cpp:32:14:32:14 | s | 's' may leak information from {0 to 4 bytes of padding in ptrDerived (inheritance.cpp:14)}. Path: s (inheritance.cpp:32) --> & ... (inheritance.cpp:35) | +| interprocedural.cpp:37:9:37:9 | p | 'p' may leak information from {y (interprocedural.cpp:8)}. Path: p (interprocedural.cpp:37) --> past assign_x (interprocedural.cpp:32) --> & ... (interprocedural.cpp:39) | +| interprocedural.cpp:104:9:104:9 | p | 'p' may leak information from {x (interprocedural.cpp:7), y (interprocedural.cpp:8)}. Path: p (interprocedural.cpp:104) --> overwrite_after_leak(...) (interprocedural.cpp:96) --> p (interprocedural.cpp:97) | +| multilayer.cpp:16:10:16:10 | s | 's' may leak information from {b (multilayer.cpp:12)}. Path: s (multilayer.cpp:16) --> & ... (multilayer.cpp:18) | +| multilayer.cpp:29:10:29:10 | s | 's' may leak information from {b (multilayer.cpp:12), x (multilayer.cpp:7)}. Path: s (multilayer.cpp:29) --> & ... (multilayer.cpp:30) | +| multilayer.cpp:34:8:34:8 | s | 's' may leak information from {intx (multilayer.cpp:6)}. Path: s (multilayer.cpp:34) --> & ... (multilayer.cpp:35) | +| test.cpp:12:12:12:12 | s | 's' may leak information from {y (test.cpp:8)}. Path: s (test.cpp:12) --> & ... (test.cpp:14) | +| test.cpp:18:12:18:12 | s | 's' may leak information from {x (test.cpp:7)}. Path: s (test.cpp:18) --> & ... (test.cpp:20) | +| test.cpp:24:12:24:12 | s | 's' may leak information from {x (test.cpp:7), y (test.cpp:8)}. Path: s (test.cpp:24) --> & ... (test.cpp:25) | +| test.cpp:36:12:36:12 | s | 's' may leak information from {y (test.cpp:8)}. Path: s (test.cpp:36) --> & ... (test.cpp:38) | +| test.cpp:43:12:43:12 | s | 's' may leak information from {x (test.cpp:7)}. Path: s (test.cpp:43) --> & ... (test.cpp:47) | +| test.cpp:58:12:58:12 | s | 's' may leak information from {x (test.cpp:7), y (test.cpp:8)}. Path: s (test.cpp:58) --> & ... (test.cpp:59) | +| test.cpp:64:12:64:12 | s | 's' may leak information from {y (test.cpp:8)}. Path: s (test.cpp:64) --> & ... (test.cpp:66) | +| test.cpp:110:16:110:16 | s | 's' may leak information from {buf (test.cpp:92)}. Path: s (test.cpp:110) --> & ... (test.cpp:113) | +| test.cpp:126:12:126:12 | s | 's' may leak information from {x (test.cpp:7), y (test.cpp:8)}. Path: s (test.cpp:126) --> & ... (test.cpp:130) | +| test.cpp:155:22:155:22 | s | 's' may leak information from {2 to 2 bytes of padding in has_padding (test.cpp:149)}. Path: s (test.cpp:155) --> & ... (test.cpp:158) | diff --git a/cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql b/cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql new file mode 100644 index 0000000000..3393d015c3 --- /dev/null +++ b/cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.informationleakageacrossboundaries.InformationLeakageAcrossBoundaries + +class TestFileQuery extends InformationLeakageAcrossBoundariesSharedQuery, TestQuery { } diff --git a/cpp/cert/test/rules/DCL55-CPP/arrays.cpp b/cpp/common/test/rules/informationleakageacrossboundaries/arrays.cpp similarity index 100% rename from cpp/cert/test/rules/DCL55-CPP/arrays.cpp rename to cpp/common/test/rules/informationleakageacrossboundaries/arrays.cpp diff --git a/cpp/cert/test/rules/DCL55-CPP/inheritance.cpp b/cpp/common/test/rules/informationleakageacrossboundaries/inheritance.cpp similarity index 100% rename from cpp/cert/test/rules/DCL55-CPP/inheritance.cpp rename to cpp/common/test/rules/informationleakageacrossboundaries/inheritance.cpp diff --git a/cpp/cert/test/rules/DCL55-CPP/interprocedural.cpp b/cpp/common/test/rules/informationleakageacrossboundaries/interprocedural.cpp similarity index 98% rename from cpp/cert/test/rules/DCL55-CPP/interprocedural.cpp rename to cpp/common/test/rules/informationleakageacrossboundaries/interprocedural.cpp index 15e8b81682..016d40baeb 100644 --- a/cpp/cert/test/rules/DCL55-CPP/interprocedural.cpp +++ b/cpp/common/test/rules/informationleakageacrossboundaries/interprocedural.cpp @@ -1,5 +1,5 @@ +#include #include -#include unsigned long copy_to_user(void *to, const void *from, unsigned long n); diff --git a/cpp/cert/test/rules/DCL55-CPP/multilayer.cpp b/cpp/common/test/rules/informationleakageacrossboundaries/multilayer.cpp similarity index 96% rename from cpp/cert/test/rules/DCL55-CPP/multilayer.cpp rename to cpp/common/test/rules/informationleakageacrossboundaries/multilayer.cpp index 2298895de1..c5a9d29e0f 100644 --- a/cpp/cert/test/rules/DCL55-CPP/multilayer.cpp +++ b/cpp/common/test/rules/informationleakageacrossboundaries/multilayer.cpp @@ -1,5 +1,5 @@ +#include #include -#include unsigned long copy_to_user(void *to, const void *from, unsigned long n); diff --git a/cpp/common/test/rules/informationleakageacrossboundaries/test.cpp b/cpp/common/test/rules/informationleakageacrossboundaries/test.cpp new file mode 100644 index 0000000000..9a5fe40ef8 --- /dev/null +++ b/cpp/common/test/rules/informationleakageacrossboundaries/test.cpp @@ -0,0 +1,165 @@ +#include +#include + +unsigned long copy_to_user(void *to, const void *from, unsigned long n); + +typedef struct { + int x; + int y; +} MyStruct; + +void forget_y() { + MyStruct s; + s.x = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) +} + +void forget_x() { + MyStruct s; + s.y = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) +} + +void forget_both() { + MyStruct s; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x, y) +} + +void init_both() { + MyStruct s; + s.x = 1; + s.y = 1; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void init_after() { + MyStruct s; + s.x = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT + s.y = 1; +} + +void init_other() { + MyStruct s, t; + s.y = 1; + t.x = 1; + t.y = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (x) + copy_to_user(0, &t, sizeof t); // COMPLIANT +} + +void zero_memory() { + MyStruct s; + std::memset(&s, 0, sizeof s); + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void zero_memory_after() { + MyStruct s; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT + std::memset(&s, 0, sizeof s); +} + +void zero_field() { + MyStruct s; + std::memset(&s.x, 0, sizeof s.x); + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (y) +} + +void overwrite_with_zeroed() { + MyStruct s, t; + std::memset(&t, 0, sizeof t); + s = t; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void overwrite_struct_with_uninit() { + MyStruct s, t; + s = t; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT[FALSE NEGATIVE] +} + +void overwrite_field_with_uninit() { + MyStruct s; + int x; + s.x = x; + s.y = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT[FALSE NEGATIVE] +} + +typedef struct { + size_t length; + char buf[128]; +} PascalString; + +void zero_array() { + PascalString s; + std::memset(s.buf, 0, sizeof s.buf); + s.length = 0; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void zero_array_by_ref() { + PascalString s; + std::memset(&s.buf, 0, sizeof s.buf); + s.length = 0; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +void use_strcpy() { + PascalString s; + strcpy(s.buf, "Hello, World"); // does not zero rest of s.buf + s.length = std::strlen(s.buf); + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT (buf) +} + +void *malloc(size_t size); + +void heap_memory() { + MyStruct *s; + s = (MyStruct *)malloc(sizeof(*s)); + s->x = 1; + copy_to_user(0, s, sizeof(*s)); // NON_COMPLIANT[FALSE NEGATIVE] +} + +void conditional_memset(int b) { + MyStruct s; + if (b) { + std::memset(&s, 0, sizeof s); + } + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT +} + +void memset_field() { + MyStruct s; + std::memset(&s.x, 0, sizeof(s.x)); + s.y = 1; + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +const static int one = 1; +void zero_if_true() { + MyStruct s; + if (one) { + std::memset(&s, 0, sizeof s); + } + copy_to_user(0, &s, sizeof s); // COMPLIANT +} + +struct has_padding { + short s; + int i; +}; + +void forget_padding() { + struct has_padding s; + s.s = 1; + s.i = 1; + copy_to_user(0, &s, sizeof s); // NON_COMPLIANT +} + +void remember_padding() { + struct has_padding s; + std::memset(&s, 0, sizeof s); + copy_to_user(0, &s, sizeof s); // COMPLIANT +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.expected b/cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.expected similarity index 100% rename from cpp/autosar/test/rules/A12-1-1/ExplicitConstructorBaseClassInitialization.expected rename to cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.expected diff --git a/cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.ql b/cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.ql new file mode 100644 index 0000000000..89f720b125 --- /dev/null +++ b/cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.initializeallvirtualbaseclasses.InitializeAllVirtualBaseClasses + +class TestFileQuery extends InitializeAllVirtualBaseClassesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/initializeallvirtualbaseclasses/test.cpp b/cpp/common/test/rules/initializeallvirtualbaseclasses/test.cpp new file mode 100644 index 0000000000..8e6b318b19 --- /dev/null +++ b/cpp/common/test/rules/initializeallvirtualbaseclasses/test.cpp @@ -0,0 +1,73 @@ +class Base {}; +class Derived : public Base { +public: + Derived() {} // NON_COMPLIANT - does not call Base() + Derived(int i) : Base() {} // COMPLIANT + Derived(int i, int j); // IGNORED - not defined, so we don't know +}; + +class Derived1 : public Base { +public: + Derived1() = default; // COMPLIANT - `default` does not have explicit + // initializers +}; + +class VirtualBase {}; + +class Derived2 : virtual public VirtualBase {}; + +class Derived3 : virtual public VirtualBase {}; + +class Derived4 : public Derived2, public Derived3 { +public: + Derived4() {} // NON_COMPLIANT - does not call Derived2(), Derived3() or + // VirtualBase() + Derived4(int i) // NON_COMPLIANT - does not call VirtualBase() + : Derived2(), Derived3() {} + Derived4(int i, int j) // COMPLIANT - calls VirtualBase() + : Derived2(), Derived3(), VirtualBase() {} +}; + +class NonTrivialBase { +public: + NonTrivialBase() = default; + NonTrivialBase(int i){}; + NonTrivialBase(int i, int j){}; +}; + +class Derived5 : public NonTrivialBase { +public: + Derived5() {} // NON_COMPLIANT - does not call NonTrivialBase() + Derived5(int i) : NonTrivialBase(i) {} // COMPLIANT + Derived5(int i, int j) : NonTrivialBase(i, j) {} // COMPLIANT +}; + +class MultipleInheritenceBase {}; + +class Child1 : public MultipleInheritenceBase {}; + +class Child2 : public MultipleInheritenceBase {}; + +class GrandChild : public Child1, public Child2 { + // no need to initialize MultipleInheritenceBase + GrandChild() : Child1(), Child2() {} // COMPLIANT +}; + +class Base2 {}; + +class Derived6 : public Base2 { +public: + Derived6() : b() {} // NON_COMPLIANT + +private: + Base2 b; +}; + +class Base3 {}; + +class Derived7 final : public Base3 { +public: + Derived7() = delete; // COMPLIANT + Derived7(const Derived7 &) = delete; // COMPLIANT + Derived7(Derived7 &&) = delete; // COMPLIANT +}; \ No newline at end of file diff --git a/cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.expected b/cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.expected similarity index 100% rename from cpp/autosar/test/rules/A8-5-4/ConfusingUseOfInitializerListConstructors.expected rename to cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.expected diff --git a/cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.ql b/cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.ql new file mode 100644 index 0000000000..a2b023a3dd --- /dev/null +++ b/cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.initializerlistconstructoristheonlyconstructor.InitializerListConstructorIsTheOnlyConstructor + +class TestFileQuery extends InitializerListConstructorIsTheOnlyConstructorSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A8-5-4/test.cpp b/cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A8-5-4/test.cpp rename to cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/test.cpp diff --git a/cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected b/cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected new file mode 100644 index 0000000000..b183ca7c42 --- /dev/null +++ b/cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.expected @@ -0,0 +1,11 @@ +| test.cpp:21:14:21:19 | tmpvar | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:13:12:13:17 | call to getenv | call to getenv | test.cpp:17:13:17:18 | call to getenv | call to getenv | +| test.cpp:134:14:134:17 | temp | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:130:12:130:17 | call to getenv | call to getenv | test.cpp:131:11:131:16 | call to getenv | call to getenv | +| test.cpp:134:20:134:22 | tmp | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:131:11:131:16 | call to getenv | call to getenv | test.cpp:130:12:130:17 | call to getenv | call to getenv | +| test.cpp:165:14:165:26 | tmpvar_global | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:157:19:157:24 | call to getenv | call to getenv | test.cpp:161:20:161:25 | call to getenv | call to getenv | +| test.cpp:188:18:188:18 | r | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:185:7:185:15 | call to setlocale | call to setlocale | test.cpp:187:8:187:17 | call to localeconv | call to localeconv | +| test.cpp:208:10:208:15 | tmpvar | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:202:12:202:17 | call to getenv | call to getenv | test.cpp:206:3:206:8 | call to f11fun | call to f11fun | +| test.cpp:216:16:216:17 | r1 | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:214:14:214:18 | call to ctime | call to ctime | test.cpp:215:3:215:9 | call to asctime | call to asctime | +| test.cpp:226:16:226:17 | r1 | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:222:14:222:18 | call to ctime | call to ctime | test.cpp:225:14:225:20 | call to asctime | call to asctime | +| test.cpp:231:16:231:17 | r2 | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:225:14:225:20 | call to asctime | call to asctime | test.cpp:229:8:229:12 | call to ctime | call to ctime | +| test.cpp:240:16:240:17 | r1 | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:236:19:236:27 | call to localtime | call to localtime | test.cpp:239:19:239:24 | call to gmtime | call to gmtime | +| test.cpp:245:16:245:17 | r2 | This pointer was returned by a $@ and may have been overwritten by the subsequent $@. | test.cpp:239:19:239:24 | call to gmtime | call to gmtime | test.cpp:243:8:243:16 | call to localtime | call to localtime | diff --git a/cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql b/cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql new file mode 100644 index 0000000000..b82c43333a --- /dev/null +++ b/cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.invalidatedenvstringpointers.InvalidatedEnvStringPointers + +class TestFileQuery extends InvalidatedEnvStringPointersSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/invalidatedenvstringpointers/test.cpp b/cpp/common/test/rules/invalidatedenvstringpointers/test.cpp new file mode 100644 index 0000000000..920d97c657 --- /dev/null +++ b/cpp/common/test/rules/invalidatedenvstringpointers/test.cpp @@ -0,0 +1,246 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +#include +#include +#include +#include + +void f1(void) { + char *tmpvar; + char *tempvar; + + tmpvar = getenv("TMP"); + if (!tmpvar) { + /* Handle error */ + } + tempvar = getenv("TEMP"); + if (!tempvar) { + /* Handle error */ + } + if (strcmp(tmpvar, tempvar) == 0) { // NON_COMPLIANT + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } +} + +void f2(void) { + char *tmpvar; + char *tempvar; + + const char *temp = getenv("TMP"); + if (temp != NULL) { + tmpvar = (char *)malloc(strlen(temp) + 1); + if (tmpvar != NULL) { + strcpy(tmpvar, temp); + } else { + /* Handle error */ + } + } else { + /* Handle error */ + } + + temp = getenv("TEMP"); + if (temp != NULL) { + tempvar = (char *)malloc(strlen(temp) + 1); + if (tempvar != NULL) { + strcpy(tempvar, temp); + } else { + /* Handle error */ + } + } else { + /* Handle error */ + } + + if (strcmp(tmpvar, tempvar) == 0) { // COMPLIANT + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } + free(tmpvar); + free(tempvar); +} + +#define __STDC_WANT_LIB_EXT1__ 1 + +void f3(void) { + char *tmpvar; + char *tempvar; + + const char *temp = getenv("TMP"); + if (temp != NULL) { + tmpvar = strdup(temp); + if (tmpvar == NULL) { + /* Handle error */ + } + } else { + /* Handle error */ + } + + temp = getenv("TEMP"); + if (temp != NULL) { + tempvar = strdup(temp); + if (tempvar == NULL) { + /* Handle error */ + } + } else { + /* Handle error */ + } + + if (strcmp(tmpvar, tempvar) == 0) { // COMPLIANT + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } + free(tmpvar); + tmpvar = NULL; + free(tempvar); + tempvar = NULL; +} + +void f4(void) { + char *temp = getenv("VAR1"); + printf(temp); + temp = getenv("VAR2"); + printf(temp); // COMPLIANT +} + +void f5(void) { + const char *envVars[] = { + "v1", + "v2", + "v3", + }; + for (int i = 0; i < 3; i++) { + char *temp = getenv(envVars[i]); + printf(temp); // COMPLIANT + } +} + +void f5b(void) { + const char *envVars[] = { + "v1", + "v2", + "v3", + }; + char *temp; + char *tmp; + for (int i = 0; i < 3; i++) { + temp = getenv(envVars[i]); + tmp = getenv(envVars[i]); + } + + if (strcmp(temp, tmp) == 0) { // NON_COMPLIANT + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } +} + +void f6(void) { + const char *envVars[] = { + "v1", + "v2", + "v3", + }; + char *temp[3]; + for (int i = 0; i < 3; i++) { + temp[i] = getenv(envVars[i]); + } + printf(temp[0]); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +char *tmpvar_global; +char *tempvar_global; +void f7(void) { + tmpvar_global = getenv("TMP"); + if (!tmpvar_global) { + /* Handle error */ + } + tempvar_global = getenv("TEMP"); + if (!tempvar_global) { + /* Handle error */ + } + if (strcmp(tmpvar_global, tempvar_global) == 0) { // NON_COMPLIANT + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } +} + +extern void f8fun(); +void f8(void) { + char *temp = getenv("VAR1"); + printf(temp); + f8fun(); // this function might call getenv() + temp = getenv("VAR2"); + printf(temp); // NON_COMPLIANT[FALSE_NEGATIVE] +} + +void f9(void) { + const char *r; + struct lconv *lc; + char c[128]; + r = setlocale(LC_ALL, "ja_JP.UTF-8"); + strcpy(c, r); + lc = localeconv(); + printf("%s\n", r); // NON_COMPLIANT + printf("%s\n", c); // COMPLIANT + printf("%s\n", lc->currency_symbol); // COMPLIANT +} + +void f10(void) { + struct tm tm = *localtime(&(time_t){time(NULL)}); + printf("%s", asctime(&tm)); // COMPLIANT +} + +void f11fun(void) { char *tempvar = getenv("TEMP"); } +void f11(void) { + char *tmpvar; + + tmpvar = getenv("TMP"); + if (!tmpvar) { + /* Handle error */ + } + f11fun(); + + printf(tmpvar); // NON_COMPLIANT +} + +void f12(void) { + time_t rawtime; + time(&rawtime); + char *r1 = ctime(&rawtime); + asctime(localtime(&rawtime)); + printf("%s", r1); // NON_COMPLIANT +} + +void f13(void) { + time_t rawtime; + time(&rawtime); + char *r1 = ctime(&rawtime); + printf("%s", r1); // COMPLIANT + + char *r2 = asctime(localtime(&rawtime)); + printf("%s", r1); // NON_COMPLIANT + printf("%s", r2); // COMPLIANT + + r1 = ctime(&rawtime); + printf("%s", r1); // COMPLIANT + printf("%s", r2); // NON_COMPLIANT +} + +void f14(void) { + time_t rawtime; + struct tm *r1 = localtime(&rawtime); + printf("%d", r1->tm_year); // COMPLIANT + + struct tm *r2 = gmtime(&rawtime); + printf("%s", r1->tm_year); // NON_COMPLIANT + printf("%s", r2->tm_year); // COMPLIANT + + r1 = localtime(&rawtime); + printf("%s", r1->tm_year); // COMPLIANT + printf("%s", r2->tm_year); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected b/cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected new file mode 100644 index 0000000000..9061fcfbc4 --- /dev/null +++ b/cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.expected @@ -0,0 +1,2 @@ +| test.cpp:15:19:15:24 | call to getenv | The value of variable $@ might become invalid after a subsequent call to function `getenv`. | test.cpp:12:7:12:19 | tmpvar_global | tmpvar_global | +| test.cpp:18:20:18:25 | call to getenv | The value of variable $@ might become invalid after a subsequent call to function `getenv`. | test.cpp:9:9:9:20 | tmpvar_field | tmpvar_field | diff --git a/cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql b/cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql new file mode 100644 index 0000000000..7a56af210d --- /dev/null +++ b/cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.invalidatedenvstringpointerswarn.InvalidatedEnvStringPointersWarn + +class TestFileQuery extends InvalidatedEnvStringPointersWarnSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/invalidatedenvstringpointerswarn/test.cpp b/cpp/common/test/rules/invalidatedenvstringpointerswarn/test.cpp new file mode 100644 index 0000000000..5001e538a1 --- /dev/null +++ b/cpp/common/test/rules/invalidatedenvstringpointerswarn/test.cpp @@ -0,0 +1,21 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include +#include +#include +#include + +struct test_struct { + char *tmpvar_field; +}; + +char *tmpvar_global; + +void f1(void) { + tmpvar_global = getenv("TMP"); // NON_COMPLIANT + + struct test_struct s; + s.tmpvar_field = getenv("TEMP"); // NON_COMPLIANT + + char *tmpvar_local = getenv("TEMP"); // COMPLIANT +} diff --git a/cpp/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql b/cpp/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql index 164aa6ed96..c1f22c408a 100644 --- a/cpp/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql +++ b/cpp/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.iofstreammissingpositioning.IOFstreamMissingPositioning + +class TestFileQuery extends IOFstreamMissingPositioningSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/joinablethreadcopiedordestroyed/JoinableThreadCopiedOrDestroyed.ql b/cpp/common/test/rules/joinablethreadcopiedordestroyed/JoinableThreadCopiedOrDestroyed.ql index 0125aa7405..affaeef13d 100644 --- a/cpp/common/test/rules/joinablethreadcopiedordestroyed/JoinableThreadCopiedOrDestroyed.ql +++ b/cpp/common/test/rules/joinablethreadcopiedordestroyed/JoinableThreadCopiedOrDestroyed.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.joinablethreadcopiedordestroyed.JoinableThreadCopiedOrDestroyed + +class TestFileQuery extends JoinableThreadCopiedOrDestroyedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.expected b/cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.expected similarity index 100% rename from cpp/autosar/test/rules/A2-7-1/SingleLineCommentEndsWithSlash.expected rename to cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.expected diff --git a/cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.ql b/cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.ql new file mode 100644 index 0000000000..55803eab88 --- /dev/null +++ b/cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.linesplicingusedincomments.LineSplicingUsedInComments + +class TestFileQuery extends LineSplicingUsedInCommentsSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A2-7-1/test.cpp b/cpp/common/test/rules/linesplicingusedincomments/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A2-7-1/test.cpp rename to cpp/common/test/rules/linesplicingusedincomments/test.cpp diff --git a/cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.expected b/cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.expected similarity index 100% rename from cpp/autosar/test/rules/M6-3-1/LoopCompoundCondition.expected rename to cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.expected diff --git a/cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.ql b/cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.ql new file mode 100644 index 0000000000..3961d76d15 --- /dev/null +++ b/cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.loopcompoundcondition.LoopCompoundCondition + +class TestFileQuery extends LoopCompoundConditionSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M6-3-1/test.cpp b/cpp/common/test/rules/loopcompoundcondition/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M6-3-1/test.cpp rename to cpp/common/test/rules/loopcompoundcondition/test.cpp diff --git a/cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.expected b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.expected new file mode 100644 index 0000000000..545b6d3441 --- /dev/null +++ b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.expected @@ -0,0 +1,16 @@ +| test.cpp:5:10:5:11 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:6:10:6:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:9:10:9:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:10:10:10:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:15:11:15:12 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:16:11:16:13 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:19:11:19:13 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:20:11:20:13 | 0 | Lowercase 'l' used as a literal suffix. | +| test.cpp:25:10:25:14 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:26:10:26:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:29:10:29:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:30:10:30:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:35:11:35:14 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:36:11:36:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:39:11:39:15 | 1 | Lowercase 'l' used as a literal suffix. | +| test.cpp:40:11:40:15 | 1 | Lowercase 'l' used as a literal suffix. | diff --git a/cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql new file mode 100644 index 0000000000..ab353ca8a9 --- /dev/null +++ b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.lowercaselstartsinliteralsuffix.LowercaseLStartsInLiteralSuffix + +class TestFileQuery extends LowercaseLStartsInLiteralSuffixSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/lowercaselstartsinliteralsuffix/README.md b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/README.md new file mode 100644 index 0000000000..b9aa3d6d8f --- /dev/null +++ b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/README.md @@ -0,0 +1 @@ +This test case was added to validate FP report [#319](https://github.com/github/codeql-coding-standards/issues/319) that occurs when this rule is run on a translation unit with language mode c++. \ No newline at end of file diff --git a/cpp/common/test/rules/lowercaselstartsinliteralsuffix/test.cpp b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/test.cpp new file mode 100644 index 0000000000..27be2a327d --- /dev/null +++ b/cpp/common/test/rules/lowercaselstartsinliteralsuffix/test.cpp @@ -0,0 +1,46 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +int x = false; // COMPLIANT - reported as FP in #319 +int a1 = 0L; // COMPLIANT +int a2 = 0l; // NON_COMPLIANT +int a3 = 0ll; // NON_COMPLIANT +int a4 = 0LL; // COMPLIANT +int a5 = 0uL; // COMPLIANT +int a6 = 0ul; // NON_COMPLIANT +int a7 = 0lu; // NON_COMPLIANT +int a8 = 0Lu; // COMPLIANT +int a9 = 0LU; // COMPLIANT + +long b1 = 0L; // COMPLIANT +long b2 = 0l; // NON_COMPLIANT +long b3 = 0ll; // NON_COMPLIANT +long b4 = 0LL; // COMPLIANT +long b5 = 0uL; // COMPLIANT +long b6 = 0ul; // NON_COMPLIANT +long b7 = 0lu; // NON_COMPLIANT +long b8 = 0Lu; // COMPLIANT +long b9 = 0LU; // COMPLIANT + +int c1 = 0x01L; // COMPLIANT +int c2 = 0x01l; // NON_COMPLIANT +int c3 = 0x01ll; // NON_COMPLIANT +int c4 = 0x01LL; // COMPLIANT +int c5 = 0x01uL; // COMPLIANT +int c6 = 0x01ul; // NON_COMPLIANT +int c7 = 0x01lu; // NON_COMPLIANT +int c8 = 0x01Lu; // COMPLIANT +int c9 = 0x01LU; // COMPLIANT + +long d1 = 001L; // COMPLIANT +long d2 = 001l; // NON_COMPLIANT +long d3 = 001ll; // NON_COMPLIANT +long d4 = 001LL; // COMPLIANT +long d5 = 001uL; // COMPLIANT +long d6 = 001ul; // NON_COMPLIANT +long d7 = 001lu; // NON_COMPLIANT +long d8 = 001Lu; // COMPLIANT +long d9 = 001LU; // COMPLIANT + +char *e1 = ""; +char *e2 = "ul"; +char *e3 = "UL"; \ No newline at end of file diff --git a/cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.expected b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected similarity index 100% rename from cpp/autosar/test/rules/M18-2-1/MacroOffsetofUsed.expected rename to cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected diff --git a/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected.gcc b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected.gcc new file mode 100644 index 0000000000..87bf6e1b01 --- /dev/null +++ b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected.gcc @@ -0,0 +1 @@ +| test.cpp:9:32:9:51 | offsetof(TYPE,MEMBER) | Use of banned macro offsetof. | diff --git a/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected.qcc b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected.qcc new file mode 100644 index 0000000000..f09fafd410 --- /dev/null +++ b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.expected.qcc @@ -0,0 +1 @@ +| test.cpp:9:32:9:51 | offsetof(__typ,__id) | Use of banned macro offsetof. | diff --git a/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.ql b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.ql new file mode 100644 index 0000000000..44e30b1a2f --- /dev/null +++ b/cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.macrooffsetofused.MacroOffsetofUsed + +class TestFileQuery extends MacroOffsetofUsedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M18-2-1/test.cpp b/cpp/common/test/rules/macrooffsetofused/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M18-2-1/test.cpp rename to cpp/common/test/rules/macrooffsetofused/test.cpp diff --git a/cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.expected b/cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.expected new file mode 100644 index 0000000000..6a3d5c5da7 --- /dev/null +++ b/cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.expected @@ -0,0 +1 @@ +| test.cpp:27:1:27:29 | #define MACROTHIRTEEN(X) #X ## X | Macro definition uses an # operator followed by a ## operator. | diff --git a/cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql b/cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql new file mode 100644 index 0000000000..f753b75463 --- /dev/null +++ b/cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.macroparameterfollowinghash.MacroParameterFollowingHash + +class TestFileQuery extends MacroParameterFollowingHashSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/macroparameterfollowinghash/test.cpp b/cpp/common/test/rules/macroparameterfollowinghash/test.cpp new file mode 100644 index 0000000000..5e6f187445 --- /dev/null +++ b/cpp/common/test/rules/macroparameterfollowinghash/test.cpp @@ -0,0 +1,29 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#define MACROONE 1 // COMPLIANT + +#define MACROTWO '#\'-#' + '#' // COMPLIANT + +#define MACROTHREE "##" // COMPLIANT + +#define MACROFOUR "##" + "#" // COMPLIANT + +#define MACROFIVE(X) #X // COMPLIANT + +#define MACROSIX(X, Y) X##Y // COMPLIANT + +#define MACROSEVEN "##'" #"#" // COMPLIANT + +#define MACROEIGHT '##' #"#" // COMPLIANT + +#define MACRONINE "##\"\"" + "#" // COMPLIANT + +#define MACROTEN "##\"\"'" + "#" // COMPLIANT + +#define MACROELEVEN(X) X #X #X // COMPLIANT + +#define MACROTWELVE(X) X##X##X // COMPLIANT + +#define MACROTHIRTEEN(X) #X##X // NON_COMPLIANT + +#define MACROFOURTEEN '#\'-#' + 1 #1 #1 + '#' // COMPLIANT \ No newline at end of file diff --git a/cpp/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql b/cpp/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql index 0ce5ceef5c..2ff9477919 100644 --- a/cpp/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql +++ b/cpp/common/test/rules/macroparameternotenclosedinparentheses/MacroParameterNotEnclosedInParentheses.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.macroparameternotenclosedinparentheses.MacroParameterNotEnclosedInParentheses + +class TestFileQuery extends MacroParameterNotEnclosedInParenthesesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.expected b/cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.expected new file mode 100644 index 0000000000..b6f56a20a5 --- /dev/null +++ b/cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.expected @@ -0,0 +1 @@ +| test.cpp:23:8:23:18 | call to memcmp | memcmp accesses bits which are not part of the object's value representation. | diff --git a/cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql b/cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql new file mode 100644 index 0000000000..55290047a1 --- /dev/null +++ b/cpp/common/test/rules/memcmpusedtocomparepaddingdata/MemcmpUsedToComparePaddingData.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.memcmpusedtocomparepaddingdata.MemcmpUsedToComparePaddingData + +class TestFileQuery extends MemcmpUsedToComparePaddingDataSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/memcmpusedtocomparepaddingdata/test.cpp b/cpp/common/test/rules/memcmpusedtocomparepaddingdata/test.cpp new file mode 100644 index 0000000000..7d004a2a39 --- /dev/null +++ b/cpp/common/test/rules/memcmpusedtocomparepaddingdata/test.cpp @@ -0,0 +1,30 @@ +#include + +struct S1 { + unsigned char buffType; + int size; + + friend bool operator==(const S1 &lhs, const S1 &rhs) { + return lhs.buffType == rhs.buffType && lhs.size == rhs.size; + } +}; + +struct S2 { + unsigned char buff[16]; +}; + +void f(const S1 &s1, const S1 &s2) { + if (s1 == s2) { + // COMPLIANT S overloads operator==() to perform a comparison of the value + // representation of the object + } +} +void f1(const S1 &s1, const S1 &s2) { + if (!std::memcmp(&s1, &s2, sizeof(S1))) { // NON_COMPLIANT + } +} + +void f2(const S2 &s1, const S2 &s2) { + if (!std::memcmp(&s1.buff, &s2.buff, sizeof(S2::buff))) { // COMPLIANT + } +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.expected b/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.expected similarity index 100% rename from cpp/autosar/test/rules/M3-3-2/MissingStaticSpecifierOnFunctionRedeclaration.expected rename to cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.expected diff --git a/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql b/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql new file mode 100644 index 0000000000..f84cbf8698 --- /dev/null +++ b/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/MissingStaticSpecifierFunctionRedeclarationShared.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.missingstaticspecifierfunctionredeclarationshared.MissingStaticSpecifierFunctionRedeclarationShared + +class TestFileQuery extends MissingStaticSpecifierFunctionRedeclarationSharedSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/test.cpp b/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/test.cpp new file mode 100644 index 0000000000..85e1aa467d --- /dev/null +++ b/cpp/common/test/rules/missingstaticspecifierfunctionredeclarationshared/test.cpp @@ -0,0 +1,5 @@ +static void f1(); +void f1() {} // NON_COMPLIANT + +static void f2(); +static void f2() {} // COMPLIANT \ No newline at end of file diff --git a/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.expected b/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.expected new file mode 100644 index 0000000000..45bc2466b6 --- /dev/null +++ b/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.expected @@ -0,0 +1,113 @@ +problems +| test.cpp:12:8:12:9 | l2 | test.cpp:8:14:8:20 | ... / ... | test.cpp:12:8:12:9 | l2 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.cpp:8:14:8:20 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:13:8:13:9 | l3 | test.cpp:8:14:8:20 | ... / ... | test.cpp:13:8:13:9 | l3 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.cpp:8:14:8:20 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:18:20:18:21 | l2 | test.cpp:8:14:8:20 | ... / ... | test.cpp:18:3:18:22 | l2 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:8:14:8:20 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:19:20:19:21 | l3 | test.cpp:8:14:8:20 | ... / ... | test.cpp:19:3:19:22 | l3 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:8:14:8:20 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:29:19:29:20 | l2 | test.cpp:8:14:8:20 | ... / ... | test.cpp:29:19:29:20 | l2 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.cpp:8:14:8:20 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:30:19:30:20 | l3 | test.cpp:8:14:8:20 | ... / ... | test.cpp:30:19:30:20 | l3 | Possibly infinite float value $@ flows to divisor, which would silently underflow and produce zero. | test.cpp:8:14:8:20 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:40:20:40:21 | l7 | test.cpp:33:14:34:15 | ... / ... | test.cpp:40:3:40:22 | l7 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:33:14:34:15 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:64:9:64:15 | ... / ... | test.cpp:63:5:64:16 | ... / ... | test.cpp:63:5:64:16 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.cpp:64:9:64:15 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:70:9:70:17 | ... / ... | test.cpp:69:5:70:18 | ... / ... | test.cpp:69:5:70:18 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.cpp:70:9:70:17 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:76:31:76:39 | ... / ... | test.cpp:76:14:76:40 | ... / ... | test.cpp:76:14:76:40 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.cpp:76:31:76:39 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:79:35:79:43 | ... / ... | test.cpp:79:18:79:44 | ... / ... | test.cpp:79:18:79:44 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.cpp:79:35:79:43 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:83:22:83:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:83:5:83:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:91:22:91:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:91:5:91:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:95:22:95:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:95:5:95:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:97:22:97:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:97:5:97:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:103:22:103:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:103:5:103:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:109:22:109:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:109:5:109:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:115:22:115:24 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:115:5:115:25 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:118:38:118:40 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:118:21:118:41 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:127:26:127:28 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:127:9:127:29 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:133:26:133:28 | l12 | test.cpp:81:15:81:21 | ... / ... | test.cpp:133:9:133:29 | l12 | Possibly infinite float value $@ flows to cast to integer. | test.cpp:81:15:81:21 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:188:7:189:17 | ... / ... | test.cpp:187:3:189:18 | ... / ... | test.cpp:187:3:189:18 | ... / ... | Possibly infinite float value $@ flows to cast to integer. | test.cpp:188:7:189:17 | ... / ... | from division by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:202:44:202:44 | p | test.cpp:216:51:216:59 | ... / ... | test.cpp:202:27:202:45 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.cpp:216:51:216:59 | ... / ... | from division by zero | test.cpp:216:6:216:24 | addInfThenCastToInt | addInfThenCastToInt | +| test.cpp:202:44:202:44 | p | test.cpp:220:13:221:15 | ... / ... | test.cpp:202:27:202:45 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.cpp:220:13:221:15 | ... / ... | from division by zero | test.cpp:219:6:219:7 | f2 | f2 | +| test.cpp:202:44:202:44 | p | test.cpp:231:19:231:27 | ... / ... | test.cpp:202:27:202:45 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.cpp:231:19:231:27 | ... / ... | from division by zero | test.cpp:219:6:219:7 | f2 | f2 | +| test.cpp:212:30:212:30 | p | test.cpp:227:25:227:33 | ... / ... | test.cpp:212:13:212:31 | p | Possibly infinite float value $@ computed in function $@ flows to cast to integer. | test.cpp:227:25:227:33 | ... / ... | from division by zero | test.cpp:219:6:219:7 | f2 | f2 | +edges +| test.cpp:8:14:8:20 | ... / ... | test.cpp:8:14:8:20 | ... / ... | provenance | | +| test.cpp:8:14:8:20 | ... / ... | test.cpp:9:14:9:16 | - ... | provenance | Config | +| test.cpp:8:14:8:20 | ... / ... | test.cpp:12:8:12:9 | l2 | provenance | | +| test.cpp:8:14:8:20 | ... / ... | test.cpp:18:3:18:22 | l2 | provenance | | +| test.cpp:8:14:8:20 | ... / ... | test.cpp:29:19:29:20 | l2 | provenance | | +| test.cpp:9:14:9:16 | - ... | test.cpp:9:14:9:16 | - ... | provenance | | +| test.cpp:9:14:9:16 | - ... | test.cpp:13:8:13:9 | l3 | provenance | | +| test.cpp:9:14:9:16 | - ... | test.cpp:19:3:19:22 | l3 | provenance | | +| test.cpp:9:14:9:16 | - ... | test.cpp:30:19:30:20 | l3 | provenance | | +| test.cpp:33:14:34:15 | ... / ... | test.cpp:33:14:34:15 | ... / ... | provenance | | +| test.cpp:33:14:34:15 | ... / ... | test.cpp:40:3:40:22 | l7 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:81:15:81:21 | ... / ... | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:83:5:83:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:91:5:91:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:95:5:95:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:97:5:97:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:103:5:103:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:109:5:109:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:115:5:115:25 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:118:21:118:41 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:127:9:127:29 | l12 | provenance | | +| test.cpp:81:15:81:21 | ... / ... | test.cpp:133:9:133:29 | l12 | provenance | | +| test.cpp:202:22:202:22 | p | test.cpp:202:27:202:45 | p | provenance | | +| test.cpp:210:34:210:34 | p | test.cpp:212:13:212:31 | p | provenance | | +| test.cpp:216:32:216:32 | p | test.cpp:216:47:216:59 | ... + ... | provenance | Config | +| test.cpp:216:47:216:59 | ... + ... | test.cpp:202:22:202:22 | p | provenance | | +| test.cpp:216:47:216:59 | ... + ... | test.cpp:202:22:202:22 | p | provenance | | +| test.cpp:216:51:216:59 | ... / ... | test.cpp:216:47:216:59 | ... + ... | provenance | Config | +| test.cpp:220:13:221:15 | ... / ... | test.cpp:202:22:202:22 | p | provenance | | +| test.cpp:227:25:227:33 | ... / ... | test.cpp:210:34:210:34 | p | provenance | | +| test.cpp:231:19:231:27 | ... / ... | test.cpp:231:19:231:27 | ... / ... | provenance | | +| test.cpp:231:19:231:27 | ... / ... | test.cpp:233:21:233:31 | ... + ... | provenance | Config | +| test.cpp:233:21:233:31 | ... + ... | test.cpp:233:21:233:31 | ... + ... | provenance | | +| test.cpp:233:21:233:31 | ... + ... | test.cpp:235:13:235:21 | middleInf | provenance | | +| test.cpp:233:21:233:31 | ... + ... | test.cpp:237:23:237:31 | middleInf | provenance | | +| test.cpp:235:13:235:21 | middleInf | test.cpp:202:22:202:22 | p | provenance | | +| test.cpp:237:23:237:31 | middleInf | test.cpp:216:32:216:32 | p | provenance | | +nodes +| test.cpp:8:14:8:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:8:14:8:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:9:14:9:16 | - ... | semmle.label | - ... | +| test.cpp:9:14:9:16 | - ... | semmle.label | - ... | +| test.cpp:12:8:12:9 | l2 | semmle.label | l2 | +| test.cpp:13:8:13:9 | l3 | semmle.label | l3 | +| test.cpp:18:3:18:22 | l2 | semmle.label | l2 | +| test.cpp:19:3:19:22 | l3 | semmle.label | l3 | +| test.cpp:29:19:29:20 | l2 | semmle.label | l2 | +| test.cpp:30:19:30:20 | l3 | semmle.label | l3 | +| test.cpp:33:14:34:15 | ... / ... | semmle.label | ... / ... | +| test.cpp:33:14:34:15 | ... / ... | semmle.label | ... / ... | +| test.cpp:40:3:40:22 | l7 | semmle.label | l7 | +| test.cpp:63:5:64:16 | ... / ... | semmle.label | ... / ... | +| test.cpp:69:5:70:18 | ... / ... | semmle.label | ... / ... | +| test.cpp:76:14:76:40 | ... / ... | semmle.label | ... / ... | +| test.cpp:79:18:79:44 | ... / ... | semmle.label | ... / ... | +| test.cpp:81:15:81:21 | ... / ... | semmle.label | ... / ... | +| test.cpp:81:15:81:21 | ... / ... | semmle.label | ... / ... | +| test.cpp:83:5:83:25 | l12 | semmle.label | l12 | +| test.cpp:91:5:91:25 | l12 | semmle.label | l12 | +| test.cpp:95:5:95:25 | l12 | semmle.label | l12 | +| test.cpp:97:5:97:25 | l12 | semmle.label | l12 | +| test.cpp:103:5:103:25 | l12 | semmle.label | l12 | +| test.cpp:109:5:109:25 | l12 | semmle.label | l12 | +| test.cpp:115:5:115:25 | l12 | semmle.label | l12 | +| test.cpp:118:21:118:41 | l12 | semmle.label | l12 | +| test.cpp:127:9:127:29 | l12 | semmle.label | l12 | +| test.cpp:133:9:133:29 | l12 | semmle.label | l12 | +| test.cpp:187:3:189:18 | ... / ... | semmle.label | ... / ... | +| test.cpp:202:22:202:22 | p | semmle.label | p | +| test.cpp:202:27:202:45 | p | semmle.label | p | +| test.cpp:210:34:210:34 | p | semmle.label | p | +| test.cpp:212:13:212:31 | p | semmle.label | p | +| test.cpp:216:32:216:32 | p | semmle.label | p | +| test.cpp:216:47:216:59 | ... + ... | semmle.label | ... + ... | +| test.cpp:216:47:216:59 | ... + ... | semmle.label | ... + ... | +| test.cpp:216:51:216:59 | ... / ... | semmle.label | ... / ... | +| test.cpp:220:13:221:15 | ... / ... | semmle.label | ... / ... | +| test.cpp:227:25:227:33 | ... / ... | semmle.label | ... / ... | +| test.cpp:231:19:231:27 | ... / ... | semmle.label | ... / ... | +| test.cpp:231:19:231:27 | ... / ... | semmle.label | ... / ... | +| test.cpp:233:21:233:31 | ... + ... | semmle.label | ... + ... | +| test.cpp:233:21:233:31 | ... + ... | semmle.label | ... + ... | +| test.cpp:235:13:235:21 | middleInf | semmle.label | middleInf | +| test.cpp:237:23:237:31 | middleInf | semmle.label | middleInf | +subpaths diff --git a/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql b/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql new file mode 100644 index 0000000000..f0d160a650 --- /dev/null +++ b/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.misuseofinfinitefloatingpointvalue.MisuseOfInfiniteFloatingPointValue + +class TestFileQuery extends MisuseOfInfiniteFloatingPointValueSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/test.cpp b/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/test.cpp new file mode 100644 index 0000000000..a0624ccbf3 --- /dev/null +++ b/cpp/common/test/rules/misuseofinfinitefloatingpointvalue/test.cpp @@ -0,0 +1,239 @@ +#include + +float getFloat() { return 1.0; } + +// Parameter could be infinity +void f1(float p1) { + float l1 = 1; + float l2 = 1.0 / 0; + float l3 = -l2; + + 10 / l1; // COMPLIANT + 10 / l2; // NON_COMPLIANT: Underflows to zero + 10 / l3; // NON_COMPLIANT: Underflow to negative zero + 10 / p1; // COMPLIANT: Reduce false positives by assuming not infinity + 10 / getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + static_cast(l1); // COMPLIANT + static_cast(l2); // NON_COMPLIANT + static_cast(l3); // NON_COMPLIANT + static_cast( + p1); // COMPLIANT: Reduce false positives by assuming not infinity + static_cast( + getFloat()); // COMPLIANT: Reduce false positives by assuming not infinity + + // Not NaN: + float l4 = l1 / l1; // COMPLIANT + + // NaN because of infinity divided by itself: + float l5 = l2 / l2; // NON_COMPLIANT: Division by infinity not allowed. + float l6 = l3 / l3; // NON_COMPLIANT: Division by infinity not allowed. + + // NaN because of zero divided by itself: + float l7 = getFloat() / + p1; // COMPLIANT: Reduce false positives by assuming not infinity + float l8 = 0.0 / 0.0; + + static_cast(l4); // COMPLIANT + static_cast(l5); // COMPLIANT: Casting NaN to int + static_cast(l6); // COMPLIANT: Casting NaN to int + static_cast(l7); // NON_COMPLIANT: Casting Infinity to int + static_cast(l8); // COMPLIANT: Casting NaN to int + + l4 == 0; // COMPLIANT + l4 != 0; // COMPLIANT + l4 <= 0; // COMPLIANT + l4 < 0; // COMPLIANT + l4 >= 0; // COMPLIANT + l5 == 0; // NON_COMPLIANT: Comparison with NaN always false + l5 != 0; // NON_COMPLIANT: Comparison with NaN always false + l5 < 0; // NON_COMPLIANT: Comparison with NaN always false + l5 <= 0; // NON_COMPLIANT: Comparison with NaN always false + l5 > 0; // NON_COMPLIANT: Comparison with NaN always false + l5 >= 0; // NON_COMPLIANT: Comparison with NaN always false + l6 == 0; // NON_COMPLIANT: Comparison with NaN always false + l7 == 0; // NON_COMPLIANT: Comparison with NaN always false + l8 == 0; // NON_COMPLIANT: Comparison with NaN always false + + // Guards + float l9 = 0; + if (l9 != 0) { + static_cast(l9 / l9); // COMPLIANT: l9 is not zero + } else { + static_cast( + l9 / l9); // NON_COMPLIANT[False positive]: Guarded to not be NaN + } + + float l10 = 0; + if (l10 == 0) { + static_cast( + l10 / l10); // NON_COMPLIANT[False positive]: Casting NaN to integer + } else { + static_cast(l10 / l10); // COMPLIANT: Guarded to not be NaN + } + + float l11 = 0; + l11 == 0 ? static_cast(l11 / l11) : 0; // NON_COMPLIANT[False positive] + l11 == 0 ? 0 : static_cast(l11 / l11); // COMPLIANT + l11 != 0 ? static_cast(l11 / l11) : 0; // COMPLIANT + l11 != 0 ? 0 : static_cast(l11 / l11); // NON_COMPLIANT[False positive] + + float l12 = 1.0 / 0; + if (std::isinf(l12)) { + static_cast(l12); // NON_COMPLIANT: Casting Infinity to integer + } else { + static_cast(l12); // COMPLIANT: Guarded not to be Infinity + } + + if (!std::isinf(l12)) { + static_cast(l12); // COMPLIANT: Guarded not to be Infinity + } else { + static_cast(l12); // NON_COMPLIANT: Casting Infinity to integer + } + + if (std::isinf(l12) == 1) { + static_cast(l12); // NON_COMPLIANT: Must be +Infinity + } else { + static_cast(l12); // NON_COMPLIANT: May be -Infinity + } + + if (std::isfinite(l12)) { + static_cast(l12); // COMPLIANT: Guarded not to be Infinity + } else { + static_cast(l12); // NON_COMPLIANT: Casting Infinity to integer + } + + if (std::isnormal(l12)) { + static_cast(l12); // COMPLIANT: Guarded not to be Infinity + } else { + static_cast(l12); // NON_COMPLIANT: Casting Infinity to integer + } + + if (std::isnan(l12)) { + static_cast(l12); // COMPLIANT: Guarded not to be Infinity + } else { + static_cast(l12); // NON_COMPLIANT: Casting Infinity to integer + } + + std::isinf(l12) ? static_cast(l12) + : 0; // NON_COMPLIANT: Check on wrong branch + std::isinf(l12) + ? 0 + : static_cast(l12); // COMPLIANT: Checked not infinite before use + std::isfinite(l12) ? static_cast(l12) + : 0; // COMPLIANT: Checked finite before use + std::isfinite(l12) + ? 0 + : static_cast(l12); // NON_COMPLIANT: Checked on wrong branch + std::isnan(l12) + ? static_cast(l12) + : 0; // COMPLIANT: Checked NaN, therefore not infinite, before use + std::isnan(l12) + ? 0 + : static_cast(l12); // NON_COMPLIANT: Check on wrong branch + + float l13 = 0.0 / 0; + if (std::isinf(l13)) { + static_cast(l13); // COMPLIANT: Guarded not to be NaN + } else { + static_cast(l13); // COMPLIANT: Casting NaN to integer + } + + if (std::isinf(l13) == 1) { + static_cast( + l13); // COMPLIANT: Guarded not to be NaN (must be +Infinity) + } else { + static_cast(l13); // COMPLIANT: Casting NaN to integer + } + + if (std::isfinite(l13)) { + static_cast(l13); // COMPLIANT: Guarded not to be NaN + } else { + static_cast(l13); // COMPLIANT: Casting NaN to integer + } + + if (std::isnormal(l13)) { + static_cast(l13); // COMPLIANT: Guarded not to be NaN + } else { + static_cast(l13); // COMPLIANT: Casting NaN to integer + } + + if (std::isnan(l13)) { + static_cast(l13); // COMPLIANT: Casting NaN to integer + } else { + static_cast(l13); // COMPLIANT: Guarded not to be NaN + } + + std::isinf(l13) + ? static_cast(l13) + : 0; // COMPLIANT: Checked infinite, therefore not NaN, before use + std::isinf(l13) ? 0 + : static_cast(l13); // COMPLIANT: Check on wrong branch + std::isfinite(l13) ? static_cast(l13) + : 0; // COMPLIANT: Checked finite before use + std::isfinite(l13) + ? 0 + : static_cast(l13); // COMPLIANT: Checked on wrong branch + std::isnan(l13) ? static_cast(l13) + : 0; // COMPLIANT: Check on wrong branch + std::isnan(l13) + ? 0 + : static_cast(l13); // COMPLIANT: Checked not NaN before use + + static_cast( + std::pow(2, p1)); // NON_COMPLIANT[False negative]: likely to be Infinity + static_cast( + std::pow(2, std::sin(p1))); // COMPLIANT: not likely to be Infinity + static_cast( + 1 / std::sin( + p1)); // NON_COMPLIANT: possible infinity from zero in denominator + static_cast(1 / + std::log(p1)); // COMPLIANT: not possibly zero in denominator + static_cast(std::pow(p1, p1)); // COMPLIANT: NaN if p1 is zero + if (p1 != 0) { + static_cast(std::pow(p1, p1)); // COMPLIANT: p1 is not zero + } + + static_cast(std::acos(p1)); // COMPLIANT: NaN if p1 is not within -1..1 + static_cast( + std::acos(std::cos(p1))); // COMPLIANT: cos(p1) is within -1..1 +} + +void castToInt(float p) { static_cast(p); } + +void checkBeforeCastToInt(float p) { + if (std::isfinite(p)) { + castToInt(p); + } +} + +void castToIntToFloatToInt(float p) { + // This should be reported as a violation, but not downstream from here. + castToInt(static_cast(p)); +} + +void addOneThenCastToInt(float p) { castToInt(p + 1); } +void addInfThenCastToInt(float p) { castToInt(p + 1.0 / 0.0); } +void addNaNThenCastToInt(float p) { castToInt(p + 0.0 / 0.0); } + +void f2() { + castToInt(1.0 / + 0.0); // NON_COMPLIANT: Infinity flows to denominator in division + castToInt(0.0 / 0.0); // COMPLIANT + checkBeforeCastToInt(1.0 / 0.0); // COMPLIANT + checkBeforeCastToInt(0.0 / 0.0); // COMPLIANT + addOneThenCastToInt(1.0 / 0.0); // NON_COMPLIANT[False negative] + addOneThenCastToInt(0.0 / 0.0); // NON_COMPLIANT + castToIntToFloatToInt(1.0 / 0.0); // NON_COMPLIANT + castToIntToFloatToInt(0.0 / 0.0); // COMPLIANT + + // Check that during flow analysis, we only report the true root cause: + float rootInf = 1.0 / 0.0; + float rootNaN = 0.0 / 0.0; + float middleInf = rootInf + 1; + float middleNaN = rootNaN + 1; + castToInt(middleInf); // NON_COMPLIANT + castToInt(middleNaN); // COMPLIANT + addInfThenCastToInt(middleInf); // NON_COMPLIANT + addNaNThenCastToInt(middleNaN); // COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.expected b/cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.expected new file mode 100644 index 0000000000..5e4d3cacd7 --- /dev/null +++ b/cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.expected @@ -0,0 +1,136 @@ +problems +| test.cpp:36:8:36:9 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:36:3:36:9 | l5 | Possible NaN value $@ flows to a cast to integer. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:37:8:37:9 | l6 | test.cpp:28:14:28:20 | ... / ... | test.cpp:37:3:37:9 | l6 | Possible NaN value $@ flows to a cast to integer. | test.cpp:28:14:28:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:38:8:38:9 | l7 | test.cpp:31:14:32:15 | ... / ... | test.cpp:38:3:38:9 | l7 | Possible NaN value $@ flows to a cast to integer. | test.cpp:31:14:32:15 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:39:8:39:9 | l8 | test.cpp:33:14:33:22 | ... / ... | test.cpp:39:3:39:9 | l8 | Possible NaN value $@ flows to a cast to integer. | test.cpp:33:14:33:22 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:46:3:46:4 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:46:3:46:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:47:3:47:4 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:47:3:47:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:48:3:48:4 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:48:3:48:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:49:3:49:4 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:49:3:49:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:50:3:50:4 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:50:3:50:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:51:3:51:4 | l5 | test.cpp:27:14:27:20 | ... / ... | test.cpp:51:3:51:4 | l5 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:27:14:27:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:52:3:52:4 | l6 | test.cpp:28:14:28:20 | ... / ... | test.cpp:52:3:52:4 | l6 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:28:14:28:20 | ... / ... | from division of infinity by infinity | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:53:3:53:4 | l7 | test.cpp:31:14:32:15 | ... / ... | test.cpp:53:3:53:4 | l7 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:31:14:32:15 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:54:3:54:4 | l8 | test.cpp:33:14:33:22 | ... / ... | test.cpp:54:3:54:4 | l8 | Possible NaN value $@ flows to comparison, which would always evaluate to false. | test.cpp:33:14:33:22 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:61:11:61:17 | ... / ... | test.cpp:61:5:61:18 | ... / ... | test.cpp:61:5:61:18 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.cpp:61:11:61:17 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:66:11:66:19 | ... / ... | test.cpp:66:5:66:20 | ... / ... | test.cpp:66:5:66:20 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.cpp:66:11:66:19 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:72:20:72:28 | ... / ... | test.cpp:72:14:72:29 | ... / ... | test.cpp:72:14:72:29 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.cpp:72:20:72:28 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:75:24:75:32 | ... / ... | test.cpp:75:18:75:33 | ... / ... | test.cpp:75:18:75:33 | ... / ... | Possible NaN value $@ flows to a cast to integer. | test.cpp:75:24:75:32 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:127:10:127:12 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:127:5:127:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:133:10:133:12 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:133:5:133:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:139:10:139:12 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:139:5:139:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:145:10:145:12 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:145:5:145:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:149:10:149:12 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:149:5:149:12 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:157:30:157:32 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:157:25:157:32 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:159:33:159:35 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:159:28:159:35 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:160:26:160:28 | l13 | test.cpp:123:15:123:21 | ... / ... | test.cpp:160:21:160:28 | l13 | Possible NaN value $@ flows to a cast to integer. | test.cpp:123:15:123:21 | ... / ... | from division of zero by zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:168:8:168:15 | call to pow | test.cpp:168:3:168:23 | call to pow | test.cpp:168:3:168:23 | call to pow | Possible NaN value $@ flows to a cast to integer. | test.cpp:168:8:168:15 | call to pow | both arguments are equal to zero | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:173:8:173:16 | call to acos | test.cpp:173:3:173:20 | call to acos | test.cpp:173:3:173:20 | call to acos | Possible NaN value $@ flows to a cast to integer. | test.cpp:173:8:173:16 | call to acos | the argument has a range -1000000000000000...1000000000000000 which is outside the domain of this function (-1.0...1.0) | test.cpp:6:6:6:7 | f1 | f1 | +| test.cpp:177:32:177:32 | p | test.cpp:192:51:192:59 | ... / ... | test.cpp:177:27:177:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.cpp:192:51:192:59 | ... / ... | from division of zero by zero | test.cpp:192:6:192:24 | addNaNThenCastToInt | addNaNThenCastToInt | +| test.cpp:177:32:177:32 | p | test.cpp:196:13:196:21 | ... / ... | test.cpp:177:27:177:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.cpp:196:13:196:21 | ... / ... | from division of zero by zero | test.cpp:194:6:194:7 | f2 | f2 | +| test.cpp:177:32:177:32 | p | test.cpp:200:23:200:31 | ... / ... | test.cpp:177:27:177:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.cpp:200:23:200:31 | ... / ... | from division of zero by zero | test.cpp:194:6:194:7 | f2 | f2 | +| test.cpp:177:32:177:32 | p | test.cpp:206:19:206:27 | ... / ... | test.cpp:177:27:177:32 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.cpp:206:19:206:27 | ... / ... | from division of zero by zero | test.cpp:194:6:194:7 | f2 | f2 | +| test.cpp:187:18:187:18 | p | test.cpp:202:25:202:33 | ... / ... | test.cpp:187:13:187:18 | p | Possible NaN value $@ computed in function $@ flows to a cast to integer. | test.cpp:202:25:202:33 | ... / ... | from division of zero by zero | test.cpp:194:6:194:7 | f2 | f2 | +edges +| test.cpp:27:14:27:20 | ... / ... | test.cpp:27:14:27:20 | ... / ... | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:36:3:36:9 | l5 | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:46:3:46:4 | l5 | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:47:3:47:4 | l5 | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:48:3:48:4 | l5 | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:49:3:49:4 | l5 | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:50:3:50:4 | l5 | provenance | | +| test.cpp:27:14:27:20 | ... / ... | test.cpp:51:3:51:4 | l5 | provenance | | +| test.cpp:28:14:28:20 | ... / ... | test.cpp:28:14:28:20 | ... / ... | provenance | | +| test.cpp:28:14:28:20 | ... / ... | test.cpp:37:3:37:9 | l6 | provenance | | +| test.cpp:28:14:28:20 | ... / ... | test.cpp:52:3:52:4 | l6 | provenance | | +| test.cpp:31:14:32:15 | ... / ... | test.cpp:31:14:32:15 | ... / ... | provenance | | +| test.cpp:31:14:32:15 | ... / ... | test.cpp:38:3:38:9 | l7 | provenance | | +| test.cpp:31:14:32:15 | ... / ... | test.cpp:53:3:53:4 | l7 | provenance | | +| test.cpp:33:14:33:22 | ... / ... | test.cpp:33:14:33:22 | ... / ... | provenance | | +| test.cpp:33:14:33:22 | ... / ... | test.cpp:39:3:39:9 | l8 | provenance | | +| test.cpp:33:14:33:22 | ... / ... | test.cpp:54:3:54:4 | l8 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:123:15:123:21 | ... / ... | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:127:5:127:12 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:133:5:133:12 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:139:5:139:12 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:145:5:145:12 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:149:5:149:12 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:157:25:157:32 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:159:28:159:35 | l13 | provenance | | +| test.cpp:123:15:123:21 | ... / ... | test.cpp:160:21:160:28 | l13 | provenance | | +| test.cpp:177:22:177:22 | p | test.cpp:177:27:177:32 | p | provenance | | +| test.cpp:185:34:185:34 | p | test.cpp:187:13:187:18 | p | provenance | | +| test.cpp:190:32:190:32 | p | test.cpp:190:47:190:51 | ... + ... | provenance | Config | +| test.cpp:190:47:190:51 | ... + ... | test.cpp:177:22:177:22 | p | provenance | | +| test.cpp:192:32:192:32 | p | test.cpp:192:47:192:59 | ... + ... | provenance | Config | +| test.cpp:192:47:192:59 | ... + ... | test.cpp:177:22:177:22 | p | provenance | | +| test.cpp:192:47:192:59 | ... + ... | test.cpp:177:22:177:22 | p | provenance | | +| test.cpp:192:51:192:59 | ... / ... | test.cpp:192:47:192:59 | ... + ... | provenance | Config | +| test.cpp:196:13:196:21 | ... / ... | test.cpp:177:22:177:22 | p | provenance | | +| test.cpp:200:23:200:31 | ... / ... | test.cpp:190:32:190:32 | p | provenance | | +| test.cpp:202:25:202:33 | ... / ... | test.cpp:185:34:185:34 | p | provenance | | +| test.cpp:206:19:206:27 | ... / ... | test.cpp:206:19:206:27 | ... / ... | provenance | | +| test.cpp:206:19:206:27 | ... / ... | test.cpp:208:21:208:31 | ... + ... | provenance | Config | +| test.cpp:208:21:208:31 | ... + ... | test.cpp:208:21:208:31 | ... + ... | provenance | | +| test.cpp:208:21:208:31 | ... + ... | test.cpp:210:13:210:21 | middleNaN | provenance | | +| test.cpp:208:21:208:31 | ... + ... | test.cpp:212:23:212:31 | middleNaN | provenance | | +| test.cpp:210:13:210:21 | middleNaN | test.cpp:177:22:177:22 | p | provenance | | +| test.cpp:212:23:212:31 | middleNaN | test.cpp:192:32:192:32 | p | provenance | | +nodes +| test.cpp:27:14:27:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:27:14:27:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:28:14:28:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:28:14:28:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:31:14:32:15 | ... / ... | semmle.label | ... / ... | +| test.cpp:31:14:32:15 | ... / ... | semmle.label | ... / ... | +| test.cpp:33:14:33:22 | ... / ... | semmle.label | ... / ... | +| test.cpp:33:14:33:22 | ... / ... | semmle.label | ... / ... | +| test.cpp:36:3:36:9 | l5 | semmle.label | l5 | +| test.cpp:37:3:37:9 | l6 | semmle.label | l6 | +| test.cpp:38:3:38:9 | l7 | semmle.label | l7 | +| test.cpp:39:3:39:9 | l8 | semmle.label | l8 | +| test.cpp:46:3:46:4 | l5 | semmle.label | l5 | +| test.cpp:47:3:47:4 | l5 | semmle.label | l5 | +| test.cpp:48:3:48:4 | l5 | semmle.label | l5 | +| test.cpp:49:3:49:4 | l5 | semmle.label | l5 | +| test.cpp:50:3:50:4 | l5 | semmle.label | l5 | +| test.cpp:51:3:51:4 | l5 | semmle.label | l5 | +| test.cpp:52:3:52:4 | l6 | semmle.label | l6 | +| test.cpp:53:3:53:4 | l7 | semmle.label | l7 | +| test.cpp:54:3:54:4 | l8 | semmle.label | l8 | +| test.cpp:61:5:61:18 | ... / ... | semmle.label | ... / ... | +| test.cpp:66:5:66:20 | ... / ... | semmle.label | ... / ... | +| test.cpp:72:14:72:29 | ... / ... | semmle.label | ... / ... | +| test.cpp:75:18:75:33 | ... / ... | semmle.label | ... / ... | +| test.cpp:123:15:123:21 | ... / ... | semmle.label | ... / ... | +| test.cpp:123:15:123:21 | ... / ... | semmle.label | ... / ... | +| test.cpp:127:5:127:12 | l13 | semmle.label | l13 | +| test.cpp:133:5:133:12 | l13 | semmle.label | l13 | +| test.cpp:139:5:139:12 | l13 | semmle.label | l13 | +| test.cpp:145:5:145:12 | l13 | semmle.label | l13 | +| test.cpp:149:5:149:12 | l13 | semmle.label | l13 | +| test.cpp:157:25:157:32 | l13 | semmle.label | l13 | +| test.cpp:159:28:159:35 | l13 | semmle.label | l13 | +| test.cpp:160:21:160:28 | l13 | semmle.label | l13 | +| test.cpp:168:3:168:23 | call to pow | semmle.label | call to pow | +| test.cpp:173:3:173:20 | call to acos | semmle.label | call to acos | +| test.cpp:177:22:177:22 | p | semmle.label | p | +| test.cpp:177:27:177:32 | p | semmle.label | p | +| test.cpp:185:34:185:34 | p | semmle.label | p | +| test.cpp:187:13:187:18 | p | semmle.label | p | +| test.cpp:190:32:190:32 | p | semmle.label | p | +| test.cpp:190:47:190:51 | ... + ... | semmle.label | ... + ... | +| test.cpp:192:32:192:32 | p | semmle.label | p | +| test.cpp:192:47:192:59 | ... + ... | semmle.label | ... + ... | +| test.cpp:192:47:192:59 | ... + ... | semmle.label | ... + ... | +| test.cpp:192:51:192:59 | ... / ... | semmle.label | ... / ... | +| test.cpp:196:13:196:21 | ... / ... | semmle.label | ... / ... | +| test.cpp:200:23:200:31 | ... / ... | semmle.label | ... / ... | +| test.cpp:202:25:202:33 | ... / ... | semmle.label | ... / ... | +| test.cpp:206:19:206:27 | ... / ... | semmle.label | ... / ... | +| test.cpp:206:19:206:27 | ... / ... | semmle.label | ... / ... | +| test.cpp:208:21:208:31 | ... + ... | semmle.label | ... + ... | +| test.cpp:208:21:208:31 | ... + ... | semmle.label | ... + ... | +| test.cpp:210:13:210:21 | middleNaN | semmle.label | middleNaN | +| test.cpp:212:23:212:31 | middleNaN | semmle.label | middleNaN | +subpaths diff --git a/cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql b/cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql new file mode 100644 index 0000000000..a1f729ed02 --- /dev/null +++ b/cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.misuseofnanfloatingpointvalue.MisuseOfNaNFloatingPointValue + +class TestFileQuery extends MisuseOfNaNFloatingPointValueSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/misuseofnanfloatingpointvalue/test.cpp b/cpp/common/test/rules/misuseofnanfloatingpointvalue/test.cpp new file mode 100644 index 0000000000..a68a47daf7 --- /dev/null +++ b/cpp/common/test/rules/misuseofnanfloatingpointvalue/test.cpp @@ -0,0 +1,213 @@ +#include + +float getFloat() { return 1.0; } + +// Parameter could be infinity +void f1(float p1) { + float l1 = 1; + float l2 = 1.0 / 0; + float l3 = -l2; + + 10 / l1; // COMPLIANT + 10 / l2; // COMPLIANT: Underflows to zero + 10 / l3; // COMPLIANT: Underflow to negative zero + 10 / p1; // COMPLIANT: Reduce false positives by assuming not infinity + 10 / getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + (int)l1; // COMPLIANT + (int)l2; // COMPLIANT + (int)l3; // COMPLIANT + (int)p1; // COMPLIANT: Reduce false positives by assuming not infinity + (int)getFloat(); // COMPLIANT: Reduce false positives by assuming not infinity + + // Not NaN: + float l4 = l1 / l1; // COMPLIANT + + // NaN because of infinity divided by itself: + float l5 = l2 / l2; // COMPLIANT: Division by infinity not allowed. + float l6 = l3 / l3; // COMPLIANT: Division by infinity not allowed. + + // NaN because of zero divided by itself: + float l7 = getFloat() / + p1; // COMPLIANT: Reduce false positives by assuming not infinity + float l8 = 0.0 / 0.0; + + (int)l4; // COMPLIANT + (int)l5; // NON_COMPLIANT: Casting NaN to int + (int)l6; // NON_COMPLIANT: Casting NaN to int + (int)l7; // NON_COMPLIANT: Casting NaN to int + (int)l8; // NON_COMPLIANT: Casting NaN to int + + l4 == 0; // COMPLIANT + l4 != 0; // COMPLIANT + l4 <= 0; // COMPLIANT + l4 < 0; // COMPLIANT + l4 >= 0; // COMPLIANT + l5 == 0; // NON_COMPLIANT: Comparison with NaN always false + l5 != 0; // NON_COMPLIANT: Comparison with NaN always false + l5 < 0; // NON_COMPLIANT: Comparison with NaN always false + l5 <= 0; // NON_COMPLIANT: Comparison with NaN always false + l5 > 0; // NON_COMPLIANT: Comparison with NaN always false + l5 >= 0; // NON_COMPLIANT: Comparison with NaN always false + l6 == 0; // NON_COMPLIANT: Comparison with NaN always false + l7 == 0; // NON_COMPLIANT: Comparison with NaN always false + l8 == 0; // NON_COMPLIANT: Comparison with NaN always false + + // Guards + float l9 = 0; + if (l9 != 0) { + (int)(l9 / l9); // COMPLIANT: l9 is not zero + } else { + (int)(l9 / l9); // NON_COMPLIANT: Casting NaN to integer + } + + float l10 = 0; + if (l10 == 0) { + (int)(l10 / l10); // NON_COMPLIANT: Casting NaN to integer + } else { + (int)(l10 / l10); // COMPLIANT: Guarded to not be NaN + } + + float l11 = 0; + l11 == 0 ? (int)(l11 / l11) : 0; // NON_COMPLIANT + l11 == 0 ? 0 : (int)(l11 / l11); // COMPLIANT + l11 != 0 ? (int)(l11 / l11) : 0; // COMPLIANT + l11 != 0 ? 0 : (int)(l11 / l11); // NON_COMPLIANT + + float l12 = 1.0 / 0; + if (std::isinf(l12)) { + (int)l12; // COMPLIANT: Casting Infinity to integer + } else { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } + + if (!std::isinf(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + if (std::isinf(l12) == 1) { + (int)l12; // COMPLIANT: Must be +Infinity + } else { + (int)l12; // COMPLIANT: May be -Infinity + } + + if (std::isfinite(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + if (std::isnormal(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + if (std::isnan(l12)) { + (int)l12; // COMPLIANT: Guarded not to be Infinity + } else { + (int)l12; // COMPLIANT: Casting Infinity to integer + } + + std::isinf(l12) ? (int)l12 : 0; // COMPLIANT: Check on wrong branch + std::isinf(l12) ? 0 : (int)l12; // COMPLIANT: Checked not infinite before use + std::isfinite(l12) ? (int)l12 : 0; // COMPLIANT: Checked finite before use + std::isfinite(l12) ? 0 : (int)l12; // COMPLIANT: Checked on wrong branch + std::isnan(l12) + ? (int)l12 + : 0; // COMPLIANT: Checked NaN, therefore not infinite, before use + std::isnan(l12) ? 0 : (int)l12; // COMPLIANT: Check on wrong branch + + float l13 = 0.0 / 0; + if (std::isinf(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (std::isinf(l13) == 1) { + (int)l13; // COMPLIANT: Guarded not to be NaN (must be +Infinity) + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (std::isfinite(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (std::isnormal(l13)) { + (int)l13; // COMPLIANT: Guarded not to be NaN + } else { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } + + if (std::isnan(l13)) { + (int)l13; // NON_COMPLIANT: Casting NaN to integer + } else { + (int)l13; // COMPLIANT: Guarded not to be NaN + } + + std::isinf(l13) + ? (int)l13 + : 0; // COMPLIANT: Checked infinite, therefore not NaN, before use + std::isinf(l13) ? 0 : (int)l13; // NON_COMPLIANT: Check on wrong branch + std::isfinite(l13) ? (int)l13 : 0; // COMPLIANT: Checked finite before use + std::isfinite(l13) ? 0 : (int)l13; // NON_COMPLIANT: Checked on wrong branch + std::isnan(l13) ? (int)l13 : 0; // NON_COMPLIANT: Check on wrong branch + std::isnan(l13) ? 0 : (int)l13; // COMPLIANT: Checked not NaN before use + + (int)std::pow(2, p1); // COMPLIANT: likely to be Infinity + (int)std::pow(2, std::sin(p1)); // COMPLIANT: not likely to be Infinity + (int)(1 / + std::sin(p1)); // COMPLIANT: possible infinity from zero in denominator + (int)(1 / std::log(p1)); // COMPLIANT: not possibly zero in denominator + (int)std::pow(p1, p1); // NON_COMPLIANT: NaN if p1 is zero + if (p1 != 0) { + (int)std::pow(p1, p1); // COMPLIANT: p1 is not zero + } + + (int)std::acos(p1); // NON_COMPLIANT: NaN if p1 is not within -1..1 + (int)std::acos(std::cos(p1)); // COMPLIANT: cos(p1) is within -1..1 +} + +void castToInt(float p) { (int)p; } + +void checkBeforeCastToInt(float p) { + if (std::isfinite(p)) { + castToInt(p); + } +} + +void castToIntToFloatToInt(float p) { + // This should be reported as a violation, but not downstream from here. + castToInt((int)p); +} + +void addOneThenCastToInt(float p) { castToInt(p + 1); } +void addInfThenCastToInt(float p) { castToInt(p + 1.0 / 0.0); } +void addNaNThenCastToInt(float p) { castToInt(p + 0.0 / 0.0); } + +void f2() { + castToInt(1.0 / 0.0); // COMPLIANT: Infinity flows to denominator in division + castToInt(0.0 / 0.0); // COMPLIANT: NaN flows to denominator in division + checkBeforeCastToInt(1.0 / 0.0); // COMPLIANT + checkBeforeCastToInt(0.0 / 0.0); // COMPLIANT + addOneThenCastToInt(1.0 / 0.0); // COMPLIANT + addOneThenCastToInt(0.0 / 0.0); // NON_COMPLIANT + castToIntToFloatToInt(1.0 / 0.0); // COMPLIANT + castToIntToFloatToInt(0.0 / 0.0); // NON_COMPLIANT + + // Check that during flow analysis, we only report the true root cause: + float rootInf = 1.0 / 0.0; + float rootNaN = 0.0 / 0.0; + float middleInf = rootInf + 1; + float middleNaN = rootNaN + 1; + castToInt(middleInf); // COMPLIANT + castToInt(middleNaN); // NON_COMPLIANT + addInfThenCastToInt(middleInf); // COMPLIANT + addNaNThenCastToInt(middleNaN); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/movedfromobjectsunspecifiedstate/MovedFromObjectsUnspecifiedState.ql b/cpp/common/test/rules/movedfromobjectsunspecifiedstate/MovedFromObjectsUnspecifiedState.ql index 47cfe4e798..3f818cc3e2 100644 --- a/cpp/common/test/rules/movedfromobjectsunspecifiedstate/MovedFromObjectsUnspecifiedState.ql +++ b/cpp/common/test/rules/movedfromobjectsunspecifiedstate/MovedFromObjectsUnspecifiedState.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.movedfromobjectsunspecifiedstate.MovedFromObjectsUnspecifiedState + +class TestFileQuery extends MovedFromObjectsUnspecifiedStateSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.expected b/cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.expected similarity index 100% rename from cpp/autosar/test/rules/M8-0-1/MultipleGlobalOrMemberDeclarators.expected rename to cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.expected diff --git a/cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.ql b/cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.ql new file mode 100644 index 0000000000..2f4d3cbdea --- /dev/null +++ b/cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.multipleglobalormemberdeclarators.MultipleGlobalOrMemberDeclarators + +class TestFileQuery extends MultipleGlobalOrMemberDeclaratorsSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/multipleglobalormemberdeclarators/test.cpp b/cpp/common/test/rules/multipleglobalormemberdeclarators/test.cpp new file mode 100644 index 0000000000..cf664e4b34 --- /dev/null +++ b/cpp/common/test/rules/multipleglobalormemberdeclarators/test.cpp @@ -0,0 +1,24 @@ +int g1, g2; // NON_COMPLIANT +int g3; // COMPLIANT + +namespace n1 { +int n_v1, n_v2; // NON_COMPLIANT +int n_v3; // COMPLIANT +} // namespace n1 + +void f() { + int l1, l2; // NON_COMPLIANT + int l3; // COMPLIANT +} + +class ClassA { + int m1, m2; // NON_COMPLIANT + int m3; // COMPLIANT +}; + +#include +void test_loop(std::vector v) { + for (const auto b : v) { // COMPLIANT - DeclStmt is compiler generated + b; + } +} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.expected b/cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.expected similarity index 100% rename from cpp/autosar/test/rules/M8-0-1/MultipleLocalDeclarators.expected rename to cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.expected diff --git a/cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.ql b/cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.ql new file mode 100644 index 0000000000..7e2fe57b24 --- /dev/null +++ b/cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.multiplelocaldeclarators.MultipleLocalDeclarators + +class TestFileQuery extends MultipleLocalDeclaratorsSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/multiplelocaldeclarators/test.cpp b/cpp/common/test/rules/multiplelocaldeclarators/test.cpp new file mode 100644 index 0000000000..cf664e4b34 --- /dev/null +++ b/cpp/common/test/rules/multiplelocaldeclarators/test.cpp @@ -0,0 +1,24 @@ +int g1, g2; // NON_COMPLIANT +int g3; // COMPLIANT + +namespace n1 { +int n_v1, n_v2; // NON_COMPLIANT +int n_v3; // COMPLIANT +} // namespace n1 + +void f() { + int l1, l2; // NON_COMPLIANT + int l3; // COMPLIANT +} + +class ClassA { + int m1, m2; // NON_COMPLIANT + int m3; // COMPLIANT +}; + +#include +void test_loop(std::vector v) { + for (const auto b : v) { // COMPLIANT - DeclStmt is compiler generated + b; + } +} \ No newline at end of file diff --git a/cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.expected b/cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.expected new file mode 100644 index 0000000000..8ddc10e90c --- /dev/null +++ b/cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.expected @@ -0,0 +1,5 @@ +| test.cpp:6:7:6:8 | x1 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.cpp:9:14:9:15 | x2 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.cpp:11:7:11:8 | x3 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.cpp:13:7:13:8 | x4 | A named bit-field with signed integral type should have at least 2 bits of storage. | +| test.cpp:22:14:22:14 | x | A named bit-field with signed integral type should have at least 2 bits of storage. | diff --git a/cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql b/cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql new file mode 100644 index 0000000000..a82fa7905a --- /dev/null +++ b/cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.namedbitfieldswithsignedintegertype.NamedBitFieldsWithSignedIntegerType + +class TestFileQuery extends NamedBitFieldsWithSignedIntegerTypeSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/namedbitfieldswithsignedintegertype/test.cpp b/cpp/common/test/rules/namedbitfieldswithsignedintegertype/test.cpp new file mode 100644 index 0000000000..0d6e838f83 --- /dev/null +++ b/cpp/common/test/rules/namedbitfieldswithsignedintegertype/test.cpp @@ -0,0 +1,28 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +struct SampleStruct { + int x1 : 1; // NON_COMPLIANT: very likely be signed, but if it's not, the + // query will automatically handle it since we use signed(), not + // isExplicitlySigned(). + signed int x2 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed char + x3 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed short + x4 : 1; // NON_COMPLIANT: single-bit named field with a signed type + unsigned int + x5 : 1; // COMPLIANT: single-bit named field but with an unsigned type + signed int x6 : 2; // COMPLIANT: named field with a signed type but declared + // to carry more than 1 bit + signed char : 1; // COMPLIANT: single-bit bit-field but unnamed +} sample_struct; + +struct S { + signed int x : 1; // NON-COMPLIANT + signed int y : 5; // COMPLIANT + signed int z : 7; // COMPLIANT + signed int : 0; // COMPLIANT + signed int : 1; // COMPLIANT + signed int : 2; // COMPLIANT +}; \ No newline at end of file diff --git a/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.expected b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.expected new file mode 100644 index 0000000000..1ea2cb3ab5 --- /dev/null +++ b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.expected @@ -0,0 +1,3 @@ +| test.cpp:16:5:16:5 | m | Use of unqualified identifier m targets $@ but a member with the name also exists $@. | test.cpp:4:5:4:5 | m | m | test.cpp:10:7:10:7 | m | in the dependent base class | +| test.cpp:17:5:17:5 | call to g | Use of unqualified identifier g targets $@ but a member with the name also exists $@. | test.cpp:2:6:2:6 | g | g | test.cpp:9:8:9:8 | g | in the dependent base class | +| test.cpp:19:20:19:20 | g | Use of unqualified identifier g targets $@ but a member with the name also exists $@. | test.cpp:2:6:2:6 | g | g | test.cpp:9:8:9:8 | g | in the dependent base class | diff --git a/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.ql b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.ql new file mode 100644 index 0000000000..731d7b1f84 --- /dev/null +++ b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.namenotreferredusingaqualifiedidorthis.NameNotReferredUsingAQualifiedIdOrThis + +class TestFileQuery extends NameNotReferredUsingAQualifiedIdOrThisSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/test.cpp b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/test.cpp new file mode 100644 index 0000000000..b16e6b40dc --- /dev/null +++ b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/test.cpp @@ -0,0 +1,87 @@ +typedef int TYPE; +void g(); +void g1(); +int m; + +template class B { +public: + typedef T TYPE; + void g(); + int m; +}; + +template class A : B { +public: + void m1() { + m = 0; // NON_COMPLIANT + g(); // NON_COMPLIANT + TYPE t = 0; // NON_COMPLIANT[FALSE_NEGATIVE] + void (*p)() = &g; // NON_COMPLIANT + } + void m2() { + ::m = 0; // COMPLIANT + ::g(); // COMPLIANT + ::TYPE t1 = 0; // COMPLIANT + B::m = 0; // COMPLIANT + this->m = 0; // COMPLIANT + this->g(); // COMPLIANT + void (B::*p)() = &B::g; // COMPLIANT + typename B::TYPE t2 = 0; // COMPLIANT + g1(); // COMPLIANT, identifier not found in B + } + void m3(int m) { + m = 0; // COMPLIANT, hides member + } + void m4() { + int m = 0; + m = 0; // COMPLIANT, hides member + } +}; + +void f() { + A a; + a.m1(); + a.m2(); + a.m3(1); + a.m4(); +} + +class D { +public: + typedef int TYPE; + void g(); + void g(int x); + static void sg(); + static void sg(int x); + int m; +}; + +class C : D { +public: + void m1() { + m = 0; // COMPLIANT - does not apply to non-class templates + g(); // COMPLIANT - does not apply to non-class templates + sg(); // COMPLIANT - does not apply to non-class templates + TYPE t1 = 0; // COMPLIANT - does not apply to non-class templates + // void (*p)() = &g; // NON_COMPILABLE - not valid to take address of member + // function without qualifier + } +}; + +template class E : D { +public: + void m1() { + m = 0; // COMPLIANT - does not apply to non dependent base types + g(); // COMPLIANT - does not apply to non dependent base types + TYPE t1 = 0; // COMPLIANT - does not apply to non dependent base types + // void (*p)() = &g; // NON_COMPILABLE - not valid to take address of member + // function without qualifier + } +}; + +void f2() { + C c; + c.m1(); + E e; + e.m1(); +} \ No newline at end of file diff --git a/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.expected b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.expected new file mode 100644 index 0000000000..1ea2cb3ab5 --- /dev/null +++ b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.expected @@ -0,0 +1,3 @@ +| test.cpp:16:5:16:5 | m | Use of unqualified identifier m targets $@ but a member with the name also exists $@. | test.cpp:4:5:4:5 | m | m | test.cpp:10:7:10:7 | m | in the dependent base class | +| test.cpp:17:5:17:5 | call to g | Use of unqualified identifier g targets $@ but a member with the name also exists $@. | test.cpp:2:6:2:6 | g | g | test.cpp:9:8:9:8 | g | in the dependent base class | +| test.cpp:19:20:19:20 | g | Use of unqualified identifier g targets $@ but a member with the name also exists $@. | test.cpp:2:6:2:6 | g | g | test.cpp:9:8:9:8 | g | in the dependent base class | diff --git a/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.ql b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.ql new file mode 100644 index 0000000000..46ffea0b3d --- /dev/null +++ b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.namenotreferredusingaqualifiedidorthisaudit.NameNotReferredUsingAQualifiedIdOrThisAudit + +class TestFileQuery extends NameNotReferredUsingAQualifiedIdOrThisAuditSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/test.cpp b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/test.cpp new file mode 100644 index 0000000000..b16e6b40dc --- /dev/null +++ b/cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/test.cpp @@ -0,0 +1,87 @@ +typedef int TYPE; +void g(); +void g1(); +int m; + +template class B { +public: + typedef T TYPE; + void g(); + int m; +}; + +template class A : B { +public: + void m1() { + m = 0; // NON_COMPLIANT + g(); // NON_COMPLIANT + TYPE t = 0; // NON_COMPLIANT[FALSE_NEGATIVE] + void (*p)() = &g; // NON_COMPLIANT + } + void m2() { + ::m = 0; // COMPLIANT + ::g(); // COMPLIANT + ::TYPE t1 = 0; // COMPLIANT + B::m = 0; // COMPLIANT + this->m = 0; // COMPLIANT + this->g(); // COMPLIANT + void (B::*p)() = &B::g; // COMPLIANT + typename B::TYPE t2 = 0; // COMPLIANT + g1(); // COMPLIANT, identifier not found in B + } + void m3(int m) { + m = 0; // COMPLIANT, hides member + } + void m4() { + int m = 0; + m = 0; // COMPLIANT, hides member + } +}; + +void f() { + A a; + a.m1(); + a.m2(); + a.m3(1); + a.m4(); +} + +class D { +public: + typedef int TYPE; + void g(); + void g(int x); + static void sg(); + static void sg(int x); + int m; +}; + +class C : D { +public: + void m1() { + m = 0; // COMPLIANT - does not apply to non-class templates + g(); // COMPLIANT - does not apply to non-class templates + sg(); // COMPLIANT - does not apply to non-class templates + TYPE t1 = 0; // COMPLIANT - does not apply to non-class templates + // void (*p)() = &g; // NON_COMPILABLE - not valid to take address of member + // function without qualifier + } +}; + +template class E : D { +public: + void m1() { + m = 0; // COMPLIANT - does not apply to non dependent base types + g(); // COMPLIANT - does not apply to non dependent base types + TYPE t1 = 0; // COMPLIANT - does not apply to non dependent base types + // void (*p)() = &g; // NON_COMPILABLE - not valid to take address of member + // function without qualifier + } +}; + +void f2() { + C c; + c.m1(); + E e; + e.m1(); +} \ No newline at end of file diff --git a/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected new file mode 100644 index 0000000000..58a238dbc4 --- /dev/null +++ b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected @@ -0,0 +1,3 @@ +| test.cpp:9:5:9:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.cpp:9:5:9:11 | case ...: | case ...: | test.cpp:6:3:17:3 | switch (...) ... | switch (...) ... | +| test.cpp:36:5:36:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.cpp:36:5:36:11 | case ...: | case ...: | test.cpp:23:3:43:3 | switch (...) ... | switch (...) ... | +| test.cpp:76:5:76:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.cpp:76:5:76:11 | case ...: | case ...: | test.cpp:73:3:79:3 | switch (...) ... | switch (...) ... | diff --git a/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql new file mode 100644 index 0000000000..3e0b1f7e8b --- /dev/null +++ b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch + +class TestFileQuery extends NestedLabelInSwitchSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nestedlabelinswitch/test.cpp b/cpp/common/test/rules/nestedlabelinswitch/test.cpp new file mode 100644 index 0000000000..5c04578f0b --- /dev/null +++ b/cpp/common/test/rules/nestedlabelinswitch/test.cpp @@ -0,0 +1,80 @@ +void f(); + +void f1(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + case 2: // NON_COMPLIANT + j; + break; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f2(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + case 3: // COMPLIANT + if (i) { + j; + case 4: // NON_COMPLIANT + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f3(int p1) { + + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + break; + case 3: // COMPLIANT + if (i) { + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f4(int p1) { + switch (p1) { + int i; + if (i) { + case 1: // NON_COMPLIANT + f(); + } + } +} diff --git a/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.expected b/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.expected new file mode 100644 index 0000000000..db392fd8f6 --- /dev/null +++ b/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.expected @@ -0,0 +1,19 @@ +problems +| test.cpp:4:6:4:15 | test_throw | test.cpp:5:3:5:20 | throw ... [ExceptionA] | test.cpp:4:6:4:15 | test_throw [ExceptionA] | Function test_throw is declared noexcept(true) but can throw exceptions of type ExceptionA. | +| test.cpp:10:6:10:27 | noexceptIndirectThrowA | test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:10:6:10:27 | noexceptIndirectThrowA [ExceptionA] | Function noexceptIndirectThrowA is declared noexcept(true) but can throw exceptions of type ExceptionA. | +| test.cpp:12:6:12:24 | test_indirect_throw | test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:12:6:12:24 | test_indirect_throw [ExceptionA] | Function test_indirect_throw is declared noexcept(true) but can throw exceptions of type ExceptionA. | +| test.cpp:16:6:16:26 | test_indirect_throw_2 | test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:16:6:16:26 | test_indirect_throw_2 [ExceptionA] | Function test_indirect_throw_2 is declared noexcept(true) but can throw exceptions of type ExceptionA. | +| test.cpp:33:6:33:26 | test_indirect_throw_6 | test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:33:6:33:26 | test_indirect_throw_6 [ExceptionA] | Function test_indirect_throw_6 is declared noexcept(true) but can throw exceptions of type ExceptionA. | +edges +| test.cpp:5:3:5:20 | throw ... [ExceptionA] | test.cpp:4:6:4:15 | test_throw [ExceptionA] | +| test.cpp:8:6:8:11 | throwA [ExceptionA] | test.cpp:9:25:9:30 | call to throwA [ExceptionA] | +| test.cpp:8:6:8:11 | throwA [ExceptionA] | test.cpp:10:42:10:47 | call to throwA [ExceptionA] | +| test.cpp:8:6:8:11 | throwA [ExceptionA] | test.cpp:13:3:13:8 | call to throwA [ExceptionA] | +| test.cpp:8:6:8:11 | throwA [ExceptionA] | test.cpp:17:3:17:8 | call to throwA [ExceptionA] | +| test.cpp:8:17:8:34 | throw ... [ExceptionA] | test.cpp:8:6:8:11 | throwA [ExceptionA] | +| test.cpp:9:6:9:19 | indirectThrowA [ExceptionA] | test.cpp:34:3:34:16 | call to indirectThrowA [ExceptionA] | +| test.cpp:9:25:9:30 | call to throwA [ExceptionA] | test.cpp:9:6:9:19 | indirectThrowA [ExceptionA] | +| test.cpp:10:42:10:47 | call to throwA [ExceptionA] | test.cpp:10:6:10:27 | noexceptIndirectThrowA [ExceptionA] | +| test.cpp:13:3:13:8 | call to throwA [ExceptionA] | test.cpp:12:6:12:24 | test_indirect_throw [ExceptionA] | +| test.cpp:17:3:17:8 | call to throwA [ExceptionA] | test.cpp:16:6:16:26 | test_indirect_throw_2 [ExceptionA] | +| test.cpp:34:3:34:16 | call to indirectThrowA [ExceptionA] | test.cpp:33:6:33:26 | test_indirect_throw_6 [ExceptionA] | diff --git a/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.ql b/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.ql new file mode 100644 index 0000000000..e8906287da --- /dev/null +++ b/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.noexceptfunctionshouldnotpropagatetothecaller.NoexceptFunctionShouldNotPropagateToTheCaller + +class TestFileQuery extends NoexceptFunctionShouldNotPropagateToTheCallerSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/test.cpp b/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/test.cpp new file mode 100644 index 0000000000..de5e00bd35 --- /dev/null +++ b/cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/test.cpp @@ -0,0 +1,35 @@ + +class ExceptionA {}; + +void test_throw() noexcept(true) { + throw ExceptionA(); // NON_COMPLIANT - function marked as noexcept(true) +} + +void throwA() { throw ExceptionA(); } +void indirectThrowA() { throwA(); } +void noexceptIndirectThrowA() noexcept { throwA(); } // NON_COMPLIANT + +void test_indirect_throw() noexcept(true) { + throwA(); // NON_COMPLIANT - function marked as noexcept(true) +} + +void test_indirect_throw_2() noexcept { + throwA(); // NON_COMPLIANT - function marked as noexcept(true) +} + +void test_indirect_throw_3() noexcept(false) { + throwA(); // COMPLIANT - function marked as noexcept(false) +} + +void test_indirect_throw_4() { + throwA(); // COMPLIANT - function marked as noexcept(false) +} + +void test_indirect_throw_5() noexcept { + noexceptIndirectThrowA(); // COMPLIANT - noexceptIndirectThrowA would call + // std::terminate() if ExceptionA is thrown +} + +void test_indirect_throw_6() noexcept { + indirectThrowA(); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.expected b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.expected new file mode 100644 index 0000000000..f3899bf81c --- /dev/null +++ b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.expected @@ -0,0 +1,3 @@ +| test.cpp:9:7:9:7 | i | If condition has non boolean type int. | +| test.cpp:11:7:11:7 | call to f | If condition has non boolean type int. | +| test.cpp:14:7:14:7 | a | If condition has non boolean type void *. | diff --git a/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql new file mode 100644 index 0000000000..2e27365953 --- /dev/null +++ b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonbooleanifstmt.NonBooleanIfStmt + +class TestFileQuery extends NonBooleanIfStmtSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nonbooleanifstmt/test.cpp b/cpp/common/test/rules/nonbooleanifstmt/test.cpp new file mode 100644 index 0000000000..e17f294a2f --- /dev/null +++ b/cpp/common/test/rules/nonbooleanifstmt/test.cpp @@ -0,0 +1,61 @@ + +#include + +int f(); +void *g(); + +void test_non_boolean_conditions() { + int i = 1; + if (i) { // NON_COMPLIANT + } + if (f()) { // NON_COMPLIANT + } + void *a = g(); + if (a) { // NON_COMPLIANT + } +} + +class ClassA { +public: + explicit operator bool() const { return true; } +}; + +void test_boolean_conditions() { + int i = 1; + if ((bool)i) { // COMPLIANT + } + + int n = 1; + if ((const bool)n) { // COMPLIANT - handles accesses of const bool + } + + const bool constj = true; + const bool constk = false; + if (n && (constj <= constk)) { // COMPLIANT - handles accesses of const bool + } + + bool j = true; + bool k = false; + if (i && (j <= k)) { // COMPLIANT - because of C++ standard + } + + if (int i = 0) { // COMPLIANT - due to exception + } + + ClassA a; + if (a) { // COMPLIANT - a has an explicit operator bool() + } +} + +template bool test_fp_reported_in_10a(T &p1) { + if (p1.length() > 10) { // COMPLIANT + return true; + } + return false; +} + +#include +void test_fp_reported_in_10b() { + std::string s; + test_fp_reported_in_10a(s); +} \ No newline at end of file diff --git a/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.expected b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.expected new file mode 100644 index 0000000000..05dfadc1f7 --- /dev/null +++ b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.expected @@ -0,0 +1,2 @@ +| test.cpp:7:3:9:3 | for(...;...;...) ... | Iteration condition has non boolean type int. | +| test.cpp:11:3:13:3 | while (...) ... | Iteration condition has non boolean type int. | diff --git a/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql new file mode 100644 index 0000000000..46c2d4c3bb --- /dev/null +++ b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonbooleaniterationstmt.NonBooleanIterationStmt + +class TestFileQuery extends NonBooleanIterationStmtSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nonbooleaniterationstmt/test.cpp b/cpp/common/test/rules/nonbooleaniterationstmt/test.cpp new file mode 100644 index 0000000000..04afb042b1 --- /dev/null +++ b/cpp/common/test/rules/nonbooleaniterationstmt/test.cpp @@ -0,0 +1,57 @@ +#include + +int f(); +void *g(); +void test_non_boolean_iterations() { + int j; + for (int i = 10; i; i++) { // NON_COMPLIANT + j = 3; + } + + while (j) { // NON_COMPLIANT + int k = 3; + } +} + +void test_boolean_iterations() { + int j = 0; + for (int i = 0; i < 10; i++) { // COMPLIANT + j = i + j; + } +} + +template class ClassB { +public: + std::deque d; + void f() { + if (d.empty()) { // COMPLIANT + } + } +}; + +void class_b_test() { + ClassB b; + + b.f(); +} + +class ClassC { + void run() { + std::deque d; + if (!d.empty()) { // COMPLIANT + } + } +}; + +#include +template void test_fp_reported_in_10a(std::vector &p1) { + for (typename std::vector::iterator it = p1.begin(); it != p1.end(); + ++it) { // COMPLIANT + (*it)++; + } +} + +void test_fp_reported_in_10b() { + std::vector vl1; + test_fp_reported_in_10a(vl1); +} \ No newline at end of file diff --git a/cpp/common/test/rules/nonconstantformat/NonConstantFormat.ql b/cpp/common/test/rules/nonconstantformat/NonConstantFormat.ql index 7a92b544e2..25750ae9e5 100644 --- a/cpp/common/test/rules/nonconstantformat/NonConstantFormat.ql +++ b/cpp/common/test/rules/nonconstantformat/NonConstantFormat.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.nonconstantformat.NonConstantFormat + +class TestFileQuery extends NonConstantFormatSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.expected b/cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.expected similarity index 100% rename from cpp/autosar/test/rules/M7-3-2/IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain.expected rename to cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.expected diff --git a/cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.ql b/cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.ql new file mode 100644 index 0000000000..02edcf3732 --- /dev/null +++ b/cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonglobalfunctionmain.NonGlobalFunctionMain + +class TestFileQuery extends NonGlobalFunctionMainSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M7-3-2/test.cpp b/cpp/common/test/rules/nonglobalfunctionmain/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M7-3-2/test.cpp rename to cpp/common/test/rules/nonglobalfunctionmain/test.cpp diff --git a/cpp/common/test/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.ql b/cpp/common/test/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.ql index 847a6c26f0..3b10c31026 100644 --- a/cpp/common/test/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.ql +++ b/cpp/common/test/rules/nonstandardentitiesinstandardnamespaces/NonStandardEntitiesInStandardNamespaces.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.nonstandardentitiesinstandardnamespaces.NonStandardEntitiesInStandardNamespaces + +class TestFileQuery extends NonStandardEntitiesInStandardNamespacesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.expected b/cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.expected new file mode 100644 index 0000000000..3051e32537 --- /dev/null +++ b/cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.expected @@ -0,0 +1,21 @@ +| test.cpp:37:18:37:24 | \u001aG | Invalid hexadecimal escape in string literal at '\\x1AG"'. | +| test.cpp:40:18:40:23 | \u00029 | Invalid octal escape in string literal at '\\029"'. | +| test.cpp:43:18:43:24 | \n7 | Invalid octal escape in string literal at '\\0127"'. | +| test.cpp:44:18:44:24 | \r7 | Invalid octal escape in string literal at '\\0157"'. | +| test.cpp:46:19:46:29 | \n\n9 | Invalid octal escape in string literal at '\\0129"'. | +| test.cpp:47:19:47:28 | \n\u00019 | Invalid octal escape in string literal at '\\019"'. | +| test.cpp:50:19:50:31 | \nAAA\u000f | Invalid octal escape in string literal at '\\012AAA\\017"'. | +| test.cpp:53:19:53:39 | Some Data \n\u000fA | Invalid octal escape in string literal at '\\017A"'. | +| test.cpp:54:19:55:21 | Some Data \n\u000fA5 | Invalid octal escape in string literal at '\\017A"\n "5"'. | +| test.cpp:56:19:58:25 | Some Data \n\u000fA\n1 | Invalid octal escape in string literal at '\\0121"'. | +| test.cpp:62:19:63:26 | \u0011G\u00012 | Invalid octal escape in string literal at '\\0012"'. | +| test.cpp:64:19:65:25 | \u0011GG\u0001 | Invalid hexadecimal escape in string literal at '\\x11G"\n "G\\001"'. | +| test.cpp:66:19:67:26 | \u0011GG\u00013 | Invalid hexadecimal escape in string literal at '\\x11G"\n "G\\0013"'. | +| test.cpp:66:19:67:26 | \u0011GG\u00013 | Invalid octal escape in string literal at '\\0013"'. | +| test.cpp:73:18:73:42 | Some Data \n\u000fA5 | Invalid octal escape in string literal at '\\017A" "5"'. | +| test.cpp:74:18:74:49 | Some Data \n\u000fA\n1 | Invalid octal escape in string literal at '\\0121"'. | +| test.cpp:76:18:76:32 | \u0011G\u00012 | Invalid octal escape in string literal at '\\0012"'. | +| test.cpp:77:18:77:32 | \u0011GG\u0001 | Invalid hexadecimal escape in string literal at '\\x11G" "G\\001"'. | +| test.cpp:78:18:78:33 | \u0011GG\u00013 | Invalid hexadecimal escape in string literal at '\\x11G" "G\\0013"'. | +| test.cpp:78:18:78:33 | \u0011GG\u00013 | Invalid octal escape in string literal at '\\0013"'. | +| test.cpp:81:11:81:16 | 10 | Invalid hexadecimal escape in string literal at '\\x0a''. | diff --git a/cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql b/cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql new file mode 100644 index 0000000000..c1aae3c31b --- /dev/null +++ b/cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonterminatedescapesequences.NonTerminatedEscapeSequences + +class TestFileQuery extends NonTerminatedEscapeSequencesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nonterminatedescapesequences/test.cpp b/cpp/common/test/rules/nonterminatedescapesequences/test.cpp new file mode 100644 index 0000000000..aa1093b791 --- /dev/null +++ b/cpp/common/test/rules/nonterminatedescapesequences/test.cpp @@ -0,0 +1,81 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + +struct SampleStruct { + int x1 : 1; // NON_COMPLIANT: very likely be signed, but if it's not, the + // query will automatically handle it since we use signed(), not + // isExplicitlySigned(). + signed int x2 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed char + x3 : 1; // NON_COMPLIANT: single-bit named field with a signed type + signed short + x4 : 1; // NON_COMPLIANT: single-bit named field with a signed type + unsigned int + x5 : 1; // COMPLIANT: single-bit named field but with an unsigned type + signed int x6 : 2; // COMPLIANT: named field with a signed type but declared + // to carry more than 1 bit + signed char : 1; // COMPLIANT: single-bit bit-field but unnamed +} sample_struct; + +struct S { + signed int x : 1; // NON-COMPLIANT + signed int y : 5; // COMPLIANT + signed int z : 7; // COMPLIANT + signed int : 0; // COMPLIANT + signed int : 1; // COMPLIANT + signed int : 2; // COMPLIANT +}; +const char *a1 = "\x11" + "G"; // COMPLIANT + +const char *a2 = "\x1" + "G"; // COMPLIANT + +const char *a3 = "\x1A"; // COMPLIANT + +const char *a4 = "\x1AG"; // NON_COMPLIANT + +const char *a5 = "\021"; // COMPLIANT +const char *a6 = "\029"; // NON_COMPLIANT +const char *a7 = "\0" + "0"; // COMPLIANT +const char *a8 = "\0127"; // NON_COMPLIANT +const char *a9 = "\0157"; // NON_COMPLIANT + +const char *a10 = "\012\0129"; // NON_COMPLIANT (1x) +const char *a11 = "\012\019"; // NON_COMPLIANT +const char *a12 = "\012\017"; // COMPLIANT + +const char *a13 = "\012AAA\017"; // NON_COMPLIANT (1x) + +const char *a14 = "Some Data \012\017"; // COMPLIANT +const char *a15 = "Some Data \012\017A"; // NON_COMPLIANT (1x) +const char *a16 = "Some Data \012\017A" + "5"; // NON_COMPLIANT (1x) +const char *a17 = "Some Data \012\017" + "A" + "\0121"; // NON_COMPLIANT (1x) + +const char *a18 = "\x11" + "G\001"; // COMPLIANT +const char *a19 = "\x11" + "G\0012"; // NON_COMPLIANT (1x) +const char *a20 = "\x11G" + "G\001"; // NON_COMPLIANT (1x) +const char *a21 = "\x11G" + "G\0013"; // NON_COMPLIANT (2x) + +// clang-format off +const char *b1 = "\x11" "G"; // COMPLIANT +const char *b2 = "\x1" "G"; // COMPLIANT +const char *b3 = "\0" "0"; // COMPLIANT +const char *b4 = "Some Data \012\017A" "5"; // NON_COMPLIANT (1x) +const char *b5 = "Some Data \012\017" "A" "\0121"; // NON_COMPLIANT (1x) +const char *b6 = "\x11" "G\001"; // COMPLIANT +const char *b7 = "\x11" "G\0012"; // NON_COMPLIANT (1x) +const char *b8 = "\x11G" "G\001"; // NON_COMPLIANT (1x) +const char *b9 = "\x11G" "G\0013"; // NON_COMPLIANT (2x) + +char c1 = '\023'; // COMPLIANT +char c2 = '\x0a'; // COMPLIANT diff --git a/cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.expected b/cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.expected new file mode 100644 index 0000000000..662a21b5d6 --- /dev/null +++ b/cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.expected @@ -0,0 +1 @@ +| test.cpp:5:19:5:20 | c4 | Nonunique value of enum constant compared to $@ | test.cpp:5:23:5:24 | c5 | c5 | diff --git a/cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql b/cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql new file mode 100644 index 0000000000..97ba6f516e --- /dev/null +++ b/cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonuniqueenumerationconstant.NonUniqueEnumerationConstant + +class TestFileQuery extends NonUniqueEnumerationConstantSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nonuniqueenumerationconstant/test.cpp b/cpp/common/test/rules/nonuniqueenumerationconstant/test.cpp new file mode 100644 index 0000000000..0712cb59e4 --- /dev/null +++ b/cpp/common/test/rules/nonuniqueenumerationconstant/test.cpp @@ -0,0 +1,6 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C++ TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +enum e { c = 3 }; // COMPLIANT +enum e1 { c1 = 3, c2 }; // COMPLIANT +enum e3 { c3 = 3, c4, c5 = 4 }; // NON_COMPLIANT +enum e4 { c6 = 3, c7, c8, c9 = 6 }; // COMPLIANT \ No newline at end of file diff --git a/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected b/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected index 10d5ff7976..774d7fa9e4 100644 --- a/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected +++ b/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.expected @@ -1,4 +1,4 @@ -| test.cpp:6:1:6:1 | return ... | Function test_return_f1 should return a value of type int but does not return a value here | -| test.cpp:16:27:16:28 | { ... } | Function test_return_f3 should return a value of type int but does not return a value here | -| test.cpp:33:1:33:1 | return ... | Function test_return_f5 should return a value of type int but does not return a value here | -| test.cpp:51:19:52:3 | { ... } | Function test_trycatch_f1 should return a value of type int but does not return a value here | +| test.cpp:8:1:8:1 | return ... | Function test_return_f1 should return a value of type int but does not return a value here | +| test.cpp:18:27:18:28 | { ... } | Function test_return_f3 should return a value of type int but does not return a value here | +| test.cpp:35:1:35:1 | return ... | Function test_return_f5 should return a value of type int but does not return a value here | +| test.cpp:53:19:54:3 | { ... } | Function test_trycatch_f1 should return a value of type int but does not return a value here | diff --git a/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql b/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql index 9b7236a26f..bcf99b44e7 100644 --- a/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql +++ b/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.nonvoidfunctiondoesnotreturn.NonVoidFunctionDoesNotReturn + +class TestFileQuery extends NonVoidFunctionDoesNotReturnSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/test.cpp b/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/test.cpp index 1be558a89c..a47c4030b2 100644 --- a/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/test.cpp +++ b/cpp/common/test/rules/nonvoidfunctiondoesnotreturn/test.cpp @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. #include int test_return_f1(int i) { // NON_COMPLIANT if (i > 100) { diff --git a/cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.expected b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected similarity index 100% rename from cpp/autosar/test/rules/A4-10-1/NullPointerConstantNotNullptr.expected rename to cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.clang b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.clang new file mode 100644 index 0000000000..1d7a675b05 --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.clang @@ -0,0 +1,3 @@ +| test.cpp:10:13:10:13 | 0 | 0 is used as the null-pointer-constant but is not nullptr. | +| test.cpp:11:6:11:6 | 0 | 0 is used as the null-pointer-constant but is not nullptr. | +| test.cpp:17:6:17:9 | 0 | NULL is used as the null-pointer-constant but is not nullptr. | diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.gcc b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.gcc new file mode 100644 index 0000000000..1d7a675b05 --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.gcc @@ -0,0 +1,3 @@ +| test.cpp:10:13:10:13 | 0 | 0 is used as the null-pointer-constant but is not nullptr. | +| test.cpp:11:6:11:6 | 0 | 0 is used as the null-pointer-constant but is not nullptr. | +| test.cpp:17:6:17:9 | 0 | NULL is used as the null-pointer-constant but is not nullptr. | diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.qcc b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.qcc new file mode 100644 index 0000000000..1d7a675b05 --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.expected.qcc @@ -0,0 +1,3 @@ +| test.cpp:10:13:10:13 | 0 | 0 is used as the null-pointer-constant but is not nullptr. | +| test.cpp:11:6:11:6 | 0 | 0 is used as the null-pointer-constant but is not nullptr. | +| test.cpp:17:6:17:9 | 0 | NULL is used as the null-pointer-constant but is not nullptr. | diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql new file mode 100644 index 0000000000..e3d6c4841f --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nullptrnottheonlyformofthenullpointerconstant.NullptrNotTheOnlyFormOfTheNullPointerConstant + +class TestFileQuery extends NullptrNotTheOnlyFormOfTheNullPointerConstantSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A4-10-1/test.cpp b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A4-10-1/test.cpp rename to cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.clang b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.clang new file mode 100644 index 0000000000..ca398adfc8 --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.clang @@ -0,0 +1,20 @@ +void f1(int *x); +void f2(int x); +void f3(char *x); +// Template function which forwards to a pointer function +template void f3(F f, X x) { f1(x); } + +#define NULL 0 + +void test_nullptr() { + int *l1 = 0; // NON_COMPLIANT - 0 converted to a pointer type + f1(0); // NON_COMPLIANT - 0 converted to a pointer type + int *l2 = nullptr; // COMPLIANT - use of nullptr + f1(nullptr); // COMPLIANT - use of nullptr + f2(0); // COMPLIANT - use of 0 literal with no conversion to pointer + int l3 = 0; // COMPLIANT - use of 0 literal with no conversion to pointer + f3(f1, nullptr); // COMPLIANT - use of nullptr + f1(NULL); // NON_COMPLIANT - use of NULL macro + // f1('\0'); // NON_COMPLIANT - use of octal escape 0 - this is compiler checked + f3("0"); // COMPLIANT - "0" is not a literal zero +} \ No newline at end of file diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.gcc b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.gcc new file mode 100644 index 0000000000..ca398adfc8 --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.gcc @@ -0,0 +1,20 @@ +void f1(int *x); +void f2(int x); +void f3(char *x); +// Template function which forwards to a pointer function +template void f3(F f, X x) { f1(x); } + +#define NULL 0 + +void test_nullptr() { + int *l1 = 0; // NON_COMPLIANT - 0 converted to a pointer type + f1(0); // NON_COMPLIANT - 0 converted to a pointer type + int *l2 = nullptr; // COMPLIANT - use of nullptr + f1(nullptr); // COMPLIANT - use of nullptr + f2(0); // COMPLIANT - use of 0 literal with no conversion to pointer + int l3 = 0; // COMPLIANT - use of 0 literal with no conversion to pointer + f3(f1, nullptr); // COMPLIANT - use of nullptr + f1(NULL); // NON_COMPLIANT - use of NULL macro + // f1('\0'); // NON_COMPLIANT - use of octal escape 0 - this is compiler checked + f3("0"); // COMPLIANT - "0" is not a literal zero +} \ No newline at end of file diff --git a/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.qcc b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.qcc new file mode 100644 index 0000000000..ca398adfc8 --- /dev/null +++ b/cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/test.cpp.qcc @@ -0,0 +1,20 @@ +void f1(int *x); +void f2(int x); +void f3(char *x); +// Template function which forwards to a pointer function +template void f3(F f, X x) { f1(x); } + +#define NULL 0 + +void test_nullptr() { + int *l1 = 0; // NON_COMPLIANT - 0 converted to a pointer type + f1(0); // NON_COMPLIANT - 0 converted to a pointer type + int *l2 = nullptr; // COMPLIANT - use of nullptr + f1(nullptr); // COMPLIANT - use of nullptr + f2(0); // COMPLIANT - use of 0 literal with no conversion to pointer + int l3 = 0; // COMPLIANT - use of 0 literal with no conversion to pointer + f3(f1, nullptr); // COMPLIANT - use of nullptr + f1(NULL); // NON_COMPLIANT - use of NULL macro + // f1('\0'); // NON_COMPLIANT - use of octal escape 0 - this is compiler checked + f3("0"); // COMPLIANT - "0" is not a literal zero +} \ No newline at end of file diff --git a/cpp/common/test/rules/objectaccessedafterlifetime/ObjectAccessedAfterLifetime.ql b/cpp/common/test/rules/objectaccessedafterlifetime/ObjectAccessedAfterLifetime.ql index 07ad7ea1f2..fbf2270fb9 100644 --- a/cpp/common/test/rules/objectaccessedafterlifetime/ObjectAccessedAfterLifetime.ql +++ b/cpp/common/test/rules/objectaccessedafterlifetime/ObjectAccessedAfterLifetime.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.objectaccessedafterlifetime.ObjectAccessedAfterLifetime + +class TestFileQuery extends ObjectAccessedAfterLifetimeSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/objectaccessedbeforelifetime/ObjectAccessedBeforeLifetime.ql b/cpp/common/test/rules/objectaccessedbeforelifetime/ObjectAccessedBeforeLifetime.ql index 4eeb0ac98b..aa88f954dc 100644 --- a/cpp/common/test/rules/objectaccessedbeforelifetime/ObjectAccessedBeforeLifetime.ql +++ b/cpp/common/test/rules/objectaccessedbeforelifetime/ObjectAccessedBeforeLifetime.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.objectaccessedbeforelifetime.ObjectAccessedBeforeLifetime + +class TestFileQuery extends ObjectAccessedBeforeLifetimeSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.expected b/cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.expected similarity index 100% rename from cpp/autosar/test/rules/M12-1-1/DynamicTypeOfThisUsedFromConstructorOrDestructor.expected rename to cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.expected diff --git a/cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql b/cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql new file mode 100644 index 0000000000..151af6a5a3 --- /dev/null +++ b/cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.objectsdynamictypeusedfromconstructorordestructor.ObjectsDynamicTypeUsedFromConstructorOrDestructor + +class TestFileQuery extends ObjectsDynamicTypeUsedFromConstructorOrDestructorSharedQuery, TestQuery { +} diff --git a/cpp/autosar/test/rules/M12-1-1/test.cpp b/cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M12-1-1/test.cpp rename to cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/test.cpp diff --git a/cpp/common/test/rules/onedefinitionruleviolation/OneDefinitionRuleViolation.ql b/cpp/common/test/rules/onedefinitionruleviolation/OneDefinitionRuleViolation.ql index ff17ce344e..0f01e0b871 100644 --- a/cpp/common/test/rules/onedefinitionruleviolation/OneDefinitionRuleViolation.ql +++ b/cpp/common/test/rules/onedefinitionruleviolation/OneDefinitionRuleViolation.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.onedefinitionruleviolation.OneDefinitionRuleViolation + +class TestFileQuery extends OneDefinitionRuleViolationSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/operationmaynotnullterminatecstylestring/OperationMayNotNullTerminateCStyleString.ql b/cpp/common/test/rules/operationmaynotnullterminatecstylestring/OperationMayNotNullTerminateCStyleString.ql index dc30d3e41b..88637e5fb8 100644 --- a/cpp/common/test/rules/operationmaynotnullterminatecstylestring/OperationMayNotNullTerminateCStyleString.ql +++ b/cpp/common/test/rules/operationmaynotnullterminatecstylestring/OperationMayNotNullTerminateCStyleString.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.operationmaynotnullterminatecstylestring.OperationMayNotNullTerminateCStyleString + +class TestFileQuery extends OperationMayNotNullTerminateCStyleStringSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/operationmaynotnullterminatecstylestring/test.cpp b/cpp/common/test/rules/operationmaynotnullterminatecstylestring/test.cpp index 1b35da9174..7e93427905 100644 --- a/cpp/common/test/rules/operationmaynotnullterminatecstylestring/test.cpp +++ b/cpp/common/test/rules/operationmaynotnullterminatecstylestring/test.cpp @@ -1,7 +1,7 @@ #pragma clang diagnostic ignored "-Wfortify-source" #include +#include #include -#include void f1() { char a1[7] = "CodeQL"; diff --git a/cpp/common/test/rules/operatordeletemissingpartner/OperatorDeleteMissingPartner.ql b/cpp/common/test/rules/operatordeletemissingpartner/OperatorDeleteMissingPartner.ql index 37f920e11d..df5ed195c3 100644 --- a/cpp/common/test/rules/operatordeletemissingpartner/OperatorDeleteMissingPartner.ql +++ b/cpp/common/test/rules/operatordeletemissingpartner/OperatorDeleteMissingPartner.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.operatordeletemissingpartner.OperatorDeleteMissingPartner + +class TestFileQuery extends OperatorDeleteMissingPartnerSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.expected b/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.expected index 5ff6f9eee3..11f98068fa 100644 --- a/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.expected +++ b/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.expected @@ -1,30 +1,30 @@ -| test.cpp:27:39:27:41 | definition of s1a | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:28:42:28:44 | definition of s1b | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:36:39:36:41 | definition of s1a | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:37:42:37:44 | definition of s1b | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:41:44:41:46 | definition of s2a | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:42:47:42:49 | definition of s2b | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:46:44:46:46 | definition of m1a | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:47:47:47:49 | definition of m1b | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:51:49:51:51 | definition of m2a | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:52:52:52:54 | definition of m2b | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:58:15:58:16 | definition of s1 | Comparator 'std::less
' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:60:7:60:8 | definition of s2 | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:62:7:62:8 | definition of s3 | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:64:7:64:8 | definition of s4 | Comparator 'std::greater' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:66:7:66:8 | definition of s5 | Comparator 'std::less' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:81:3:81:11 | call to sort | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:84:3:84:11 | call to sort | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:105:3:105:11 | call to sort | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:108:3:108:11 | call to sort | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:120:3:120:18 | call to stable_sort | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:123:3:123:18 | call to stable_sort | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:135:3:135:18 | call to stable_sort | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:138:3:138:18 | call to stable_sort | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:153:3:153:19 | call to partial_sort | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:156:3:156:19 | call to partial_sort | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:168:3:168:19 | call to partial_sort | Comparator 'std::less_equal' used on container or sorting algorithm that is not strictly weakly ordered | -| test.cpp:171:3:171:19 | call to partial_sort | Comparator 'std::greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:27:39:27:41 | definition of s1a | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:28:42:28:44 | definition of s1b | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:36:39:36:41 | definition of s1a | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:37:42:37:44 | definition of s1b | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:41:44:41:46 | definition of s2a | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:42:47:42:49 | definition of s2b | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:46:44:46:46 | definition of m1a | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:47:47:47:49 | definition of m1b | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:51:49:51:51 | definition of m2a | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:52:52:52:54 | definition of m2b | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:58:15:58:16 | definition of s1 | Comparator 'less' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:60:7:60:8 | definition of s2 | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:62:7:62:8 | definition of s3 | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:64:7:64:8 | definition of s4 | Comparator 'greater' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:66:7:66:8 | definition of s5 | Comparator 'less' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:81:3:81:11 | call to sort | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:84:3:84:11 | call to sort | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:105:3:105:11 | call to sort | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:108:3:108:11 | call to sort | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:120:3:120:18 | call to stable_sort | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:123:3:123:18 | call to stable_sort | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:135:3:135:18 | call to stable_sort | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:138:3:138:18 | call to stable_sort | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:153:3:153:19 | call to partial_sort | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:156:3:156:19 | call to partial_sort | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:168:3:168:19 | call to partial_sort | Comparator 'less_equal' used on container or sorting algorithm that is not strictly weakly ordered | +| test.cpp:171:3:171:19 | call to partial_sort | Comparator 'greater_equal' used on container or sorting algorithm that is not strictly weakly ordered | | test.cpp:194:7:194:8 | definition of s1 | Comparator 'UnknownUserDefinedComparator' used on container or sorting algorithm that is not strictly weakly ordered | | test.cpp:196:7:196:8 | definition of s2 | Comparator 'UnknownUserDefinedComparator' used on container or sorting algorithm that is not strictly weakly ordered | | test.cpp:197:3:197:11 | call to sort | Comparator 'UnknownUserDefinedComparator' used on container or sorting algorithm that is not strictly weakly ordered | diff --git a/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.ql b/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.ql index 0020ba4207..765e11c79e 100644 --- a/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.ql +++ b/cpp/common/test/rules/orderingpredicatemustbestrictlyweak/OrderingPredicateMustBeStrictlyWeak.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.orderingpredicatemustbestrictlyweak.OrderingPredicateMustBeStrictlyWeak + +class TestFileQuery extends OrderingPredicateMustBeStrictlyWeakSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.expected b/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.expected new file mode 100644 index 0000000000..d2d80e0572 --- /dev/null +++ b/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.expected @@ -0,0 +1,2 @@ +| test.cpp:16:8:16:8 | f | Overriding function does not have the same default parameters as $@ | test.cpp:4:16:4:16 | f | overridden function | +| test.cpp:21:8:21:8 | f | Overriding function does not have the same default parameters as $@ | test.cpp:4:16:4:16 | f | overridden function | diff --git a/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.ql b/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.ql new file mode 100644 index 0000000000..2bb15bb684 --- /dev/null +++ b/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.overridingshallspecifydifferentdefaultarguments.OverridingShallSpecifyDifferentDefaultArguments + +class TestFileQuery extends OverridingShallSpecifyDifferentDefaultArgumentsSharedQuery, TestQuery { +} diff --git a/cpp/autosar/test/rules/M8-3-1/test.cpp b/cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M8-3-1/test.cpp rename to cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/test.cpp diff --git a/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected b/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected index a4f85ecb72..7790582443 100644 --- a/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected +++ b/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected @@ -6,47 +6,39 @@ problems | test.cpp:12:28:12:29 | v2 | test.cpp:10:8:10:17 | new | test.cpp:12:28:12:29 | v2 | Raw pointer flows to initialize multiple unrelated smart pointers. | | test.cpp:17:27:17:28 | v1 | test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | Raw pointer flows to initialize multiple unrelated smart pointers. | edges -| ../../includes/standard-library/memory.h:76:17:76:19 | ptr | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | -| test.cpp:3:14:3:15 | v1 | test.cpp:5:27:5:28 | v1 | -| test.cpp:3:14:3:15 | v1 | test.cpp:6:31:6:33 | call to get | -| test.cpp:3:14:3:15 | v1 | test.cpp:7:28:7:29 | v2 | -| test.cpp:4:13:4:14 | v1 | test.cpp:7:28:7:29 | v2 | -| test.cpp:5:27:5:29 | call to shared_ptr | test.cpp:6:31:6:33 | call to get | -| test.cpp:8:8:8:14 | 0 | test.cpp:9:28:9:29 | v2 | -| test.cpp:10:8:10:17 | new | test.cpp:11:28:11:29 | v2 | -| test.cpp:10:8:10:17 | new | test.cpp:11:28:11:29 | v2 | -| test.cpp:10:8:10:17 | new | test.cpp:12:28:12:29 | v2 | -| test.cpp:11:28:11:29 | ref arg v2 | test.cpp:12:28:12:29 | v2 | -| test.cpp:11:28:11:29 | v2 | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | -| test.cpp:11:28:11:29 | v2 | test.cpp:11:28:11:29 | ref arg v2 | -| test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | -| test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | -| test.cpp:16:13:16:22 | new | test.cpp:19:6:19:7 | v1 | -| test.cpp:17:27:17:28 | ref arg v1 | test.cpp:19:6:19:7 | v1 | -| test.cpp:17:27:17:28 | v1 | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | -| test.cpp:17:27:17:28 | v1 | test.cpp:17:27:17:28 | ref arg v1 | -| test.cpp:19:6:19:7 | v1 | test.cpp:3:14:3:15 | v1 | +| test.cpp:3:14:3:15 | v1 | test.cpp:5:27:5:28 | v1 | provenance | | +| test.cpp:3:14:3:15 | v1 | test.cpp:5:27:5:28 | v1 | provenance | | +| test.cpp:3:14:3:15 | v1 | test.cpp:7:28:7:29 | v2 | provenance | | +| test.cpp:4:13:4:14 | v1 | test.cpp:7:28:7:29 | v2 | provenance | | +| test.cpp:5:27:5:28 | v1 | test.cpp:5:27:5:29 | call to shared_ptr | provenance | | +| test.cpp:5:27:5:28 | v1 | test.cpp:5:27:5:29 | call to shared_ptr | provenance | Config | +| test.cpp:5:27:5:29 | call to shared_ptr | test.cpp:6:28:6:29 | p1 | provenance | | +| test.cpp:5:27:5:29 | call to shared_ptr | test.cpp:6:28:6:29 | p1 | provenance | | +| test.cpp:6:28:6:29 | p1 | test.cpp:6:31:6:33 | call to get | provenance | Config | +| test.cpp:6:28:6:29 | p1 | test.cpp:6:31:6:33 | call to get | provenance | Config | +| test.cpp:8:8:8:14 | 0 | test.cpp:9:28:9:29 | v2 | provenance | | +| test.cpp:10:8:10:17 | new | test.cpp:11:28:11:29 | v2 | provenance | | +| test.cpp:10:8:10:17 | new | test.cpp:12:28:12:29 | v2 | provenance | | +| test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | provenance | | +| test.cpp:16:13:16:22 | new | test.cpp:19:6:19:7 | v1 | provenance | | +| test.cpp:19:6:19:7 | v1 | test.cpp:3:14:3:15 | v1 | provenance | | nodes -| ../../includes/standard-library/memory.h:76:17:76:19 | ptr | semmle.label | ptr | -| ../../includes/standard-library/memory.h:76:17:76:19 | ptr | semmle.label | ptr | | test.cpp:3:14:3:15 | v1 | semmle.label | v1 | | test.cpp:4:13:4:14 | v1 | semmle.label | v1 | | test.cpp:5:27:5:28 | v1 | semmle.label | v1 | +| test.cpp:5:27:5:28 | v1 | semmle.label | v1 | +| test.cpp:5:27:5:29 | call to shared_ptr | semmle.label | call to shared_ptr | | test.cpp:5:27:5:29 | call to shared_ptr | semmle.label | call to shared_ptr | +| test.cpp:6:28:6:29 | p1 | semmle.label | p1 | +| test.cpp:6:28:6:29 | p1 | semmle.label | p1 | | test.cpp:6:31:6:33 | call to get | semmle.label | call to get | | test.cpp:7:28:7:29 | v2 | semmle.label | v2 | | test.cpp:8:8:8:14 | 0 | semmle.label | 0 | | test.cpp:9:28:9:29 | v2 | semmle.label | v2 | | test.cpp:10:8:10:17 | new | semmle.label | new | -| test.cpp:11:28:11:29 | ref arg v2 | semmle.label | ref arg v2 | -| test.cpp:11:28:11:29 | v2 | semmle.label | v2 | | test.cpp:11:28:11:29 | v2 | semmle.label | v2 | | test.cpp:12:28:12:29 | v2 | semmle.label | v2 | | test.cpp:16:13:16:22 | new | semmle.label | new | -| test.cpp:17:27:17:28 | ref arg v1 | semmle.label | ref arg v1 | -| test.cpp:17:27:17:28 | v1 | semmle.label | v1 | | test.cpp:17:27:17:28 | v1 | semmle.label | v1 | | test.cpp:19:6:19:7 | v1 | semmle.label | v1 | subpaths -| test.cpp:11:28:11:29 | v2 | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | test.cpp:11:28:11:29 | ref arg v2 | -| test.cpp:17:27:17:28 | v1 | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | ../../includes/standard-library/memory.h:76:17:76:19 | ptr | test.cpp:17:27:17:28 | ref arg v1 | diff --git a/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected.qcc b/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected.qcc new file mode 100644 index 0000000000..a579781f95 --- /dev/null +++ b/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.expected.qcc @@ -0,0 +1,102 @@ +problems +| test.cpp:5:27:5:28 | v1 | test.cpp:16:13:16:22 | new | test.cpp:5:27:5:28 | v1 | Raw pointer flows to initialize multiple unrelated smart pointers. | +| test.cpp:6:31:6:33 | call to get | test.cpp:16:13:16:22 | new | test.cpp:6:31:6:33 | call to get | Raw pointer flows to initialize multiple unrelated smart pointers. | +| test.cpp:7:28:7:29 | v2 | test.cpp:16:13:16:22 | new | test.cpp:7:28:7:29 | v2 | Raw pointer flows to initialize multiple unrelated smart pointers. | +| test.cpp:11:28:11:29 | v2 | test.cpp:10:8:10:17 | new | test.cpp:11:28:11:29 | v2 | Raw pointer flows to initialize multiple unrelated smart pointers. | +| test.cpp:12:28:12:29 | v2 | test.cpp:10:8:10:17 | new | test.cpp:12:28:12:29 | v2 | Raw pointer flows to initialize multiple unrelated smart pointers. | +| test.cpp:17:27:17:28 | v1 | test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | Raw pointer flows to initialize multiple unrelated smart pointers. | +edges +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:14:4063:16 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:19:3927:19 | this [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | this [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | this [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | __ptr_ | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:7:4063:17 | constructor init of field __ptr_ [post-this] [__ptr_] | test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:14:4063:16 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:7:4063:17 | constructor init of field __ptr_ [post-this] [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:14:4063:16 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:7:4063:17 | constructor init of field __ptr_ [post-this] [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | ref arg __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | ref arg __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | ref arg __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | ref arg __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | ref arg __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | +| test.cpp:3:14:3:15 | v1 | test.cpp:5:27:5:28 | v1 | +| test.cpp:3:14:3:15 | v1 | test.cpp:5:27:5:28 | v1 | +| test.cpp:3:14:3:15 | v1 | test.cpp:7:28:7:29 | v2 | +| test.cpp:4:13:4:14 | v1 | test.cpp:7:28:7:29 | v2 | +| test.cpp:5:27:5:28 | v1 | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | +| test.cpp:5:27:5:28 | v1 | test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | +| test.cpp:5:27:5:29 | call to shared_ptr | test.cpp:6:31:6:33 | call to get | +| test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | test.cpp:6:28:6:29 | p1 [__ptr_] | +| test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | test.cpp:6:28:6:29 | p1 [__ptr_] | +| test.cpp:6:28:6:29 | p1 [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:19:3927:19 | this [__ptr_] | +| test.cpp:6:28:6:29 | p1 [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:19:3927:19 | this [__ptr_] | +| test.cpp:6:28:6:29 | p1 [__ptr_] | test.cpp:6:31:6:33 | call to get | +| test.cpp:6:28:6:29 | p1 [__ptr_] | test.cpp:6:31:6:33 | call to get | +| test.cpp:8:8:8:14 | 0 | test.cpp:9:28:9:29 | v2 | +| test.cpp:10:8:10:17 | new | test.cpp:11:28:11:29 | v2 | +| test.cpp:10:8:10:17 | new | test.cpp:11:28:11:29 | v2 | +| test.cpp:10:8:10:17 | new | test.cpp:12:28:12:29 | v2 | +| test.cpp:11:28:11:29 | ref arg v2 | test.cpp:12:28:12:29 | v2 | +| test.cpp:11:28:11:29 | v2 | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | +| test.cpp:11:28:11:29 | v2 | test.cpp:11:28:11:29 | ref arg v2 | +| test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | +| test.cpp:16:13:16:22 | new | test.cpp:17:27:17:28 | v1 | +| test.cpp:16:13:16:22 | new | test.cpp:19:6:19:7 | v1 | +| test.cpp:17:27:17:28 | ref arg v1 | test.cpp:19:6:19:7 | v1 | +| test.cpp:17:27:17:28 | v1 | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | +| test.cpp:17:27:17:28 | v1 | test.cpp:17:27:17:28 | ref arg v1 | +| test.cpp:19:6:19:7 | v1 | test.cpp:3:14:3:15 | v1 | +nodes +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:19:3927:19 | this [__ptr_] | semmle.label | this [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | __ptr_ | semmle.label | __ptr_ | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | this [__ptr_] | semmle.label | this [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:7:4063:17 | constructor init of field __ptr_ [post-this] [__ptr_] | semmle.label | constructor init of field __ptr_ [post-this] [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:7:4063:17 | constructor init of field __ptr_ [post-this] [__ptr_] | semmle.label | constructor init of field __ptr_ [post-this] [__ptr_] | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:14:4063:16 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:14:4063:16 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | ref arg __p | semmle.label | ref arg __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | __p | semmle.label | __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | ref arg __p | semmle.label | ref arg __p | +| test.cpp:3:14:3:15 | v1 | semmle.label | v1 | +| test.cpp:4:13:4:14 | v1 | semmle.label | v1 | +| test.cpp:5:27:5:28 | v1 | semmle.label | v1 | +| test.cpp:5:27:5:28 | v1 | semmle.label | v1 | +| test.cpp:5:27:5:29 | call to shared_ptr | semmle.label | call to shared_ptr | +| test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | semmle.label | call to shared_ptr [__ptr_] | +| test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | semmle.label | call to shared_ptr [__ptr_] | +| test.cpp:6:28:6:29 | p1 [__ptr_] | semmle.label | p1 [__ptr_] | +| test.cpp:6:28:6:29 | p1 [__ptr_] | semmle.label | p1 [__ptr_] | +| test.cpp:6:31:6:33 | call to get | semmle.label | call to get | +| test.cpp:7:28:7:29 | v2 | semmle.label | v2 | +| test.cpp:8:8:8:14 | 0 | semmle.label | 0 | +| test.cpp:9:28:9:29 | v2 | semmle.label | v2 | +| test.cpp:10:8:10:17 | new | semmle.label | new | +| test.cpp:11:28:11:29 | ref arg v2 | semmle.label | ref arg v2 | +| test.cpp:11:28:11:29 | v2 | semmle.label | v2 | +| test.cpp:11:28:11:29 | v2 | semmle.label | v2 | +| test.cpp:12:28:12:29 | v2 | semmle.label | v2 | +| test.cpp:16:13:16:22 | new | semmle.label | new | +| test.cpp:17:27:17:28 | ref arg v1 | semmle.label | ref arg v1 | +| test.cpp:17:27:17:28 | v1 | semmle.label | v1 | +| test.cpp:17:27:17:28 | v1 | semmle.label | v1 | +| test.cpp:19:6:19:7 | v1 | semmle.label | v1 | +subpaths +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:2469:31:2469:33 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4065:28:4065:30 | ref arg __p | +| file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3611:30:3611:32 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4068:30:4068:32 | ref arg __p | +| test.cpp:5:27:5:28 | v1 | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:4063:7:4063:17 | constructor init of field __ptr_ [post-this] [__ptr_] | test.cpp:5:27:5:29 | call to shared_ptr [__ptr_] | +| test.cpp:6:28:6:29 | p1 [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:19:3927:19 | this [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | __ptr_ | test.cpp:6:31:6:33 | call to get | +| test.cpp:6:28:6:29 | p1 [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:19:3927:19 | this [__ptr_] | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3927:49:3927:54 | __ptr_ | test.cpp:6:31:6:33 | call to get | +| test.cpp:11:28:11:29 | v2 | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | test.cpp:11:28:11:29 | ref arg v2 | +| test.cpp:17:27:17:28 | v1 | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | file:///opt/qcc/qnx-sdp/target/qnx7/usr/include/c++/v1/memory:3757:34:3757:36 | __p | test.cpp:17:27:17:28 | ref arg v1 | diff --git a/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.ql b/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.ql index c6a85d318e..efdcb47a16 100644 --- a/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.ql +++ b/cpp/common/test/rules/ownedpointervaluestoredinunrelatedsmartpointer/OwnedPointerValueStoredInUnrelatedSmartPointer.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.ownedpointervaluestoredinunrelatedsmartpointer.OwnedPointerValueStoredInUnrelatedSmartPointer + +class TestFileQuery extends OwnedPointerValueStoredInUnrelatedSmartPointerSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.expected b/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.expected index 3c38e192bc..a4c3bb1df6 100644 --- a/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.expected +++ b/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.expected @@ -7,18 +7,18 @@ problems | test.cpp:100:16:100:39 | new | test.cpp:65:36:65:38 | call to pop | test.cpp:100:21:100:28 | badAlloc | Placement new expression is used with an insufficiently large memory allocation from $@. | test.cpp:65:36:65:38 | call to pop | call to pop | | test.cpp:113:7:113:32 | new[] | test.cpp:113:14:113:21 | badAlloc | test.cpp:113:14:113:21 | badAlloc | Placement new expression is used with an insufficiently large memory allocation from $@. | test.cpp:113:14:113:21 | badAlloc | badAlloc | edges -| test.cpp:18:36:18:49 | call to operator new | test.cpp:19:21:19:44 | correctlyAllocatedMemory | -| test.cpp:24:37:24:50 | call to operator new | test.cpp:25:21:25:45 | correctlyAllocatedMemory2 | -| test.cpp:29:32:29:45 | call to operator new | test.cpp:31:21:31:40 | badlyAllocatedMemory | -| test.cpp:35:33:35:46 | call to operator new | test.cpp:37:21:37:41 | badlyAllocatedMemory2 | -| test.cpp:62:16:62:29 | call to operator new | test.cpp:67:12:67:17 | memory | -| test.cpp:62:16:62:29 | call to operator new | test.cpp:67:12:67:17 | memory | -| test.cpp:65:36:65:38 | call to pop | test.cpp:67:12:67:17 | memory | -| test.cpp:65:36:65:38 | call to pop | test.cpp:67:12:67:17 | memory | -| test.cpp:67:12:67:17 | memory | test.cpp:94:32:94:39 | call to allocate | -| test.cpp:67:12:67:17 | memory | test.cpp:98:31:98:38 | call to allocate | -| test.cpp:94:32:94:39 | call to allocate | test.cpp:95:21:95:29 | goodAlloc | -| test.cpp:98:31:98:38 | call to allocate | test.cpp:100:21:100:28 | badAlloc | +| test.cpp:18:36:18:49 | call to operator new | test.cpp:19:21:19:44 | correctlyAllocatedMemory | provenance | | +| test.cpp:24:37:24:50 | call to operator new | test.cpp:25:21:25:45 | correctlyAllocatedMemory2 | provenance | | +| test.cpp:29:32:29:45 | call to operator new | test.cpp:31:21:31:40 | badlyAllocatedMemory | provenance | | +| test.cpp:35:33:35:46 | call to operator new | test.cpp:37:21:37:41 | badlyAllocatedMemory2 | provenance | | +| test.cpp:62:16:62:29 | call to operator new | test.cpp:67:12:67:17 | memory | provenance | | +| test.cpp:62:16:62:29 | call to operator new | test.cpp:67:12:67:17 | memory | provenance | | +| test.cpp:65:36:65:38 | call to pop | test.cpp:67:12:67:17 | memory | provenance | | +| test.cpp:65:36:65:38 | call to pop | test.cpp:67:12:67:17 | memory | provenance | | +| test.cpp:67:12:67:17 | memory | test.cpp:94:32:94:39 | call to allocate | provenance | | +| test.cpp:67:12:67:17 | memory | test.cpp:98:31:98:38 | call to allocate | provenance | | +| test.cpp:94:32:94:39 | call to allocate | test.cpp:95:21:95:29 | goodAlloc | provenance | | +| test.cpp:98:31:98:38 | call to allocate | test.cpp:100:21:100:28 | badAlloc | provenance | | nodes | test.cpp:18:36:18:49 | call to operator new | semmle.label | call to operator new | | test.cpp:19:21:19:44 | correctlyAllocatedMemory | semmle.label | correctlyAllocatedMemory | diff --git a/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.ql b/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.ql index 4f07c3df65..d63da2dc8d 100644 --- a/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.ql +++ b/cpp/common/test/rules/placementnewinsufficientstorage/PlacementNewInsufficientStorage.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.placementnewinsufficientstorage.PlacementNewInsufficientStorage + +class TestFileQuery extends PlacementNewInsufficientStorageSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.ql b/cpp/common/test/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.ql index 3214b16c59..913b1c9c66 100644 --- a/cpp/common/test/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.ql +++ b/cpp/common/test/rules/placementnewnotproperlyaligned/PlacementNewNotProperlyAligned.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.placementnewnotproperlyaligned.PlacementNewNotProperlyAligned + +class TestFileQuery extends PlacementNewNotProperlyAlignedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.expected b/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.expected new file mode 100644 index 0000000000..71c0662377 --- /dev/null +++ b/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.expected @@ -0,0 +1,2 @@ +| test.cpp:14:14:14:29 | ... == ... | A pointer to member virtual function $@ is tested for equality with non-null-pointer-constant $@. | test.cpp:6:16:6:17 | F3 | F3 | test.cpp:14:24:14:29 | F1 | F1 | +| test.cpp:15:14:15:29 | ... == ... | A pointer to member virtual function $@ is tested for equality with non-null-pointer-constant $@. | test.cpp:6:16:6:17 | F3 | F3 | test.cpp:15:24:15:29 | F2 | F2 | diff --git a/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.ql b/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.ql new file mode 100644 index 0000000000..84263abc91 --- /dev/null +++ b/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.potentiallyvirtualpointeronlycomparestonullptr.PotentiallyVirtualPointerOnlyComparesToNullptr + +class TestFileQuery extends PotentiallyVirtualPointerOnlyComparesToNullptrSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A5-10-1/test.cpp b/cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A5-10-1/test.cpp rename to cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/test.cpp diff --git a/cpp/common/test/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.ql b/cpp/common/test/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.ql index 24aa754e0a..1c9c73fb3d 100644 --- a/cpp/common/test/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.ql +++ b/cpp/common/test/rules/predicatefunctionobjectsshouldnotbemutable/PredicateFunctionObjectsShouldNotBeMutable.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.predicatefunctionobjectsshouldnotbemutable.PredicateFunctionObjectsShouldNotBeMutable + +class TestFileQuery extends PredicateFunctionObjectsShouldNotBeMutableSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql index 37ff4945f4..d66babdb6d 100644 --- a/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql +++ b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument + +class TestFileQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/test.cpp b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/test.cpp index a70d631041..f58cb4a442 100644 --- a/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/test.cpp +++ b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/test.cpp @@ -1,4 +1,4 @@ -#include +#include #define MACROFUNCTION(X) std::strlen(X) void f() { diff --git a/cpp/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql b/cpp/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql index 1b27d1e0ee..c7652ab4ae 100644 --- a/cpp/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql +++ b/cpp/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preprocessorincludesforbiddenheadernames.PreprocessorIncludesForbiddenHeaderNames + +class TestFileQuery extends PreprocessorIncludesForbiddenHeaderNamesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql b/cpp/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql index 343b168637..43701dbbf9 100644 --- a/cpp/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql +++ b/cpp/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preprocessorincludespreceded.PreprocessorIncludesPreceded + +class TestFileQuery extends PreprocessorIncludesPrecededSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql b/cpp/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql index cef4c700ab..009c7f9e26 100644 --- a/cpp/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql +++ b/cpp/common/test/rules/preservesafetywhenusingconditionvariables/PreserveSafetyWhenUsingConditionVariables.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preservesafetywhenusingconditionvariables.PreserveSafetyWhenUsingConditionVariables + +class TestFileQuery extends PreserveSafetyWhenUsingConditionVariablesSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql b/cpp/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql index 6412db389a..4ca46f15ea 100644 --- a/cpp/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql +++ b/cpp/common/test/rules/preventdeadlockbylockinginpredefinedorder/PreventDeadlockByLockingInPredefinedOrder.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.preventdeadlockbylockinginpredefinedorder.PreventDeadlockByLockingInPredefinedOrder + +class TestFileQuery extends PreventDeadlockByLockingInPredefinedOrderSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql b/cpp/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql index 71c2a6cecd..9150d4459d 100644 --- a/cpp/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql +++ b/cpp/common/test/rules/readofuninitializedmemory/ReadOfUninitializedMemory.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.readofuninitializedmemory.ReadOfUninitializedMemory + +class TestFileQuery extends ReadOfUninitializedMemorySharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/readofuninitializedmemory/test.cpp b/cpp/common/test/rules/readofuninitializedmemory/test.cpp index bdd3fdc203..6ed07d795f 100644 --- a/cpp/common/test/rules/readofuninitializedmemory/test.cpp +++ b/cpp/common/test/rules/readofuninitializedmemory/test.cpp @@ -121,4 +121,6 @@ void test_non_default_init() { use(slp); // COMPLIANT - static variables are zero initialized thread_local int *tlp; use(tlp); // COMPLIANT - thread local variables are zero initialized + _Atomic int ai; + use(ai); // COMPLIANT - atomics are special and not covered by this rule } \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.expected b/cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.expected similarity index 100% rename from cpp/autosar/test/rules/A5-2-4/ReinterpretCastUsed.expected rename to cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.expected diff --git a/cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.ql b/cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.ql new file mode 100644 index 0000000000..b58a7f4dbb --- /dev/null +++ b/cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.reinterpretcastused.ReinterpretCastUsed + +class TestFileQuery extends ReinterpretCastUsedSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A5-2-4/test.cpp b/cpp/common/test/rules/reinterpretcastused/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A5-2-4/test.cpp rename to cpp/common/test/rules/reinterpretcastused/test.cpp diff --git a/cpp/common/test/rules/removeconstorvolatilequalification/RemoveConstOrVolatileQualification.ql b/cpp/common/test/rules/removeconstorvolatilequalification/RemoveConstOrVolatileQualification.ql index 0534eba191..61865cccab 100644 --- a/cpp/common/test/rules/removeconstorvolatilequalification/RemoveConstOrVolatileQualification.ql +++ b/cpp/common/test/rules/removeconstorvolatilequalification/RemoveConstOrVolatileQualification.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.removeconstorvolatilequalification.RemoveConstOrVolatileQualification + +class TestFileQuery extends RemoveConstOrVolatileQualificationSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected b/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected new file mode 100644 index 0000000000..3f2720dd76 --- /dev/null +++ b/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.expected @@ -0,0 +1,3 @@ +| test.cpp:9:7:9:12 | ... = ... | Use of an assignment operator's result. | +| test.cpp:13:11:13:16 | ... = ... | Use of an assignment operator's result. | +| test.cpp:15:8:15:13 | ... = ... | Use of an assignment operator's result. | diff --git a/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql b/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql new file mode 100644 index 0000000000..286e4424a4 --- /dev/null +++ b/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.resultofanassignmentoperatorshouldnotbeused.ResultOfAnAssignmentOperatorShouldNotBeUsed + +class TestFileQuery extends ResultOfAnAssignmentOperatorShouldNotBeUsedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/test.cpp b/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/test.cpp new file mode 100644 index 0000000000..21fb4c0910 --- /dev/null +++ b/cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/test.cpp @@ -0,0 +1,16 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void test() { + int l1, l2; + int l3[1]; + + l1 = l2; // COMPLIANT + + if (l1 = 1) // NON_COMPLIANT + { + } + + l1 = l3[l2 = 0]; // NON_COMPLIANT + + l1 = l2 = 0; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/rethrownestedwithoutcapture/RethrowNestedWithoutCapture.ql b/cpp/common/test/rules/rethrownestedwithoutcapture/RethrowNestedWithoutCapture.ql index da62185e5d..ab45ada710 100644 --- a/cpp/common/test/rules/rethrownestedwithoutcapture/RethrowNestedWithoutCapture.ql +++ b/cpp/common/test/rules/rethrownestedwithoutcapture/RethrowNestedWithoutCapture.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.rethrownestedwithoutcapture.RethrowNestedWithoutCapture + +class TestFileQuery extends RethrowNestedWithoutCaptureSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.expected b/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.expected similarity index 100% rename from cpp/autosar/test/rules/M7-5-1/FunctionReturnAutomaticVarCondition.expected rename to cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.expected diff --git a/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.ql b/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.ql new file mode 100644 index 0000000000..c6c9c9e8fc --- /dev/null +++ b/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.returnreferenceorpointertoautomaticlocalvariable.ReturnReferenceOrPointerToAutomaticLocalVariable + +class TestFileQuery extends ReturnReferenceOrPointerToAutomaticLocalVariableSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/test.cpp b/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/test.cpp new file mode 100644 index 0000000000..d383d7859f --- /dev/null +++ b/cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/test.cpp @@ -0,0 +1,48 @@ +int *test_ptr_return_f2() { + int x = 100; + return &x; // NON_COMPLIANT +} + +int *test_ptr_return_f3(int y) { return &y; } // NON_COMPLIANT + +int &test_ref_return_f2() { + int x = 100; + return x; // NON_COMPLIANT +} + +int &test_ref_return_f3(int y) { return y; } // NON_COMPLIANT + +int *test_ptr_return_f4() { + static int x = 1; + return &x; // COMPLIANT +} + +int test_return_f2() { + int x = 100; + return x; // COMPLIANT +} +template void t1(T &x, T &y) { + if (x > y) + x = y; // NON_COMPLIANT[FALSE NEGATIVE] + else + x = -y; // NON_COMPLIANT[FALSE NEGATIVE] +} + +void test_templatefunction_return() { + int j = 2; + int k = 3; + t1(j, k); +} + +class C1 { +private: + int x; + +public: + int test() { return x; } // COMPLIANT - ignore member vars +}; + +int x; +int test_global() { + return x; // COMPLIANT - ignore global vars +} \ No newline at end of file diff --git a/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected b/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected index bd67e19880..1355eb96f0 100644 --- a/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected +++ b/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.expected @@ -1,20 +1,20 @@ -| test.cpp:37:1:37:39 | // int myFunction() { return myValue; } | This comment appears to contain commented-out code. | -| test.cpp:39:1:39:45 | // int myFunction() const { return myValue; } | This comment appears to contain commented-out code. | -| test.cpp:41:1:41:54 | // int myFunction() const noexcept { return myValue; } | This comment appears to contain commented-out code. | -| test.cpp:43:1:43:18 | // #define MYMACRO | This comment appears to contain commented-out code. | -| test.cpp:45:1:45:23 | // #include "include.h" | This comment appears to contain commented-out code. | -| test.cpp:47:1:51:2 | /*\n#ifdef\nvoid myFunction();\n#endif\n*/ | This comment appears to contain commented-out code. | -| test.cpp:59:1:59:24 | // #if(defined(MYMACRO)) | This comment appears to contain commented-out code. | -| test.cpp:63:1:63:15 | // #pragma once | This comment appears to contain commented-out code. | -| test.cpp:65:1:65:17 | // # pragma once | This comment appears to contain commented-out code. | -| test.cpp:67:1:67:19 | /*#error"myerror"*/ | This comment appears to contain commented-out code. | -| test.cpp:91:1:95:2 | /*\n#ifdef MYMACRO\n // ...\n#endif // #ifdef MYMACRO\n*/ | This comment appears to contain commented-out code. | -| test.cpp:106:21:106:43 | // #include "config2.h" | This comment appears to contain commented-out code. | -| test.cpp:112:16:112:35 | /* #ifdef MYMACRO */ | This comment appears to contain commented-out code. | -| test.cpp:116:1:116:24 | // commented_out_code(); | This comment appears to contain commented-out code. | -| test.cpp:119:2:119:25 | // commented_out_code(); | This comment appears to contain commented-out code. | -| test.cpp:122:1:122:22 | // commented out code; | This comment appears to contain commented-out code. | -| test.cpp:124:1:127:8 | // some; | This comment appears to contain commented-out code. | -| test.cpp:129:1:133:8 | // also; | This comment appears to contain commented-out code. | -| test.cpp:141:1:146:2 | /*\n some;\n commented;\n out;\n code;\n*/ | This comment appears to contain commented-out code. | -| test.cpp:148:1:154:2 | /*\n also;\n this\n is;\n commented-out\n code;\n*/ | This comment appears to contain commented-out code. | +| test.cpp:39:1:39:39 | // int myFunction() { return myValue; } | This comment appears to contain commented-out code. | +| test.cpp:41:1:41:45 | // int myFunction() const { return myValue; } | This comment appears to contain commented-out code. | +| test.cpp:43:1:43:54 | // int myFunction() const noexcept { return myValue; } | This comment appears to contain commented-out code. | +| test.cpp:45:1:45:18 | // #define MYMACRO | This comment appears to contain commented-out code. | +| test.cpp:47:1:47:23 | // #include "include.h" | This comment appears to contain commented-out code. | +| test.cpp:49:1:53:2 | /*\n#ifdef\nvoid myFunction();\n#endif\n*/ | This comment appears to contain commented-out code. | +| test.cpp:61:1:61:24 | // #if(defined(MYMACRO)) | This comment appears to contain commented-out code. | +| test.cpp:65:1:65:15 | // #pragma once | This comment appears to contain commented-out code. | +| test.cpp:67:1:67:17 | // # pragma once | This comment appears to contain commented-out code. | +| test.cpp:69:1:69:19 | /*#error"myerror"*/ | This comment appears to contain commented-out code. | +| test.cpp:93:1:97:2 | /*\n#ifdef MYMACRO\n // ...\n#endif // #ifdef MYMACRO\n*/ | This comment appears to contain commented-out code. | +| test.cpp:108:21:108:43 | // #include "config2.h" | This comment appears to contain commented-out code. | +| test.cpp:114:16:114:35 | /* #ifdef MYMACRO */ | This comment appears to contain commented-out code. | +| test.cpp:118:1:118:24 | // commented_out_code(); | This comment appears to contain commented-out code. | +| test.cpp:121:2:121:25 | // commented_out_code(); | This comment appears to contain commented-out code. | +| test.cpp:124:1:124:22 | // commented out code; | This comment appears to contain commented-out code. | +| test.cpp:126:1:129:8 | // some; | This comment appears to contain commented-out code. | +| test.cpp:131:1:135:8 | // also; | This comment appears to contain commented-out code. | +| test.cpp:143:1:148:2 | /*\n some;\n commented;\n out;\n code;\n*/ | This comment appears to contain commented-out code. | +| test.cpp:150:1:156:2 | /*\n also;\n this\n is;\n commented-out\n code;\n*/ | This comment appears to contain commented-out code. | diff --git a/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql b/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql index c60068c7cc..aacadf0253 100644 --- a/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql +++ b/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/SectionsOfCodeShallNotBeCommentedOut.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.sectionsofcodeshallnotbecommentedout.SectionsOfCodeShallNotBeCommentedOut + +class TestFileQuery extends SectionsOfCodeShallNotBeCommentedOutSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/test.cpp b/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/test.cpp index 97d40f2353..389bff5f74 100644 --- a/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/test.cpp +++ b/cpp/common/test/rules/sectionsofcodeshallnotbecommentedout/test.cpp @@ -1,3 +1,5 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. /* * This sentence contains a semicolon; * however, this doesn't make it code. diff --git a/cpp/common/test/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.ql b/cpp/common/test/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.ql index 7bb5f78c86..7fae4b8b9a 100644 --- a/cpp/common/test/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.ql +++ b/cpp/common/test/rules/stringnumberconversionmissingerrorcheck/StringNumberConversionMissingErrorCheck.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.stringnumberconversionmissingerrorcheck.StringNumberConversionMissingErrorCheck + +class TestFileQuery extends StringNumberConversionMissingErrorCheckSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected new file mode 100644 index 0000000000..9bad1dc42e --- /dev/null +++ b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected @@ -0,0 +1 @@ +| test.cpp:5:3:24:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.cpp:5:3:24:3 | switch (...) ... | Switch | diff --git a/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql new file mode 100644 index 0000000000..1b323a652d --- /dev/null +++ b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition + +class TestFileQuery extends SwitchCasePositionConditionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/switchcasepositioncondition/test.cpp b/cpp/common/test/rules/switchcasepositioncondition/test.cpp new file mode 100644 index 0000000000..bbdbddfb80 --- /dev/null +++ b/cpp/common/test/rules/switchcasepositioncondition/test.cpp @@ -0,0 +1,42 @@ +void f1(int p1); + +void f2(int p1) { + + switch (p1) { + start:; // NON_COMPLIANT + case 1: + if (p1) { + ; + }; + break; + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} +void f3(int p1) { + + switch (p1) { // COMPLIANT + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} diff --git a/cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.expected b/cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.expected similarity index 100% rename from cpp/autosar/test/rules/M6-3-1/SwitchCompoundCondition.expected rename to cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.expected diff --git a/cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.ql b/cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.ql new file mode 100644 index 0000000000..8fb855036c --- /dev/null +++ b/cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchcompoundcondition.SwitchCompoundCondition + +class TestFileQuery extends SwitchCompoundConditionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/switchcompoundcondition/test.cpp b/cpp/common/test/rules/switchcompoundcondition/test.cpp new file mode 100644 index 0000000000..487c007fdc --- /dev/null +++ b/cpp/common/test/rules/switchcompoundcondition/test.cpp @@ -0,0 +1,56 @@ +void test_loop_missing_braces(int expression) { + for (int i = 0; i < expression; i++) // BAD + expression = expression % 2; +} + +void test_loop_valid_braces_check(int expression) { + for (int i = 0; i < expression; i++) { // GOOD + expression = expression % 2; + } + + int j = 10; + while (expression < 10) // BAD + j = j + 10; +} + +void test_loop_mix_validity(int expression) { + do // BAD + expression = expression % 2; + while (expression < 10); + + while (expression > 10) // GOOD + { + expression = expression * 2; + } + + do { // GOOD + expression = expression % 2; + } while (expression < 5); +} + +void test_switch_valid_braces(int i, int expression) { + // GOOD + switch (expression) { + case 0: + while (i < 10) { + i = i + expression; + } + break; + case 1: + if (i > 10) { + i = i * i; + } + break; + default: + break; + } +} + +void test_switch_invalid_braces(int i, int expression) { + // BAD + switch (expression) + case 0: + while (i < 10) { + i = i + expression; + } +} diff --git a/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected new file mode 100644 index 0000000000..0353e68531 --- /dev/null +++ b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected @@ -0,0 +1,3 @@ +| test.cpp:4:3:10:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:4:3:10:3 | switch (...) ... | Switch | test.cpp:5:3:5:9 | case ...: | case | +| test.cpp:13:3:20:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:13:3:20:3 | switch (...) ... | Switch | test.cpp:14:3:14:10 | case ...: | case | +| test.cpp:25:3:30:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:25:3:30:3 | switch (...) ... | Switch | test.cpp:26:3:26:9 | case ...: | case | diff --git a/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql new file mode 100644 index 0000000000..75ce3cb1ec --- /dev/null +++ b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed + +class TestFileQuery extends SwitchNotWellFormedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/switchnotwellformed/test.cpp b/cpp/common/test/rules/switchnotwellformed/test.cpp new file mode 100644 index 0000000000..d1fe00d5af --- /dev/null +++ b/cpp/common/test/rules/switchnotwellformed/test.cpp @@ -0,0 +1,40 @@ + +void f1(); +void f2(int p1) { + switch (p1) { + case 1: + int y = p1; // NON_COMPLIANT - `DeclStmt` whose parent + // statement is the switch body + f1(); + break; + } +} +void f3(int p1) { + switch (p1) { + case 10: + f1(); + goto L1; // NON_COMPLIANT - `JumpStmt` whose parent statement is the// + // switch// body + case 2: + break; + } +L1:; +} + +void f4(int p1) { + switch (p1) { + case 1: + L1:; // NON_COMPLIANT - `LabelStmt` whose parent statement is the + // switch body + break; + } +} + +void f5(int p1) { + switch (p1) { + case 1: // COMPLIANT + default: + p1 = 0; + break; + } +} diff --git a/cpp/common/test/rules/throwingnothrowoperatornewdelete/ThrowingNoThrowOperatorNewDelete.ql b/cpp/common/test/rules/throwingnothrowoperatornewdelete/ThrowingNoThrowOperatorNewDelete.ql index 509ebce02c..0135c410f4 100644 --- a/cpp/common/test/rules/throwingnothrowoperatornewdelete/ThrowingNoThrowOperatorNewDelete.ql +++ b/cpp/common/test/rules/throwingnothrowoperatornewdelete/ThrowingNoThrowOperatorNewDelete.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.throwingnothrowoperatornewdelete.ThrowingNoThrowOperatorNewDelete + +class TestFileQuery extends ThrowingNoThrowOperatorNewDeleteSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.expected b/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.expected index ae8a0d626b..5e047a77da 100644 --- a/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.expected +++ b/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.expected @@ -3,8 +3,8 @@ problems | test.cpp:12:5:12:19 | return ... | test.cpp:12:12:12:18 | 0 | test.cpp:12:12:12:18 | 0 | operator new(size_t) may return null instead of throwing a std::bad_alloc exception. | | test.cpp:14:5:14:33 | return ... | test.cpp:4:10:4:23 | call to operator new | test.cpp:14:12:14:26 | call to can_return_null | operator new(size_t) may return null instead of throwing a std::bad_alloc exception. | edges -| test.cpp:4:10:4:23 | call to operator new | test.cpp:14:12:14:26 | call to can_return_null | -| test.cpp:8:23:8:23 | 0 | test.cpp:10:12:10:24 | localVariable | +| test.cpp:4:10:4:23 | call to operator new | test.cpp:14:12:14:26 | call to can_return_null | provenance | | +| test.cpp:8:23:8:23 | 0 | test.cpp:10:12:10:24 | localVariable | provenance | | nodes | test.cpp:4:10:4:23 | call to operator new | semmle.label | call to operator new | | test.cpp:8:23:8:23 | 0 | semmle.label | 0 | diff --git a/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.ql b/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.ql index a0d67c17c9..c0fc6c8619 100644 --- a/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.ql +++ b/cpp/common/test/rules/throwingoperatornewreturnsnull/ThrowingOperatorNewReturnsNull.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.throwingoperatornewreturnsnull.ThrowingOperatorNewReturnsNull + +class TestFileQuery extends ThrowingOperatorNewReturnsNullSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/throwingoperatornewthrowsinvalidexception/ThrowingOperatorNewThrowsInvalidException.ql b/cpp/common/test/rules/throwingoperatornewthrowsinvalidexception/ThrowingOperatorNewThrowsInvalidException.ql index 4e2cc46a8b..072a5c7027 100644 --- a/cpp/common/test/rules/throwingoperatornewthrowsinvalidexception/ThrowingOperatorNewThrowsInvalidException.ql +++ b/cpp/common/test/rules/throwingoperatornewthrowsinvalidexception/ThrowingOperatorNewThrowsInvalidException.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.throwingoperatornewthrowsinvalidexception.ThrowingOperatorNewThrowsInvalidException + +class TestFileQuery extends ThrowingOperatorNewThrowsInvalidExceptionSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.expected b/cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.expected new file mode 100644 index 0000000000..4bbdb307ff --- /dev/null +++ b/cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.expected @@ -0,0 +1,30 @@ +| test.cpp:5:3:5:6 | call to acos | Domain error in call to 'acos': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | +| test.cpp:9:3:9:6 | call to acos | Domain error in call to 'acos': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | +| test.cpp:10:3:10:6 | call to asin | Domain error in call to 'asin': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | +| test.cpp:14:3:14:6 | call to asin | Domain error in call to 'asin': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | +| test.cpp:15:3:15:7 | call to atanh | Domain error in call to 'atanh': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). | +| test.cpp:17:3:17:7 | call to atanh | Domain error in call to 'atanh': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). | +| test.cpp:18:3:18:7 | call to atan2 | Domain error in call to 'atan2': both arguments are equal to zero. | +| test.cpp:22:3:22:5 | call to pow | Domain error in call to 'pow': both arguments are equal to zero. | +| test.cpp:26:3:26:5 | call to pow | Domain error in call to 'pow': both arguments are less than zero. | +| test.cpp:31:3:31:7 | call to acosh | Domain error in call to 'acosh': argument is less than 1. | +| test.cpp:32:3:32:7 | call to ilogb | Domain error in call to 'ilogb': argument is equal to zero. | +| test.cpp:35:3:35:5 | call to log | Domain error in call to 'log': argument is negative. | +| test.cpp:37:3:37:7 | call to log10 | Domain error in call to 'log10': argument is negative. | +| test.cpp:39:3:39:6 | call to log2 | Domain error in call to 'log2': argument is negative. | +| test.cpp:41:3:41:6 | call to sqrt | Domain error in call to 'sqrt': argument is negative. | +| test.cpp:44:3:44:7 | call to log1p | Domain error in call to 'log1p': argument is less than 1. | +| test.cpp:46:3:46:6 | call to logb | Domain error in call to 'logb': argument is equal to zero. | +| test.cpp:49:3:49:8 | call to tgamma | Domain error in call to 'tgamma': argument is equal to zero. | +| test.cpp:55:3:55:5 | call to abs | Range error in call to 'abs': argument is most negative number. | +| test.cpp:56:3:56:6 | call to fmod | Domain error in call to 'fmod': y is 0. | +| test.cpp:58:3:58:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. | +| test.cpp:59:3:59:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. | +| test.cpp:63:3:63:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. | +| test.cpp:64:3:64:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. | +| test.cpp:65:3:65:5 | call to log | Pole error in call to 'log': argument is equal to zero. | +| test.cpp:66:3:66:7 | call to log10 | Pole error in call to 'log10': argument is equal to zero. | +| test.cpp:67:3:67:6 | call to log2 | Pole error in call to 'log2': argument is equal to zero. | +| test.cpp:68:3:68:7 | call to log1p | Pole error in call to 'log1p': argument is equal to negative one. | +| test.cpp:70:3:70:5 | call to pow | Pole error in call to 'pow': base is zero and exp is negative. | +| test.cpp:71:3:71:8 | call to lgamma | Pole error in call to 'lgamma': argument is equal to zero. | diff --git a/cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql b/cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql new file mode 100644 index 0000000000..11720fb8da --- /dev/null +++ b/cpp/common/test/rules/uncheckedrangedomainpoleerrors/UncheckedRangeDomainPoleErrors.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.uncheckedrangedomainpoleerrors.UncheckedRangeDomainPoleErrors + +class TestFileQuery extends UncheckedRangeDomainPoleErrorsSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/uncheckedrangedomainpoleerrors/test.cpp b/cpp/common/test/rules/uncheckedrangedomainpoleerrors/test.cpp new file mode 100644 index 0000000000..d194702e83 --- /dev/null +++ b/cpp/common/test/rules/uncheckedrangedomainpoleerrors/test.cpp @@ -0,0 +1,73 @@ +#include "limits.h" +#include "math.h" + +void test_domain_errors() { + acos(-1.1f); // NON_COMPLIANT + acos(-1.0f); // COMPLIANT + acos(0.0f); // COMPLIANT + acos(1.0f); // COMPLIANT + acos(1.1f); // NON_COMPLIANT + asin(-1.1f); // NON_COMPLIANT + asin(-1.0f); // COMPLIANT + asin(0.0f); // COMPLIANT + asin(1.0f); // COMPLIANT + asin(1.1f); // NON_COMPLIANT + atanh(-1.1f); // NON_COMPLIANT + atanh(0.0f); // COMPLIANT + atanh(1.1f); // NON_COMPLIANT + atan2(0.0f, 0.0f); // NON_COMPLIANT + atan2(1.0f, 0.0f); // COMPLIANT + atan2(0.0f, 1.0f); // COMPLIANT + atan2(1.0f, 1.0f); // COMPLIANT + pow(0.0f, 0.0f); // NON_COMPLIANT + pow(1.0f, 0.0f); // COMPLIANT + pow(0.0f, 1.0f); // COMPLIANT + pow(1.0f, 1.0f); // COMPLIANT + pow(-1.0f, -1.0f); // NON_COMPLIANT + pow(-1.0f, 0.0f); // COMPLIANT + pow(1.0f, -1.0f); // COMPLIANT + pow(-1.0f, 1.0f); // COMPLIANT + acosh(1.0f); // COMPLIANT + acosh(0.9f); // NON_COMPLIANT + ilogb(0.0f); // NON_COMPLIANT + ilogb(1.0f); // COMPLIANT + ilogb(-1.0f); // COMPLIANT + log(-1.0f); // NON_COMPLIANT + log(1.0f); // COMPLIANT + log10(-1.0f); // NON_COMPLIANT + log10(1.0f); // COMPLIANT + log2(-1.0f); // NON_COMPLIANT + log2(1.0f); // COMPLIANT + sqrt(-1.0f); // NON_COMPLIANT + sqrt(0.0f); // COMPLIANT + sqrt(1.0f); // COMPLIANT + log1p(-2.0f); // NON_COMPLIANT + log1p(0.0f); // COMPLIANT + logb(0.0f); // NON_COMPLIANT + logb(1.0f); // COMPLIANT + logb(-1.0f); // COMPLIANT + tgamma(0.0f); // NON_COMPLIANT + tgamma(1.0f); // COMPLIANT + tgamma(-1.1f); // COMPLIANT +} + +void fn_in_193_missing_domain_or_range_cases() { + abs(INT_MIN); // NON_COMPLIANT + fmod(1.0f, 0.0f); // NON_COMPLIANT + int *exp; + frexp(NAN, exp); // NON_COMPLIANT + frexp(INFINITY, exp); // NON_COMPLIANT +} + +void test_pole_errors() { + atanh(-1.0f); // NON_COMPLIANT + atanh(1.0f); // NON_COMPLIANT + log(0.0f); // NON_COMPLIANT + log10(0.0f); // NON_COMPLIANT + log2(0.0f); // NON_COMPLIANT + log1p(-1.0f); // NON_COMPLIANT + // logb(x) already covered in domain cases + pow(0.0f, -1.0f); // NON_COMPLIANT + lgamma(0.0f); // NON_COMPLIANT + lgamma(-1); // NON_COMPLIANT[FALSE_NEGATIVE] +} \ No newline at end of file diff --git a/cpp/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql b/cpp/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql index afede1b8ef..316565cab7 100644 --- a/cpp/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql +++ b/cpp/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.undefinedmacroidentifiers.UndefinedMacroIdentifiers + +class TestFileQuery extends UndefinedMacroIdentifiersSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.expected b/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.expected similarity index 100% rename from cpp/autosar/test/rules/M3-4-1/UnnecessaryExposedIdentifierDeclaration.expected rename to cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.expected diff --git a/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql b/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql new file mode 100644 index 0000000000..3baad901da --- /dev/null +++ b/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/UnnecessaryExposedIdentifierDeclarationShared.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unnecessaryexposedidentifierdeclarationshared.UnnecessaryExposedIdentifierDeclarationShared + +class TestFileQuery extends UnnecessaryExposedIdentifierDeclarationSharedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/test.cpp b/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/test.cpp new file mode 100644 index 0000000000..c4e01b8224 --- /dev/null +++ b/cpp/common/test/rules/unnecessaryexposedidentifierdeclarationshared/test.cpp @@ -0,0 +1,231 @@ +extern void f1(int i); +extern int g1; // COMPLIANT +extern int g2; // NON_COMPLIANT; single use of a global variable +bool f2() { return g1 == 1; } +void f3() { + int j = g1; // NON_COMPLIANT + if (f2()) { + int k; // COMPLIANT + f1(j); + f1(k); + } +} + +void f4() { + int j = g1; // COMPLIANT; value of g1 changed between + // definition and use + g1 = 1; + if (f2()) { + f1(j); + } +} + +void f5() { + int j = g1; // COMPLIANT; shouldn't be moved inside loop + while (true) { + int i = g1++; + while (f2()) { + i += j; + } + + if (i % 2) + break; + } +} + +void f6() { + int j = g1; // COMPLIANT; can't moved into smaller scope +#ifdef FOO + if (g1) { + g1 = j + 1; + } +#else + if (g1) { + g1 = j + 2; + } +#endif +} + +void f7() { + int j = g1; // COMPLIANT; potentially stores previous value of + // g1 so moving this would be incorrect. + f1(1); // f1 may change the value of g1 + if (f2()) { + f1(j); + } +} + +void f8() { int i = g2; } + +void f9() { + int i; // NON_COMPLIANT + + if (f2()) { + if (f2()) { + i++; + } else { + i--; + } + } +} + +struct S1 { // NON_COMPLIANT + int i; +}; + +void f10() { S1 l1{}; } + +void f11() { + struct S2 { // COMPLIANT + int i; + } l1{}; +} + +struct S3 { + int i; +}; + +template int f12(T p); +template <> int f12(S3 p) { return p.i; } + +struct S4 { // NON_COMPLIANT; single use in function f13 + int i; +}; + +template class C1 { // COMPLIANT; used in both f13 and f14 +private: + T m1; +}; + +void f13() { C1 l1; } +void f14() { C1 l1; } + +void f15() { + int i; // COMPLIANT + + if (i == 0) { + i++; + } +} + +namespace ns1 { +int i; // NON_COMPLIANT +namespace ns2 { +int j = i + 1; +void f1() { i++; } +} // namespace ns2 +} // namespace ns1 + +void f16() { + for (int i = 0; i < 10; i++) { + int j = i + 1; // NON_COMPLIANT[FALSE_NEGATIVE]; we are not consider + // candidates inside loops. + try { + j++; + } catch (...) { + } + } +} + +void f17() { + int i; // COMPLIANT + int *ptr; + { + // Moving the declaration of i into the reduced scope will result in a + // dangling pointer + ptr = &i; + } + *ptr = 1; +} + +namespace a_namespace { + +constexpr static unsigned int a_constexpr_var{ + 10U}; // COMPLIANT; used in + // a_namespace and + // another_namespace_function +static unsigned int + a_namespace_var[a_constexpr_var]{}; // COMPLIANT; used in + // a_namespace_function and + // another_namespace_function + +constexpr static unsigned int a_namespace_function(void) noexcept { + unsigned int a_return_value{0U}; + + for (auto loop_var : a_namespace_var) { // usage of a_namespace_var + a_return_value += loop_var; + } + return a_return_value; +} + +constexpr static unsigned int another_namespace_function(void) noexcept { + unsigned int a_return_value{0U}; + + for (unsigned int i{0U}; i < a_constexpr_var; + i++) { // usage of a_constexpr_var + a_return_value += a_namespace_var[i]; // usage of a_namespace_var + } + return a_return_value; +} +} // namespace a_namespace + +namespace parent_namespace { +namespace child_namespace { +template class a_class_in_child_namespace { +public: + template constexpr auto &&operator()(To &&val) const noexcept { + return static_cast(val); + } +}; // a_class_in_child_namespace end + +template +extern constexpr a_class_in_child_namespace + a_class_in_child_namespace_impl{}; + +} // namespace child_namespace + +template +static constexpr auto const &a_parent_namespace_variable = + child_namespace::a_class_in_child_namespace_impl< + From>; // COMPLIANT; used in child_namespace2::a_class::bar() and + // parent_namespace::another_class::foo() + +namespace child_namespace2 { +class a_class { +public: + int func(...) { return 0; } + void foo(int x) { x++; } + template constexpr auto bar(F(*func), int b) { + foo(func(a_parent_namespace_variable( + b))); // usage of a_parent_namespace_variable + } +}; // a_class +} // namespace child_namespace2 + +class another_class { + int a; + int b; + void bar(int param) { param++; } + + bool has_value() { return a == b; } + +public: + template int foo(F(*func), int b) { + if (has_value()) { + bar(func(a_parent_namespace_variable( + b))); // usage of a_parent_namespace_variable + } + return 0; + } +}; // another_class +} // namespace parent_namespace + +template T a_func(T v) { return v++; } + +int main() { + parent_namespace::child_namespace2::a_class a_class_obj; + a_class_obj.bar(a_func, 10); + parent_namespace::another_class another_class_obj; + another_class_obj.foo(a_func, 10); + return 0; +} diff --git a/cpp/common/test/rules/unreachablecode/UnreachableCode.expected b/cpp/common/test/rules/unreachablecode/UnreachableCode.expected new file mode 100644 index 0000000000..1c8f16611b --- /dev/null +++ b/cpp/common/test/rules/unreachablecode/UnreachableCode.expected @@ -0,0 +1,5 @@ +| test.cpp:17:3:17:12 | declaration | This statement in function $@ is unreachable. | test.cpp:15:5:15:21 | test_after_return | test_after_return | +| test.cpp:21:10:22:12 | { ... } | This statement in function $@ is unreachable. | test.cpp:20:5:20:27 | test_constant_condition | test_constant_condition | +| test.cpp:32:10:33:12 | { ... } | This statement in function $@ is unreachable. | test.cpp:31:24:31:24 | f | f | +| test.cpp:53:12:54:14 | { ... } | This statement in function $@ is unreachable. | test.cpp:52:7:52:8 | h1 | h1 | +| test.cpp:58:12:59:14 | { ... } | This statement in function $@ is unreachable. | test.cpp:57:7:57:8 | h2 | h2 | diff --git a/cpp/common/test/rules/unreachablecode/UnreachableCode.ql b/cpp/common/test/rules/unreachablecode/UnreachableCode.ql new file mode 100644 index 0000000000..c394bfba3e --- /dev/null +++ b/cpp/common/test/rules/unreachablecode/UnreachableCode.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unreachablecode.UnreachableCode + +class TestFileQuery extends UnreachableCodeSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/unreachablecode/test.cpp b/cpp/common/test/rules/unreachablecode/test.cpp new file mode 100644 index 0000000000..749b3a43da --- /dev/null +++ b/cpp/common/test/rules/unreachablecode/test.cpp @@ -0,0 +1,75 @@ +// NOTICE: SOME OF THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +void test_switch(int p1) { + int l1 = 0; + switch (p1) { + l1 = p1; // NON_COMPLIANT[FALSE_NEGATIVE] + case 1: + break; + default: + break; + } +} + +int test_after_return() { + return 0; + int l1 = 0; // NON_COMPLIANT - function has returned by this point +} + +int test_constant_condition() { + if (0) { // NON_COMPLIANT + return 1; + } else { // COMPLIANT + return 2; + } +} + +// NOTICE: THE TEST CASES ABOVE ARE ALSO INCLUDED IN THE C TEST CASE AND CHANGES +// SHOULD BE REFLECTED THERE AS WELL. + +template int f() { + if (0) { // NON_COMPLIANT - block is unreachable in all instances + return 3; + } + if (T::isVal()) { // COMPLIANT - block is reachable in at least one template + // instantiation, so we do not flag it as dead code + return 2; + } + + return 1; // COMPLIANT - block is reachable in the uninstantiated template +} + +class A { +public: + static bool isVal() { return true; } +}; + +void test_f() { f(); } + +template class B { +public: + int h1(T t) { + if (0) { // NON_COMPLIANT - block is unreachable in all instances + return 3; + } + } + int h2() { + if (0) { // NON_COMPLIANT - block is unreachable in all instances + return 3; + } + } +}; + +class C {}; + +void test_template() { + B b1; + A a; + b1.h1(a); + b1.h2(); + B b2; + C c; + b2.h1(c); + b2.h2(); +} \ No newline at end of file diff --git a/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.expected b/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.expected new file mode 100644 index 0000000000..3326ede548 --- /dev/null +++ b/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.expected @@ -0,0 +1,18 @@ +| test.cpp:111:3:111:12 | 2147483648 | Hex literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:116:3:116:20 | 9223372036854775808 | Hex literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:139:3:139:21 | 9223372036854775808 | Hex literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:162:3:162:21 | 9223372036854775808 | Hex literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:185:3:185:22 | 9223372036854775808 | Hex literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:208:3:208:22 | 9223372036854775808 | Hex literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:227:3:227:14 | 2147483648 | Octal literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:232:3:232:25 | 9223372036854775808 | Octal literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:249:3:249:26 | 9223372036854775808 | Octal literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:266:3:266:26 | 9223372036854775808 | Octal literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:283:3:283:27 | 9223372036854775808 | Octal literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:300:3:300:27 | 9223372036854775808 | Octal literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:315:3:315:36 | 2147483648 | Binary literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:322:3:322:68 | 9223372036854775808 | Binary literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:365:3:365:69 | 9223372036854775808 | Binary literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:412:3:412:69 | 9223372036854775808 | Binary literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:457:3:457:70 | 9223372036854775808 | Binary literal is an unsigned integer but does not include a 'U' suffix. | +| test.cpp:502:3:502:70 | 9223372036854775808 | Binary literal is an unsigned integer but does not include a 'U' suffix. | diff --git a/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql b/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql new file mode 100644 index 0000000000..30f07a3f22 --- /dev/null +++ b/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unsignedintegerliteralsnotappropriatelysuffixed.UnsignedIntegerLiteralsNotAppropriatelySuffixed + +class TestFileQuery extends UnsignedIntegerLiteralsNotAppropriatelySuffixedSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/test.cpp b/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/test.cpp new file mode 100644 index 0000000000..fcbd51b3de --- /dev/null +++ b/cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/test.cpp @@ -0,0 +1,546 @@ +// Assumed platform in qltest is linux_x86_64, so +// int, long, long long sizes are assumed to be 32, 64, 64 bits respectively + +// The type of an integer constant is determined by "6.4.4.1 Integer constants" +// in the C11 Standard. The principle is that any decimal integer constant will +// be signed, unless it has the `U` or `u` suffix. Any hexadecimal integer will +// depend on whether it is larger than the maximum value of the smallest signed +// integer value that can hold the value. So the signedness depends on the +// magnitude of the constant. + +void test_decimal_constants() { + 0; // COMPLIANT + 2147483648; // COMPLIANT - larger than int, but decimal constants never use + // unsigned without the suffix, so will be `long` + 4294967296; // COMPLIANT - larger than unsigned int, still `long` + 9223372036854775807; // COMPLIANT - max long int + // 9223372036854775808; Not a valid integer constant, out of signed range + 0U; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648U; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296U; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807U; // COMPLIANT - max long int + 9223372036854775808U; // COMPLIANT - explicitly unsigned, so can go large than + // max long int + 0u; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648u; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296u; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807u; // COMPLIANT - max long int + 9223372036854775808u; // COMPLIANT - explicitly unsigned, so can go large than + // max long int + + // l suffix + 0l; // COMPLIANT + 2147483648l; // COMPLIANT - within the range of long int + 4294967296l; // COMPLIANT - within the range of long int + 9223372036854775807l; // COMPLIANT - max long int + // 9223372036854775808l; Not a valid integer constant, out of signed range + 0lU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648lU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296lU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807lU; // COMPLIANT - max long int + 9223372036854775808lU; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + 0lu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648lu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296lu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807lu; // COMPLIANT - max long int + 9223372036854775808lu; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + + // L suffix + 0L; // COMPLIANT + 2147483648L; // COMPLIANT - within the range of long int + 4294967296L; // COMPLIANT - within the range of long int + 9223372036854775807L; // COMPLIANT - max long int + // 9223372036854775808L; Not a valid integer constant, out of signed range + 0LU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648LU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296LU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807LU; // COMPLIANT - max long int + 9223372036854775808LU; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + 0Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807Lu; // COMPLIANT - max long int + 9223372036854775808Lu; // COMPLIANT - explicitly unsigned, so can go large + // than max long int + + // ll suffix + 0ll; // COMPLIANT + 2147483648ll; // COMPLIANT - within the range of long long int + 4294967296ll; // COMPLIANT - within the range of long long int + 9223372036854775807ll; // COMPLIANT - max long long int + // 9223372036854775808ll; Not a valid integer constant, out of signed range + 0llU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648llU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296llU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807llU; // COMPLIANT - max long long int + 9223372036854775808llU; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int + 0llu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648llu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296llu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807llu; // COMPLIANT - max long long int + 9223372036854775808llu; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int + + // LL suffix + 0LL; // COMPLIANT + 2147483648LL; // COMPLIANT - within the range of long long int + 4294967296LL; // COMPLIANT - within the range of long long int + 9223372036854775807LL; // COMPLIANT - max long long int + // 9223372036854775808LL; Not a valid integer constant, out of signed range + 0LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807LLU; // COMPLIANT - max long long int + 9223372036854775808LLU; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int + 0LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 2147483648LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 4294967296LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 9223372036854775807LLu; // COMPLIANT - max long long int + 9223372036854775808LLu; // COMPLIANT - explicitly unsigned, so can go large + // than max long long int +} + +void test_hexadecimal_constants() { + 0x0; // COMPLIANT - uses signed int + 0x7FFFFFFF; // COMPLIANT - max value held by signed int + 0x80000000; // NON_COMPLIANT - larger than max signed int, so will be unsigned + // int + 0x100000000; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0x7FFFFFFFFFFFFFFF; // COMPLIANT - max long int + 0x8000000000000000; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 0x0U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0u; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000u; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000u; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000u; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `l` suffix + 0x0l; // COMPLIANT - uses signed int + 0x7FFFFFFFl; // COMPLIANT - max value held by signed int + 0x80000000l; // COMPLIANT - larger than max signed int, but smaller than long + // int + 0x100000000l; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0x7FFFFFFFFFFFFFFFl; // COMPLIANT - max long int + 0x8000000000000000l; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 0x0lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFlU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFlU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000lU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFlu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFlu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000lu; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `L` suffix + 0x0L; // COMPLIANT - uses signed int + 0x7FFFFFFFL; // COMPLIANT - max value held by signed int + 0x80000000L; // COMPLIANT - larger than max signed int, but smaller than long + // int + 0x100000000L; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0x7FFFFFFFFFFFFFFFL; // COMPLIANT - max long int + 0x8000000000000000L; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 0x0LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000LU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000Lu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000Lu; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `ll` suffix + 0x0ll; // COMPLIANT - uses signed int + 0x7FFFFFFFll; // COMPLIANT - max value held by signed int + 0x80000000ll; // COMPLIANT - larger than max signed int, but smaller than long + // long int + 0x100000000ll; // COMPLIANT - larger than unsigned int, but smaller than long + // long int + 0x7FFFFFFFFFFFFFFFll; // COMPLIANT - max long long int + 0x8000000000000000ll; // NON_COMPLIANT - larger than long long int, so will be + // unsigned long long int + 0x0llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFllU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFllU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000llU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0llu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFllu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000llu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000llu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFllu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000llu; // COMPLIANT - unsigned, but uses the suffix correctly + + // Use of the `LL` suffix + 0x0LL; // COMPLIANT - uses signed int + 0x7FFFFFFFLL; // COMPLIANT - max value held by signed int + 0x80000000LL; // COMPLIANT - larger than max signed int, but smaller than long + // long int + 0x100000000LL; // COMPLIANT - larger than unsigned int, but smaller than long + // long int + 0x7FFFFFFFFFFFFFFFLL; // COMPLIANT - max long long int + 0x8000000000000000LL; // NON_COMPLIANT - larger than long long int, so will be + // unsigned long long int + 0x0LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000LLU; // COMPLIANT - unsigned, but uses the suffix correctly + 0x0LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFLLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x80000000LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x100000000LLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x7FFFFFFFFFFFFFFFLLu; // COMPLIANT - unsigned, but uses the suffix correctly + 0x8000000000000000LLu; // COMPLIANT - unsigned, but uses the suffix correctly +} + +void test_octal_constants() { + 00; // COMPLIANT - uses signed int + 017777777777; // COMPLIANT - max value held by signed int + 020000000000; // NON_COMPLIANT - larger than max signed int, so will be + // unsigned int + 040000000000; // COMPLIANT - larger than unsigned int, but smaller than long + // int + 0777777777777777777777; // COMPLIANT - max long int + 01000000000000000000000; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00U; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777U; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000U; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777U; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000U; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `l` suffix + 00l; // COMPLIANT - uses signed long + 017777777777l; // COMPLIANT - uses signed long + 020000000000l; // COMPLIANT - uses signed long + 040000000000l; // COMPLIANT - uses signed long + 0777777777777777777777l; // COMPLIANT - max long int + 01000000000000000000000l; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777Ul; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000Ul; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `L` suffix + 00L; // COMPLIANT - uses signed long + 017777777777L; // COMPLIANT - uses signed long + 020000000000L; // COMPLIANT - uses signed long + 040000000000L; // COMPLIANT - uses signed long + 0777777777777777777777L; // COMPLIANT - COMPLIANT - uses signed long + 01000000000000000000000L; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00UL; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777UL; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000UL; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000UL; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777UL; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000UL; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `ll` suffix + 00ll; // COMPLIANT - uses signed long long + 017777777777ll; // COMPLIANT - uses signed long long + 020000000000ll; // COMPLIANT - uses signed long long + 040000000000ll; // COMPLIANT - uses signed long long + 0777777777777777777777ll; // COMPLIANT - max long int + 01000000000000000000000ll; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777Ull; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000Ull; // COMPLIANT - unsigned, but uses the suffix + // correctly + + // Use of the `LL` suffix + 00LL; // COMPLIANT - uses signed long long + 017777777777LL; // COMPLIANT - uses signed long long + 020000000000LL; // COMPLIANT - uses signed long long + 040000000000LL; // COMPLIANT - uses signed long long + 0777777777777777777777LL; // COMPLIANT - max long int + 01000000000000000000000LL; // NON_COMPLIANT - larger than long int, so will be + // unsigned long int + 00ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 017777777777ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 020000000000ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 040000000000ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 0777777777777777777777ULL; // COMPLIANT - unsigned, but uses the suffix + // correctly + 01000000000000000000000ULL; // COMPLIANT - unsigned, but uses the suffix + // correctly +} + +void test_binary_constants() { + 0b0; // COMPLIANT - uses signed int + 0b1111111111111111111111111111111; // COMPLIANT - max value held by signed int + 0b10000000000000000000000000000000; // NON_COMPLIANT - larger than max signed + // int, so will be unsigned int + 0b100000000000000000000000000000000; // COMPLIANT - larger than unsigned int, + // but smaller than long int + 0b111111111111111111111111111111111111111111111111111111111111111; // COMPLIANT + // - max + // long int + 0b1000000000000000000000000000000000000000000000000000000000000000; // NON_COMPLIANT + // - + // larger + // than + // long + // int, so + // will be + // unsigned + // long + // int + 0b0U; // COMPLIANT - unsigned, but uses the suffix correctly + 0b1111111111111111111111111111111U; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b10000000000000000000000000000000U; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b100000000000000000000000000000000U; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b111111111111111111111111111111111111111111111111111111111111111U; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + 0b1000000000000000000000000000000000000000000000000000000000000000U; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + + // Use of the `l` suffix + 0b0l; // COMPLIANT - uses signed long + 0b1111111111111111111111111111111l; // COMPLIANT - uses signed long + 0b10000000000000000000000000000000l; // COMPLIANT - uses signed long + 0b100000000000000000000000000000000l; // COMPLIANT - uses signed long + 0b111111111111111111111111111111111111111111111111111111111111111l; // COMPLIANT + // - max + // long + // int + 0b1000000000000000000000000000000000000000000000000000000000000000l; // NON_COMPLIANT + // - + // larger + // than + // long + // int, + // so + // will + // be + // unsigned + // long + // int + 0b0Ul; // COMPLIANT - unsigned, but uses the suffix correctly + 0b1111111111111111111111111111111Ul; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b10000000000000000000000000000000Ul; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b100000000000000000000000000000000Ul; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b111111111111111111111111111111111111111111111111111111111111111Ul; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + 0b1000000000000000000000000000000000000000000000000000000000000000Ul; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + + // Use of the `L` suffix + 0b0L; // COMPLIANT - uses signed long + 0b1111111111111111111111111111111L; // COMPLIANT - uses signed long + 0b10000000000000000000000000000000L; // COMPLIANT - uses signed long + 0b100000000000000000000000000000000L; // COMPLIANT - uses signed long + 0b111111111111111111111111111111111111111111111111111111111111111L; // COMPLIANT + // - + // COMPLIANT + // - uses + // signed + // long + 0b1000000000000000000000000000000000000000000000000000000000000000L; // NON_COMPLIANT + // - + // larger + // than + // long + // int, + // so + // will + // be + // unsigned + // long + // int + 0b0UL; // COMPLIANT - unsigned, but uses the suffix correctly + 0b1111111111111111111111111111111UL; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b10000000000000000000000000000000UL; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b100000000000000000000000000000000UL; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b111111111111111111111111111111111111111111111111111111111111111UL; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + 0b1000000000000000000000000000000000000000000000000000000000000000UL; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + + // Use of the `ll` suffix + 0b0ll; // COMPLIANT - uses signed long long + 0b1111111111111111111111111111111ll; // COMPLIANT - uses signed long long + 0b10000000000000000000000000000000ll; // COMPLIANT - uses signed long long + 0b100000000000000000000000000000000ll; // COMPLIANT - uses signed long long + 0b111111111111111111111111111111111111111111111111111111111111111ll; // COMPLIANT + // - max + // long + // int + 0b1000000000000000000000000000000000000000000000000000000000000000ll; // NON_COMPLIANT + // - + // larger + // than + // long + // int, + // so + // will + // be + // unsigned + // long + // int + 0b0Ull; // COMPLIANT - unsigned, but uses the suffix correctly + 0b1111111111111111111111111111111Ull; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b10000000000000000000000000000000Ull; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b100000000000000000000000000000000Ull; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b111111111111111111111111111111111111111111111111111111111111111Ull; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + 0b1000000000000000000000000000000000000000000000000000000000000000Ull; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + + // Use of the `LL` suffix + 00LL; // COMPLIANT - uses signed long long + 0b1111111111111111111111111111111LL; // COMPLIANT - uses signed long long + 0b10000000000000000000000000000000LL; // COMPLIANT - uses signed long long + 0b100000000000000000000000000000000LL; // COMPLIANT - uses signed long long + 0b111111111111111111111111111111111111111111111111111111111111111LL; // COMPLIANT + // - max + // long + // int + 0b1000000000000000000000000000000000000000000000000000000000000000LL; // NON_COMPLIANT + // - + // larger + // than + // long + // int, + // so + // will + // be + // unsigned + // long + // int + 00ULL; // COMPLIANT - unsigned, but uses the suffix correctly + 0b1111111111111111111111111111111ULL; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b10000000000000000000000000000000ULL; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b100000000000000000000000000000000ULL; // COMPLIANT - unsigned, but uses the + // suffix correctly + 0b111111111111111111111111111111111111111111111111111111111111111ULL; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly + 0b1000000000000000000000000000000000000000000000000000000000000000ULL; // COMPLIANT + // - + // unsigned, + // but + // uses + // the + // suffix + // correctly +} + +#define COMPLIANT_VAL 0x80000000U +#define NON_COMPLIANT_VAL 0x80000000 + +void test_macro() { + COMPLIANT_VAL; // COMPLIANT + NON_COMPLIANT_VAL; // NON_COMPLIANT[FALSE_NEGATIVE] - cannot determine suffix + // in macro expansions +} \ No newline at end of file diff --git a/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.expected b/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.expected new file mode 100644 index 0000000000..3902cae09d --- /dev/null +++ b/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.expected @@ -0,0 +1,4 @@ +| test.cpp:7:3:7:9 | ... + ... | Operation + of type unsigned int may wrap. | +| test.cpp:8:3:8:10 | ... += ... | Operation += of type unsigned int may wrap. | +| test.cpp:61:3:61:9 | ... - ... | Operation - of type unsigned int may wrap. | +| test.cpp:62:3:62:10 | ... -= ... | Operation -= of type unsigned int may wrap. | diff --git a/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql b/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql new file mode 100644 index 0000000000..b88e7637c1 --- /dev/null +++ b/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unsignedoperationwithconstantoperandswraps.UnsignedOperationWithConstantOperandsWraps + +class TestFileQuery extends UnsignedOperationWithConstantOperandsWrapsSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/test.cpp b/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/test.cpp new file mode 100644 index 0000000000..8f76fbeeeb --- /dev/null +++ b/cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/test.cpp @@ -0,0 +1,83 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +#include + +void test_add_simple(unsigned int i1, unsigned int i2) { + i1 + i2; // NON_COMPLIANT - not bounds checked + i1 += i2; // NON_COMPLIANT - not bounds checked +} + +void test_add_precheck(unsigned int i1, unsigned int i2) { + if (UINT_MAX - i1 < i2) { + // handle error + } else { + i1 + i2; // COMPLIANT - bounds checked + i1 += i2; // COMPLIANT - bounds checked + } +} + +void test_add_precheck_2(unsigned int i1, unsigned int i2) { + if (i1 + i2 < i1) { + // handle error + } else { + i1 + i2; // COMPLIANT - bounds checked + i1 += i2; // COMPLIANT - bounds checked + } +} + +void test_add_postcheck(unsigned int i1, unsigned int i2) { + unsigned int i3 = i1 + i2; // COMPLIANT - checked for overflow afterwards + if (i3 < i1) { + // handle error + } + i1 += i2; // COMPLIANT - checked for overflow afterwards + if (i1 < i2) { + // handle error + } +} + +void test_ex2(unsigned int i1, unsigned int i2) { + unsigned int ci1 = 2; + unsigned int ci2 = 3; + ci1 + ci2; // COMPLIANT, compile time constants + i1 + 0; // COMPLIANT + i1 += 0; // COMPLIANT + i1 - 0; // COMPLIANT + i1 -= 0; // COMPLIANT + UINT_MAX - i1; // COMPLIANT - cannot be smaller than 0 + i1 * 1; // COMPLIANT + i1 *= 1; // COMPLIANT + if (0 <= i1 && i1 < 32) { + UINT_MAX >> i1; // COMPLIANT + } +} + +void test_ex3(unsigned int i1, unsigned int i2) { + i1 << i2; // COMPLIANT - by EX3 +} + +void test_sub_simple(unsigned int i1, unsigned int i2) { + i1 - i2; // NON_COMPLIANT - not bounds checked + i1 -= i2; // NON_COMPLIANT - not bounds checked +} + +void test_sub_precheck(unsigned int i1, unsigned int i2) { + if (i1 < i2) { + // handle error + } else { + i1 - i2; // COMPLIANT - bounds checked + i1 -= i2; // COMPLIANT - bounds checked + } +} + +void test_sub_postcheck(unsigned int i1, unsigned int i2) { + unsigned int i3 = i1 - i2; // COMPLIANT - checked for wrap afterwards + if (i3 > i1) { + // handle error + } + i1 -= i2; // COMPLIANT - checked for wrap afterwards + if (i1 > i2) { + // handle error + } +} \ No newline at end of file diff --git a/cpp/common/test/rules/unusedparameter/UnusedParameter.expected b/cpp/common/test/rules/unusedparameter/UnusedParameter.expected new file mode 100644 index 0000000000..eaeeeae4ca --- /dev/null +++ b/cpp/common/test/rules/unusedparameter/UnusedParameter.expected @@ -0,0 +1,3 @@ +| test.cpp:8:22:8:22 | x | Unused parameter 'x' for function $@. | test.cpp:8:6:8:16 | test_unused | test_unused | +| test.cpp:16:14:16:14 | x | Unused parameter 'x' for function $@. | test.cpp:16:8:16:8 | b | A::b | +| test.cpp:35:14:35:14 | y | Unused parameter 'y' for function $@. | test.cpp:34:9:34:9 | operator() | lambda expression | \ No newline at end of file diff --git a/cpp/common/test/rules/unusedparameter/UnusedParameter.ql b/cpp/common/test/rules/unusedparameter/UnusedParameter.ql new file mode 100644 index 0000000000..e990a7dcf3 --- /dev/null +++ b/cpp/common/test/rules/unusedparameter/UnusedParameter.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unusedparameter.UnusedParameter + +class TestFileQuery extends UnusedParameterSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/unusedparameter/test.cpp b/cpp/common/test/rules/unusedparameter/test.cpp new file mode 100644 index 0000000000..a1c198f3a5 --- /dev/null +++ b/cpp/common/test/rules/unusedparameter/test.cpp @@ -0,0 +1,46 @@ +// NOTICE: SOME OF THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +#include + +int test_used(int x) { return x; } // COMPLIANT + +void test_unused(int x) {} // NON_COMPLIANT + +void test_unnamed( + int) { // COMPLIANT - unnamed parameters are allowed to be unused +} + +class A { + int a(int x) { return x; } // COMPLIANT + void b(int x) {} // NON_COMPLIANT + void c(int) {} // COMPLIANT + virtual void d(int x, int y) {} // virtual, not covered by this rule +}; + +void f( + int i, // COMPLIANT + int j, // COMPLIANT + int k, // COMPLIANT + [[maybe_unused]] int l // COMPLIANT: explicitly stated as [[maybe_unused]] +) { + static_cast(i); // COMPLIANT: explicitly ignored by static_cast to void + (void)j; // COMPLIANT: explicitly ignored by c-style cast to void + std::ignore = k; // COMPLIANT: explicitly ignored by assignment to std::ignore +} + +void test_lambda_expr() { + auto lambda = + [](int x, // COMPLIANT: used + int y, // NON_COMPLIANT: unused without explicit notice + [[maybe_unused]] int z, // COMPLIANT: stdattribute [[maybe_unused]] + int w, // COMPLIANT: static_cast to void + int u, // COMPLIANT: c-style cast to void + int) { // COMPLIANT: unnamed parameter + static_cast(w); + (void)u; + return x; + }; +} + +void test_no_def(int x); // COMPLIANT - no definition, so cannot be "unused" diff --git a/cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected b/cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected new file mode 100644 index 0000000000..6ae78462b7 --- /dev/null +++ b/cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.expected @@ -0,0 +1,6 @@ +| test.cpp:4:7:4:7 | A | Type declaration A is not used. | +| test.cpp:6:7:6:7 | B | Type declaration B is not used. | +| test.cpp:13:11:13:11 | D | Type declaration D is not used. | +| test.cpp:77:11:77:11 | R | Type declaration R is not used. | +| test.cpp:90:12:90:12 | (unnamed class/struct/union) | Type declaration (unnamed class/struct/union) is not used. | +| test.cpp:111:29:111:30 | AA | Type declaration AA is not used. | diff --git a/cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql b/cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql new file mode 100644 index 0000000000..f1c09524d5 --- /dev/null +++ b/cpp/common/test/rules/unusedtypedeclarations/UnusedTypeDeclarations.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.unusedtypedeclarations.UnusedTypeDeclarations + +class TestFileQuery extends UnusedTypeDeclarationsSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/unusedtypedeclarations/test.cpp b/cpp/common/test/rules/unusedtypedeclarations/test.cpp new file mode 100644 index 0000000000..6eb673c1c7 --- /dev/null +++ b/cpp/common/test/rules/unusedtypedeclarations/test.cpp @@ -0,0 +1,124 @@ +// NOTICE: SOME OF THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. + +class A {}; // NON_COMPLIANT - unused + +class B { // NON_COMPLIANT - only used within itself +public: + B f(); + void g(B b); +}; + +class C {}; // COMPLIANT - used in the type def +typedef C D; // NON_COMPLIANT - typedef itself not used + +template // `T` is a `TemplateParameter` and shouldn't be flagged by + // the query +class E { // COMPLIANT - used in test_template_class below +private: + T m_t; + +public: + E(T t) : m_t(t) {} +}; + +void test_template_class() { E e(0); } + +class F {}; // COMPLIANT - used as a global function return type + +F test_return_value() { + F f; + return f; +} + +class G {}; // COMPLIANT - used as a global function parameter type + +void test_return_value(G g) {} + +class H {}; // COMPLIANT, used with template function + +template T get() { + T t; + return t; +} + +void test_template_function() { get(); } + +template class I : T::Base {}; + +class J { +public: + class Base {}; // COMPLIANT - I has J::Base as a base class +}; + +void test_template_base() { I i{}; } + +template class K { // COMPLIANT - used in function below, statically +public: + static T f(); +}; + +class L {}; // COMPLIANT - used in function below as type argument + +void test_static_call() { L l = K::f(); } + +enum M { C1, C2, C3 }; // COMPLIANT - used in an enum type access below +enum class N { C4, C5, C6 }; // COMPLIANT - used in an enum type access below + +void test_enum_access() { + int i = C1; + N::C4; +} + +class O {}; // COMPLIANT - used in typedef below + +typedef O P; // COMPLIANT - used in typedef below +typedef P Q; // COMPLIANT - used in function below +typedef Q R; // NON_COMPLIANT - never used + +Q test_type_def() {} + +struct { // COMPLIANT - used in type definition + union { // COMPLIANT - f1 and f3 is accessed + struct { // COMPLIANT - f1 is accessed + int f1; + }; + struct { // COMPLIANT - f3 is accessed + float f2; + float f3; + }; + struct { // NON_COMPLIANT - f4 is never accessed + long f4; + }; + }; + int f5; +} s; + +void test_nested_struct() { + s.f1; + s.f3; + s.f5; +} + +template class X { // COMPLIANT - template class never instantiated + using custom_type = E; // COMPLIANT - template class never instantiated +}; + +template class Y {}; // COMPLIANT - used in the test case below + +// Alias templates +template using Z = Y; // COMPLIANT - used below +template using AA = Y; // NON_COMPLIANT - never instantiated + +void test_alias_template() { Z v; } + +void test_temporary_object_creation() { + auto l1 = [](const auto &p1) noexcept { + class C1 { // COMPLIANT - used in temporary object construction + public: + constexpr static const char *m1() noexcept { return "foo"; } + }; + + return C1{p1}; + }; +} \ No newline at end of file diff --git a/cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.expected b/cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.expected new file mode 100644 index 0000000000..bc7343e73a --- /dev/null +++ b/cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.expected @@ -0,0 +1 @@ +| test.cpp:10:42:10:58 | asm statement | Use of assembler is not documented. | diff --git a/cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql b/cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql new file mode 100644 index 0000000000..f9997627b4 --- /dev/null +++ b/cpp/common/test/rules/usageofassemblernotdocumented/UsageOfAssemblerNotDocumented.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.usageofassemblernotdocumented.UsageOfAssemblerNotDocumented + +class TestFileQuery extends UsageOfAssemblerNotDocumentedSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/usageofassemblernotdocumented/test.cpp b/cpp/common/test/rules/usageofassemblernotdocumented/test.cpp new file mode 100644 index 0000000000..08096d26dc --- /dev/null +++ b/cpp/common/test/rules/usageofassemblernotdocumented/test.cpp @@ -0,0 +1,14 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +// COMPLIANT +void test_assembly_is_documented() { + // This comment serves as documentation + __asm__("ret\n"); +} + +// NON_COMPLIANT +void test_assembly_is_not_documented() { __asm__("ret\n"); } + +// COMPLIANT +#define RETURN __asm__("ret\n") +void test_undocumented_assembly_from_macro() { RETURN; } \ No newline at end of file diff --git a/cpp/common/test/rules/usecanonicalorderformemberinit/UseCanonicalOrderForMemberInit.ql b/cpp/common/test/rules/usecanonicalorderformemberinit/UseCanonicalOrderForMemberInit.ql index 17b299aadb..c703151f75 100644 --- a/cpp/common/test/rules/usecanonicalorderformemberinit/UseCanonicalOrderForMemberInit.ql +++ b/cpp/common/test/rules/usecanonicalorderformemberinit/UseCanonicalOrderForMemberInit.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.usecanonicalorderformemberinit.UseCanonicalOrderForMemberInit + +class TestFileQuery extends UseCanonicalOrderForMemberInitSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.expected b/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.expected new file mode 100644 index 0000000000..b4394172d4 --- /dev/null +++ b/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.expected @@ -0,0 +1,5 @@ +| test.cpp:41:16:41:19 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 0 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | +| test.cpp:41:22:41:25 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 1 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | +| test.cpp:41:28:41:31 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 2 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | +| test.cpp:41:34:41:37 | {...} | Missing braces on aggregate literal of type int[2]$@ which is assigned to index 3 in $@. | file://:0:0:0:0 | int[2] | int[2] | test.cpp:41:16:41:38 | {...} | array of type int[4][2] | +| test.cpp:48:15:48:18 | {...} | Missing braces on aggregate literal of type $@ which is assigned to field $@. | test.cpp:6:10:6:10 | (unnamed class/struct/union) | (unnamed class/struct/union) | test.cpp:9:5:9:8 | m_s1 | m_s1 | diff --git a/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql b/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql new file mode 100644 index 0000000000..b2bbb0ff1c --- /dev/null +++ b/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/UseInitializerBracesToMatchAggregateTypeStructure.ql @@ -0,0 +1,5 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.useinitializerbracestomatchaggregatetypestructure.UseInitializerBracesToMatchAggregateTypeStructure + +class TestFileQuery extends UseInitializerBracesToMatchAggregateTypeStructureSharedQuery, TestQuery { +} diff --git a/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/test.cpp b/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/test.cpp new file mode 100644 index 0000000000..b3a75e92ee --- /dev/null +++ b/cpp/common/test/rules/useinitializerbracestomatchaggregatetypestructure/test.cpp @@ -0,0 +1,58 @@ +#include + +struct Foo { + int m_i1; + int m_i2; + struct { + int m_s1_i1; + int m_s1_i2; + } m_s1; +}; + +struct Bar { + struct { + int m_s1_i1; + int m_s1_i2; + } m_s1; + int m_i1; + int m_i2; +}; + +struct Baz { + int m_baz_i1; + int m_baz_i2; + std::mutex m_mutex; + Foo f; +}; + +class StructNested { +public: + int m_nested_i1; + int *m_nested_i2; + Baz m_baz; + int m_array[10]; + StructNested() = default; + ~StructNested(); +}; + +void test() { + int l1[4]{1, 2, 3, 4}; // COMPLIANT + int l2[4][2]{{1, 2}, {3, 4}, {3, 4}, {3, 4}}; // COMPLIANT + int l3[4][2]{1, 2, 3, 4, 3, 4, 3, 4}; // NON_COMPLIANT - implied braces + int l4[4][2]{0}; // COMPLIANT + int l5[4][2]{{}, {}, {}, {}}; // NON_COMPLIANT - nested zero initializer + int l6[4][2]{{0}, {0}, {0}, {0}}; // NON_COMPLIANT - nested zero initializer + int l7[4][2]{}; // COMPLIANT + int l8[4]{1, 2}; // NON_COMPLIANT - missing explicit init + int l9[4][2]{{1, 2}}; // NON_COMPLIANT - missing explicit init + Foo f{1, 2, 3, 4}; // NON_COMPLIANT - implied braces + Foo f1{1, 2, {3, 4}}; // COMPLIANT + Foo f3{}; // COMPLIANT + Foo f4{0, 2}; // NON_COMPLIANT - missing explicit init + Foo f5{0, 2, {}}; // NON_COMPLIANT - nested zero initializer + Bar b{}; // COMPLIANT + Bar b1{0}; // COMPLIANT + Bar b2{{0}}; // NON_COMPLIANT - missing explicit init, nested zero init + StructNested n{}; // COMPLIANT + StructNested n1 = {}; // COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.expected b/cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.expected new file mode 100644 index 0000000000..e4280f2f1a --- /dev/null +++ b/cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.expected @@ -0,0 +1,2 @@ +| test.cpp:5:3:5:5 | 10 | Non zero octal literal 012. | +| test.cpp:6:3:6:5 | 44 | Non zero octal literal 054. | diff --git a/cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql b/cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql new file mode 100644 index 0000000000..0404a7bc0c --- /dev/null +++ b/cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.useofnonzerooctalliteral.UseOfNonZeroOctalLiteral + +class TestFileQuery extends UseOfNonZeroOctalLiteralSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/useofnonzerooctalliteral/test.cpp b/cpp/common/test/rules/useofnonzerooctalliteral/test.cpp new file mode 100644 index 0000000000..0bf928c9ec --- /dev/null +++ b/cpp/common/test/rules/useofnonzerooctalliteral/test.cpp @@ -0,0 +1,7 @@ +// NOTICE: THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND +// CHANGES SHOULD BE REFLECTED THERE AS WELL. +void test_non_zero_octal() { + 0; // COMPLIANT - octal literal zero permitted + 012; // NON_COMPLIANT + 054; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql b/cpp/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql index 819d12c4e8..55554bee07 100644 --- a/cpp/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql +++ b/cpp/common/test/rules/useonlyarrayindexingforpointerarithmetic/UseOnlyArrayIndexingForPointerArithmetic.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.useonlyarrayindexingforpointerarithmetic.UseOnlyArrayIndexingForPointerArithmetic + +class TestFileQuery extends UseOnlyArrayIndexingForPointerArithmeticSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/validcontainerelementaccess/ValidContainerElementAccess.ql b/cpp/common/test/rules/validcontainerelementaccess/ValidContainerElementAccess.ql index 43e757d5be..5f61b8a2a9 100644 --- a/cpp/common/test/rules/validcontainerelementaccess/ValidContainerElementAccess.ql +++ b/cpp/common/test/rules/validcontainerelementaccess/ValidContainerElementAccess.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.validcontainerelementaccess.ValidContainerElementAccess + +class TestFileQuery extends ValidContainerElementAccessSharedQuery, TestQuery { } diff --git a/cpp/common/test/rules/validcontainerelementaccess/test.cpp b/cpp/common/test/rules/validcontainerelementaccess/test.cpp index 45957ec141..55c94cf8f1 100644 --- a/cpp/common/test/rules/validcontainerelementaccess/test.cpp +++ b/cpp/common/test/rules/validcontainerelementaccess/test.cpp @@ -111,7 +111,7 @@ void f11(std::string cs) { const char *cp = cs.c_str(); const char *cpe = cp + 2; - while (cp < cpe) { // COMPLAINT + while (cp < cpe) { // COMPLIANT std::string arg(cp); // COMPLIANT cp += arg.size() + 1; } diff --git a/cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.expected b/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.expected similarity index 100% rename from cpp/autosar/test/rules/A18-1-2/VectorboolSpecializationUsed.expected rename to cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.expected diff --git a/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.expected.qcc b/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.expected.qcc new file mode 100644 index 0000000000..570c7c18ea --- /dev/null +++ b/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.expected.qcc @@ -0,0 +1,7 @@ +| test.cpp:3:19:3:19 | g | Use of std::vector specialization. | +| test.cpp:18:21:18:21 | l | Use of std::vector specialization. | +| test.cpp:20:8:20:9 | l3 | Use of std::vector specialization. | +| test.cpp:23:34:23:35 | l6 | Use of std::vector specialization. | +| test.cpp:24:47:24:48 | l7 | Use of std::vector specialization. | +| test.cpp:27:36:27:37 | it | Use of std::vector specialization. | +| test.cpp:34:40:34:41 | d3 | Use of std::vector specialization. | diff --git a/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.ql b/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.ql new file mode 100644 index 0000000000..a965d5e5d6 --- /dev/null +++ b/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.vectorshouldnotbespecializedwithbool.VectorShouldNotBeSpecializedWithBool + +class TestFileQuery extends VectorShouldNotBeSpecializedWithBoolSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/A18-1-2/test.cpp b/cpp/common/test/rules/vectorshouldnotbespecializedwithbool/test.cpp similarity index 100% rename from cpp/autosar/test/rules/A18-1-2/test.cpp rename to cpp/common/test/rules/vectorshouldnotbespecializedwithbool/test.cpp diff --git a/cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.expected b/cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.expected similarity index 100% rename from cpp/autosar/test/rules/M10-1-3/AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy.expected rename to cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.expected diff --git a/cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.ql b/cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.ql new file mode 100644 index 0000000000..2137cbeb66 --- /dev/null +++ b/cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.virtualandnonvirtualclassinthehierarchy.VirtualAndNonVirtualClassInTheHierarchy + +class TestFileQuery extends VirtualAndNonVirtualClassInTheHierarchySharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M10-1-3/test.cpp b/cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M10-1-3/test.cpp rename to cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/test.cpp diff --git a/cpp/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql b/cpp/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql index 6f4ad4c40e..44947bf85a 100644 --- a/cpp/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql +++ b/cpp/common/test/rules/wrapspuriousfunctioninloop/WrapSpuriousFunctionInLoop.ql @@ -1,2 +1,4 @@ // GENERATED FILE - DO NOT MODIFY import codingstandards.cpp.rules.wrapspuriousfunctioninloop.WrapSpuriousFunctionInLoop + +class TestFileQuery extends WrapSpuriousFunctionInLoopSharedQuery, TestQuery { } diff --git a/cpp/misra/src/codeql-pack.lock.yml b/cpp/misra/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/misra/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/cpp/misra/src/codeql-suites/misra-cpp-advisory.qls b/cpp/misra/src/codeql-suites/misra-cpp-advisory.qls new file mode 100644 index 0000000000..5da16cc2af --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-cpp-advisory.qls @@ -0,0 +1,12 @@ +- description: MISRA C++ 2023 (Default) +- qlpack: codeql/misra-cpp-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/obligation/advisory +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/cpp/misra/src/codeql-suites/misra-cpp-default.qls b/cpp/misra/src/codeql-suites/misra-cpp-default.qls new file mode 100644 index 0000000000..670b043caa --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-cpp-default.qls @@ -0,0 +1,10 @@ +- description: MISRA C++ 2023 (Default) +- qlpack: codeql/misra-cpp-coding-standards +- include: + kind: + - problem + - path-problem +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/cpp/misra/src/codeql-suites/misra-cpp-mandatory.qls b/cpp/misra/src/codeql-suites/misra-cpp-mandatory.qls new file mode 100644 index 0000000000..0c5ec7155f --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-cpp-mandatory.qls @@ -0,0 +1,12 @@ +- description: MISRA C++ 2023 (Default) +- qlpack: codeql/misra-cpp-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/obligation/mandatory +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/cpp/misra/src/codeql-suites/misra-cpp-required.qls b/cpp/misra/src/codeql-suites/misra-cpp-required.qls new file mode 100644 index 0000000000..2fe61301e7 --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-cpp-required.qls @@ -0,0 +1,12 @@ +- description: MISRA C++ 2023 (Default) +- qlpack: codeql/misra-cpp-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/obligation/required +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/cpp/misra/src/codeql-suites/misra-cpp-single-translation-unit.qls b/cpp/misra/src/codeql-suites/misra-cpp-single-translation-unit.qls new file mode 100644 index 0000000000..0782dd876d --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-cpp-single-translation-unit.qls @@ -0,0 +1,12 @@ +- description: MISRA C++ 2023 (Single Translation Unit) +- qlpack: codeql/misra-cpp-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - scope/single-translation-unit +- exclude: + tags contain: + - external/misra/audit + - external/misra/default-disabled diff --git a/cpp/misra/src/codeql-suites/misra-default.qls b/cpp/misra/src/codeql-suites/misra-default.qls new file mode 100644 index 0000000000..3c205157cd --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-default.qls @@ -0,0 +1,2 @@ +- description: "DEPRECATED - MISRA C++ 2023 - use misra-cpp-default.qls instead" +- import: codeql-suites/misra-cpp-default.qls diff --git a/cpp/misra/src/codeql-suites/misra-single-translation-unit.qls b/cpp/misra/src/codeql-suites/misra-single-translation-unit.qls new file mode 100644 index 0000000000..9dcd3f0c97 --- /dev/null +++ b/cpp/misra/src/codeql-suites/misra-single-translation-unit.qls @@ -0,0 +1,2 @@ +- description: "DEPRECATED - MISRA C++ 2023 (Single Translation Unit) - use misra-cpp-single-translation-unit.qls instead" +- import: codeql-suites/misra-cpp-single-translation-unit.qls diff --git a/cpp/misra/src/codingstandards/cpp/misra.qll b/cpp/misra/src/codingstandards/cpp/misra.qll new file mode 100644 index 0000000000..ff308d4fd2 --- /dev/null +++ b/cpp/misra/src/codingstandards/cpp/misra.qll @@ -0,0 +1,4 @@ +import cpp +import misra.Customizations +import codingstandards.cpp.CodingStandards +import codingstandards.cpp.exclusions.cpp.RuleMetadata diff --git a/cpp/misra/src/codingstandards/cpp/misra/Customizations.qll b/cpp/misra/src/codingstandards/cpp/misra/Customizations.qll new file mode 100644 index 0000000000..b95d1bb3b3 --- /dev/null +++ b/cpp/misra/src/codingstandards/cpp/misra/Customizations.qll @@ -0,0 +1,8 @@ +/** + * Contains customizations to the MISRA C++ query rules. + * + * This module is imported by `misra.qll`, so any customizations defined here + * automatically apply to all MISRA C++ queries. + */ + +import cpp diff --git a/cpp/misra/src/qlpack.yml b/cpp/misra/src/qlpack.yml index 47cb6677df..201291b135 100644 --- a/cpp/misra/src/qlpack.yml +++ b/cpp/misra/src/qlpack.yml @@ -1,3 +1,8 @@ -name: misra-cpp-coding-standards -version: 2.9.0 -libraryPathDependencies: common-cpp-coding-standards +name: codeql/misra-cpp-coding-standards +version: 2.49.0-dev +description: MISRA C++ 2023 +default-suite: codeql-suites/misra-cpp-default.qls +license: MIT +dependencies: + codeql/common-cpp-coding-standards: '*' + codeql/cpp-all: 4.0.3 diff --git a/cpp/misra/src/rules/DIR-0-3-1/PossibleMisuseOfInfiniteFloatingPointValue.ql b/cpp/misra/src/rules/DIR-0-3-1/PossibleMisuseOfInfiniteFloatingPointValue.ql new file mode 100644 index 0000000000..0e3363137e --- /dev/null +++ b/cpp/misra/src/rules/DIR-0-3-1/PossibleMisuseOfInfiniteFloatingPointValue.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/possible-misuse-of-infinite-floating-point-value + * @name DIR-0-3-1: Possible misuse of a generate infinite floating point value + * @description Possible misuse of a generate infinite floating point value. + * @kind path-problem + * @precision medium + * @problem.severity warning + * @tags external/misra/id/dir-0-3-1 + * correctness + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.misuseofinfinitefloatingpointvalue.MisuseOfInfiniteFloatingPointValue + +class PossibleMisuseOfInfiniteFloatingPointValueQuery extends MisuseOfInfiniteFloatingPointValueSharedQuery +{ + PossibleMisuseOfInfiniteFloatingPointValueQuery() { + this = FloatingPointPackage::possibleMisuseOfInfiniteFloatingPointValueQuery() + } +} diff --git a/cpp/misra/src/rules/DIR-0-3-1/PossibleMisuseOfNaNFloatingPointValue.ql b/cpp/misra/src/rules/DIR-0-3-1/PossibleMisuseOfNaNFloatingPointValue.ql new file mode 100644 index 0000000000..035edd85b8 --- /dev/null +++ b/cpp/misra/src/rules/DIR-0-3-1/PossibleMisuseOfNaNFloatingPointValue.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/possible-misuse-of-nan-floating-point-value + * @name DIR-0-3-1: Possible mishandling of an undetected NaN value produced by a floating point operation + * @description Possible mishandling of an undetected NaN value produced by a floating point + * operation. + * @kind path-problem + * @precision low + * @problem.severity warning + * @tags external/misra/id/dir-0-3-1 + * correctness + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.misuseofnanfloatingpointvalue.MisuseOfNaNFloatingPointValue + +class PossibleMisuseOfNaNFloatingPointValueQuery extends MisuseOfNaNFloatingPointValueSharedQuery { + PossibleMisuseOfNaNFloatingPointValueQuery() { + this = FloatingPointPackage::possibleMisuseOfNaNFloatingPointValueQuery() + } +} diff --git a/cpp/misra/src/rules/DIR-15-8-1/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql b/cpp/misra/src/rules/DIR-15-8-1/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql new file mode 100644 index 0000000000..daf6e89530 --- /dev/null +++ b/cpp/misra/src/rules/DIR-15-8-1/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/copy-and-move-assignments-shall-handle-self-assignment + * @name DIR-15-8-1: User-provided copy assignment operators and move assignment operators shall handle self-assignment + * @description User-provided copy assignment operators and move assignment operators shall handle + * self-assignment. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/dir-15-8-1 + * external/misra/allocated-target/implementation + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.copyandmoveassignmentsshallhandleselfassignment.CopyAndMoveAssignmentsShallHandleSelfAssignment + +class CopyAndMoveAssignmentsShallHandleSelfAssignmentQuery extends CopyAndMoveAssignmentsShallHandleSelfAssignmentSharedQuery +{ + CopyAndMoveAssignmentsShallHandleSelfAssignmentQuery() { + this = ImportMisra23Package::copyAndMoveAssignmentsShallHandleSelfAssignmentQuery() + } +} diff --git a/cpp/misra/src/rules/DIR-5-7-2/SectionsOfCodeShouldNotBeCommentedOut.ql b/cpp/misra/src/rules/DIR-5-7-2/SectionsOfCodeShouldNotBeCommentedOut.ql new file mode 100644 index 0000000000..e51ae39d3d --- /dev/null +++ b/cpp/misra/src/rules/DIR-5-7-2/SectionsOfCodeShouldNotBeCommentedOut.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/sections-of-code-should-not-be-commented-out + * @name DIR-5-7-2: Sections of code should not be “commented out” + * @description Commented out code may become out of date leading to developer confusion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/dir-5-7-2 + * maintainability + * readability + * correctness + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.sectionsofcodeshallnotbecommentedout.SectionsOfCodeShallNotBeCommentedOut + +class SectionsOfCodeShouldNotBeCommentedOutQuery extends SectionsOfCodeShallNotBeCommentedOutSharedQuery +{ + SectionsOfCodeShouldNotBeCommentedOutQuery() { + this = ImportMisra23Package::sectionsOfCodeShouldNotBeCommentedOutQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-10-0-1/UseSingleGlobalOrMemberDeclarators.ql b/cpp/misra/src/rules/RULE-10-0-1/UseSingleGlobalOrMemberDeclarators.ql new file mode 100644 index 0000000000..ffbc5bacaf --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-0-1/UseSingleGlobalOrMemberDeclarators.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/use-single-global-or-member-declarators + * @name RULE-10-0-1: Multiple declarations in the same global or member declaration sequence + * @description A declaration should not declare more than one variable or member variable. + * @kind problem + * @precision medium + * @problem.severity recommendation + * @tags external/misra/id/rule-10-0-1 + * readability + * maintainability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.multipleglobalormemberdeclarators.MultipleGlobalOrMemberDeclarators + +class UseSingleGlobalOrMemberDeclaratorsQuery extends MultipleGlobalOrMemberDeclaratorsSharedQuery { + UseSingleGlobalOrMemberDeclaratorsQuery() { + this = ImportMisra23Package::useSingleGlobalOrMemberDeclaratorsQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-10-0-1/UseSingleLocalDeclarators.ql b/cpp/misra/src/rules/RULE-10-0-1/UseSingleLocalDeclarators.ql new file mode 100644 index 0000000000..6d756daa87 --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-0-1/UseSingleLocalDeclarators.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/use-single-local-declarators + * @name RULE-10-0-1: Multiple declarations in the same local statement + * @description A declaration should not declare more than one variable or member variable. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-10-0-1 + * readability + * maintainability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.multiplelocaldeclarators.MultipleLocalDeclarators + +class UseSingleLocalDeclaratorsQuery extends MultipleLocalDeclaratorsSharedQuery { + UseSingleLocalDeclaratorsQuery() { this = ImportMisra23Package::useSingleLocalDeclaratorsQuery() } +} diff --git a/cpp/misra/src/rules/RULE-10-2-1/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql b/cpp/misra/src/rules/RULE-10-2-1/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql new file mode 100644 index 0000000000..ab4b6a19a1 --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-2-1/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/enumeration-not-defined-with-an-explicit-underlying-type + * @name RULE-10-2-1: An enumeration shall be defined with an explicit underlying type + * @description An enumeration shall be defined with an explicit underlying type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-10-2-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.enumerationnotdefinedwithanexplicitunderlyingtype.EnumerationNotDefinedWithAnExplicitUnderlyingType + +class EnumerationNotDefinedWithAnExplicitUnderlyingTypeQuery extends EnumerationNotDefinedWithAnExplicitUnderlyingTypeSharedQuery +{ + EnumerationNotDefinedWithAnExplicitUnderlyingTypeQuery() { + this = ImportMisra23Package::enumerationNotDefinedWithAnExplicitUnderlyingTypeQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-10-4-1/AsmDeclarationShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-10-4-1/AsmDeclarationShallNotBeUsed.ql new file mode 100644 index 0000000000..5a2f4c4265 --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-4-1/AsmDeclarationShallNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/asm-declaration-shall-not-be-used + * @name RULE-10-4-1: The asm declaration shall not be used + * @description The asm declaration shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-10-4-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.asmdeclarationused.AsmDeclarationUsed + +class AsmDeclarationShallNotBeUsedQuery extends AsmDeclarationUsedSharedQuery { + AsmDeclarationShallNotBeUsedQuery() { + this = ImportMisra23Package::asmDeclarationShallNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-11-3-2/DeclarationOfAnObjectIndirectionsLevel.ql b/cpp/misra/src/rules/RULE-11-3-2/DeclarationOfAnObjectIndirectionsLevel.ql new file mode 100644 index 0000000000..ddbe4a3db0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-11-3-2/DeclarationOfAnObjectIndirectionsLevel.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/declaration-of-an-object-indirections-level + * @name RULE-11-3-2: The declaration of an object should contain no more than two levels of pointer indirection + * @description Declarations with more than two levels of pointer nesting can result in code that is + * difficult to read and understand. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-11-3-2 + * readability + * maintainability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.donotusemorethantwolevelsofpointerindirection.DoNotUseMoreThanTwoLevelsOfPointerIndirection + +class DeclarationOfAnObjectIndirectionsLevelQuery extends DoNotUseMoreThanTwoLevelsOfPointerIndirectionSharedQuery +{ + DeclarationOfAnObjectIndirectionsLevelQuery() { + this = ImportMisra23Package::declarationOfAnObjectIndirectionsLevelQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-11-6-3/NonUniqueEnumerationConstant.ql b/cpp/misra/src/rules/RULE-11-6-3/NonUniqueEnumerationConstant.ql new file mode 100644 index 0000000000..faa0880a75 --- /dev/null +++ b/cpp/misra/src/rules/RULE-11-6-3/NonUniqueEnumerationConstant.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/non-unique-enumeration-constant + * @name RULE-11-6-3: Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique + * @description Within an enumerator list, the value of an implicitly-specified enumeration constant + * shall be unique. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-11-6-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.nonuniqueenumerationconstant.NonUniqueEnumerationConstant + +class NonUniqueEnumerationConstantQuery extends NonUniqueEnumerationConstantSharedQuery { + NonUniqueEnumerationConstantQuery() { + this = ImportMisra23Package::nonUniqueEnumerationConstantQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-12-2-2/BitFieldShallHaveAnAppropriateType.ql b/cpp/misra/src/rules/RULE-12-2-2/BitFieldShallHaveAnAppropriateType.ql new file mode 100644 index 0000000000..f5041252f9 --- /dev/null +++ b/cpp/misra/src/rules/RULE-12-2-2/BitFieldShallHaveAnAppropriateType.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/bit-field-shall-have-an-appropriate-type + * @name RULE-12-2-2: A bit-field shall have an appropriate type + * @description A bit-field shall have an appropriate type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-12-2-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.bitfieldshallhaveanappropriatetype.BitFieldShallHaveAnAppropriateType + +class BitFieldShallHaveAnAppropriateTypeQuery extends BitFieldShallHaveAnAppropriateTypeSharedQuery { + BitFieldShallHaveAnAppropriateTypeQuery() { + this = ImportMisra23Package::bitFieldShallHaveAnAppropriateTypeQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-12-2-3/SignedIntegerNamedBitFieldHaveALengthOfOneBit.ql b/cpp/misra/src/rules/RULE-12-2-3/SignedIntegerNamedBitFieldHaveALengthOfOneBit.ql new file mode 100644 index 0000000000..df547bbec8 --- /dev/null +++ b/cpp/misra/src/rules/RULE-12-2-3/SignedIntegerNamedBitFieldHaveALengthOfOneBit.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/signed-integer-named-bit-field-have-a-length-of-one-bit + * @name RULE-12-2-3: A named bit-field with signed integer type shall not have a length of one bit + * @description A named bit-field with signed integer type shall not have a length of one bit. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-12-2-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.namedbitfieldswithsignedintegertype.NamedBitFieldsWithSignedIntegerType + +class SignedIntegerNamedBitFieldHaveALengthOfOneBitQuery extends NamedBitFieldsWithSignedIntegerTypeSharedQuery +{ + SignedIntegerNamedBitFieldHaveALengthOfOneBitQuery() { + this = ImportMisra23Package::signedIntegerNamedBitFieldHaveALengthOfOneBitQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-13-1-2/VirtualAndNonVirtualClassInTheHierarchy.ql b/cpp/misra/src/rules/RULE-13-1-2/VirtualAndNonVirtualClassInTheHierarchy.ql new file mode 100644 index 0000000000..75030afbfb --- /dev/null +++ b/cpp/misra/src/rules/RULE-13-1-2/VirtualAndNonVirtualClassInTheHierarchy.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/virtual-and-non-virtual-class-in-the-hierarchy + * @name RULE-13-1-2: An accessible base class shall not be both virtual and non-virtual in the same hierarchy + * @description An accessible base class shall not be both virtual and non-virtual in the same + * hierarchy. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-13-1-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.virtualandnonvirtualclassinthehierarchy.VirtualAndNonVirtualClassInTheHierarchy + +class VirtualAndNonVirtualClassInTheHierarchyQuery extends VirtualAndNonVirtualClassInTheHierarchySharedQuery +{ + VirtualAndNonVirtualClassInTheHierarchyQuery() { + this = ImportMisra23Package::virtualAndNonVirtualClassInTheHierarchyQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-13-3-2/OverridingShallSpecifyDifferentDefaultArguments.ql b/cpp/misra/src/rules/RULE-13-3-2/OverridingShallSpecifyDifferentDefaultArguments.ql new file mode 100644 index 0000000000..519589984e --- /dev/null +++ b/cpp/misra/src/rules/RULE-13-3-2/OverridingShallSpecifyDifferentDefaultArguments.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/overriding-shall-specify-different-default-arguments + * @name RULE-13-3-2: Parameters in an overriding virtual function shall not specify different default arguments + * @description Parameters in an overriding virtual function shall not specify different default + * arguments. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-13-3-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.overridingshallspecifydifferentdefaultarguments.OverridingShallSpecifyDifferentDefaultArguments + +class OverridingShallSpecifyDifferentDefaultArgumentsQuery extends OverridingShallSpecifyDifferentDefaultArgumentsSharedQuery +{ + OverridingShallSpecifyDifferentDefaultArgumentsQuery() { + this = ImportMisra23Package::overridingShallSpecifyDifferentDefaultArgumentsQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-13-3-4/PotentiallyVirtualPointerOnlyComparesToNullptr.ql b/cpp/misra/src/rules/RULE-13-3-4/PotentiallyVirtualPointerOnlyComparesToNullptr.ql new file mode 100644 index 0000000000..1c528396e0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-13-3-4/PotentiallyVirtualPointerOnlyComparesToNullptr.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/potentially-virtual-pointer-only-compares-to-nullptr + * @name RULE-13-3-4: A comparison of a potentially virtual pointer to member function shall only be with nullptr + * @description A comparison of a potentially virtual pointer to member function shall only be with + * nullptr. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-13-3-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.potentiallyvirtualpointeronlycomparestonullptr.PotentiallyVirtualPointerOnlyComparesToNullptr + +class PotentiallyVirtualPointerOnlyComparesToNullptrQuery extends PotentiallyVirtualPointerOnlyComparesToNullptrSharedQuery +{ + PotentiallyVirtualPointerOnlyComparesToNullptrQuery() { + this = ImportMisra23Package::potentiallyVirtualPointerOnlyComparesToNullptrQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-15-1-1/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql b/cpp/misra/src/rules/RULE-15-1-1/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql new file mode 100644 index 0000000000..f23c1afab8 --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-1-1/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/objects-dynamic-type-used-from-constructor-or-destructor + * @name RULE-15-1-1: An object’s dynamic type shall not be used from within its constructor or destructor + * @description An object’s dynamic type shall not be used from within its constructor or + * destructor. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-1-1 + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.objectsdynamictypeusedfromconstructorordestructor.ObjectsDynamicTypeUsedFromConstructorOrDestructor + +class ObjectsDynamicTypeUsedFromConstructorOrDestructorQuery extends ObjectsDynamicTypeUsedFromConstructorOrDestructorSharedQuery +{ + ObjectsDynamicTypeUsedFromConstructorOrDestructorQuery() { + this = ImportMisra23Package::objectsDynamicTypeUsedFromConstructorOrDestructorQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-15-1-2/InitializeAllVirtualBaseClasses.ql b/cpp/misra/src/rules/RULE-15-1-2/InitializeAllVirtualBaseClasses.ql new file mode 100644 index 0000000000..3dd7b7e3e2 --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-1-2/InitializeAllVirtualBaseClasses.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/initialize-all-virtual-base-classes + * @name RULE-15-1-2: All constructors of a class should explicitly initialize all of its virtual base classes and + * @description All constructors of a class should explicitly initialize all of its virtual base + * classes and immediate base classes. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-1-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.initializeallvirtualbaseclasses.InitializeAllVirtualBaseClasses + +class InitializeAllVirtualBaseClassesQuery extends InitializeAllVirtualBaseClassesSharedQuery { + InitializeAllVirtualBaseClassesQuery() { + this = ImportMisra23Package::initializeAllVirtualBaseClassesQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-15-1-5/InitializerListConstructorIsTheOnlyConstructor.ql b/cpp/misra/src/rules/RULE-15-1-5/InitializerListConstructorIsTheOnlyConstructor.ql new file mode 100644 index 0000000000..c7cf1856cd --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-1-5/InitializerListConstructorIsTheOnlyConstructor.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/initializer-list-constructor-is-the-only-constructor + * @name RULE-15-1-5: A class shall only define an initializer-list constructor when it is the only constructor + * @description A class shall only define an initializer-list constructor when it is the only + * constructor. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-1-5 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.initializerlistconstructoristheonlyconstructor.InitializerListConstructorIsTheOnlyConstructor + +class InitializerListConstructorIsTheOnlyConstructorQuery extends InitializerListConstructorIsTheOnlyConstructorSharedQuery +{ + InitializerListConstructorIsTheOnlyConstructorQuery() { + this = ImportMisra23Package::initializerListConstructorIsTheOnlyConstructorQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-16-5-2/AddressOfOperatorOverloaded.ql b/cpp/misra/src/rules/RULE-16-5-2/AddressOfOperatorOverloaded.ql new file mode 100644 index 0000000000..937ec4e9e3 --- /dev/null +++ b/cpp/misra/src/rules/RULE-16-5-2/AddressOfOperatorOverloaded.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/address-of-operator-overloaded + * @name RULE-16-5-2: The address-of operator shall not be overloaded + * @description The address-of operator shall not be overloaded. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-16-5-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.addressofoperatoroverloaded.AddressOfOperatorOverloaded + +class AddressOfOperatorOverloadedQuery extends AddressOfOperatorOverloadedSharedQuery { + AddressOfOperatorOverloadedQuery() { + this = ImportMisra23Package::addressOfOperatorOverloadedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-17-8-1/FunctionTemplatesExplicitlySpecialized.ql b/cpp/misra/src/rules/RULE-17-8-1/FunctionTemplatesExplicitlySpecialized.ql new file mode 100644 index 0000000000..c7b306946b --- /dev/null +++ b/cpp/misra/src/rules/RULE-17-8-1/FunctionTemplatesExplicitlySpecialized.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/function-templates-explicitly-specialized + * @name RULE-17-8-1: Function templates shall not be explicitly specialized + * @description Function templates shall not be explicitly specialized. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-8-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.functiontemplatesexplicitlyspecialized.FunctionTemplatesExplicitlySpecialized + +class FunctionTemplatesExplicitlySpecializedQuery extends FunctionTemplatesExplicitlySpecializedSharedQuery +{ + FunctionTemplatesExplicitlySpecializedQuery() { + this = ImportMisra23Package::functionTemplatesExplicitlySpecializedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-18-1-1/ExceptionObjectHavePointerType.ql b/cpp/misra/src/rules/RULE-18-1-1/ExceptionObjectHavePointerType.ql new file mode 100644 index 0000000000..cbae5c1da4 --- /dev/null +++ b/cpp/misra/src/rules/RULE-18-1-1/ExceptionObjectHavePointerType.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/exception-object-have-pointer-type + * @name RULE-18-1-1: An exception object shall not have pointer type + * @description An exception object shall not have pointer type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-1-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.exceptionobjecthavepointertype.ExceptionObjectHavePointerType + +class ExceptionObjectHavePointerTypeQuery extends ExceptionObjectHavePointerTypeSharedQuery { + ExceptionObjectHavePointerTypeQuery() { + this = ImportMisra23Package::exceptionObjectHavePointerTypeQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-18-1-2/EmptyThrowOnlyWithinACatchHandler.ql b/cpp/misra/src/rules/RULE-18-1-2/EmptyThrowOnlyWithinACatchHandler.ql new file mode 100644 index 0000000000..15ca773943 --- /dev/null +++ b/cpp/misra/src/rules/RULE-18-1-2/EmptyThrowOnlyWithinACatchHandler.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/empty-throw-only-within-a-catch-handler + * @name RULE-18-1-2: An empty throw shall only occur within the compound-statement of a catch handler + * @description An empty throw shall only occur within the compound-statement of a catch handler. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-1-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.emptythrowonlywithinacatchhandler.EmptyThrowOnlyWithinACatchHandler + +class EmptyThrowOnlyWithinACatchHandlerQuery extends EmptyThrowOnlyWithinACatchHandlerSharedQuery { + EmptyThrowOnlyWithinACatchHandlerQuery() { + this = ImportMisra23Package::emptyThrowOnlyWithinACatchHandlerQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-18-3-3/HandlersReferToNonStaticMembersFromTheirClass.ql b/cpp/misra/src/rules/RULE-18-3-3/HandlersReferToNonStaticMembersFromTheirClass.ql new file mode 100644 index 0000000000..b5db9095b1 --- /dev/null +++ b/cpp/misra/src/rules/RULE-18-3-3/HandlersReferToNonStaticMembersFromTheirClass.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/handlers-refer-to-non-static-members-from-their-class + * @name RULE-18-3-3: Handlers for a function-try-block of a constructor or destructor shall not refer to non-static + * @description Handlers for a function-try-block of a constructor or destructor shall not refer to + * non-static members from their class or its bases. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-3-3 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.destroyedvaluereferencedindestructorcatchblock.DestroyedValueReferencedInDestructorCatchBlock + +class HandlersReferToNonStaticMembersFromTheirClassQuery extends DestroyedValueReferencedInDestructorCatchBlockSharedQuery +{ + HandlersReferToNonStaticMembersFromTheirClassQuery() { + this = ImportMisra23Package::handlersReferToNonStaticMembersFromTheirClassQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-18-5-1/NoexceptFunctionShouldNotPropagateToTheCaller.ql b/cpp/misra/src/rules/RULE-18-5-1/NoexceptFunctionShouldNotPropagateToTheCaller.ql new file mode 100644 index 0000000000..61d8a0ebd4 --- /dev/null +++ b/cpp/misra/src/rules/RULE-18-5-1/NoexceptFunctionShouldNotPropagateToTheCaller.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/noexcept-function-should-not-propagate-to-the-caller + * @name RULE-18-5-1: A noexcept function should not attempt to propagate an exception to the calling function + * @description A noexcept function should not attempt to propagate an exception to the calling + * function. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-18-5-1 + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.noexceptfunctionshouldnotpropagatetothecaller.NoexceptFunctionShouldNotPropagateToTheCaller + +class NoexceptFunctionShouldNotPropagateToTheCallerQuery extends NoexceptFunctionShouldNotPropagateToTheCallerSharedQuery +{ + NoexceptFunctionShouldNotPropagateToTheCallerQuery() { + this = ImportMisra23Package::noexceptFunctionShouldNotPropagateToTheCallerQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-0-2/FunctionLikeMacrosDefined.ql b/cpp/misra/src/rules/RULE-19-0-2/FunctionLikeMacrosDefined.ql new file mode 100644 index 0000000000..d9e4d5a810 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-0-2/FunctionLikeMacrosDefined.ql @@ -0,0 +1,20 @@ +/** + * @id cpp/misra/function-like-macros-defined + * @name RULE-19-0-2: Function-like macros shall not be defined + * @description Function-like macros shall not be defined. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-0-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.functionlikemacrosdefined.FunctionLikeMacrosDefined + +class FunctionLikeMacrosDefinedQuery extends FunctionLikeMacrosDefinedSharedQuery { + FunctionLikeMacrosDefinedQuery() { this = ImportMisra23Package::functionLikeMacrosDefinedQuery() } +} diff --git a/cpp/misra/src/rules/RULE-19-0-3/IncludeDirectivesPrecededByPreprocessorDirectives.ql b/cpp/misra/src/rules/RULE-19-0-3/IncludeDirectivesPrecededByPreprocessorDirectives.ql new file mode 100644 index 0000000000..7068c7bea0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-0-3/IncludeDirectivesPrecededByPreprocessorDirectives.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/include-directives-preceded-by-preprocessor-directives + * @name RULE-19-0-3: #include directives should only be preceded by preprocessor directives or comments + * @description Using anything other than other pre-processor directives or comments before an + * '#include' directive makes the code more difficult to read. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-0-3 + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.preprocessorincludespreceded.PreprocessorIncludesPreceded + +class IncludeDirectivesPrecededByPreprocessorDirectivesQuery extends PreprocessorIncludesPrecededSharedQuery +{ + IncludeDirectivesPrecededByPreprocessorDirectivesQuery() { + this = ImportMisra23Package::includeDirectivesPrecededByPreprocessorDirectivesQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-1-3/IdentifiersUsedInTheControllingExpressionOf.ql b/cpp/misra/src/rules/RULE-19-1-3/IdentifiersUsedInTheControllingExpressionOf.ql new file mode 100644 index 0000000000..c30be08109 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-1-3/IdentifiersUsedInTheControllingExpressionOf.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/identifiers-used-in-the-controlling-expression-of + * @name RULE-19-1-3: All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be + * @description All identifiers used in the controlling expression of #if or #elif preprocessing + * directives shall be defined prior to evaluation. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-1-3 + * correctness + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.undefinedmacroidentifiers.UndefinedMacroIdentifiers + +class IdentifiersUsedInTheControllingExpressionOfQuery extends UndefinedMacroIdentifiersSharedQuery { + IdentifiersUsedInTheControllingExpressionOfQuery() { + this = ImportMisra23Package::identifiersUsedInTheControllingExpressionOfQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-2-3/CharsThatShouldNotOccurInHeaderFileName.ql b/cpp/misra/src/rules/RULE-19-2-3/CharsThatShouldNotOccurInHeaderFileName.ql new file mode 100644 index 0000000000..30c6c50662 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-2-3/CharsThatShouldNotOccurInHeaderFileName.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/chars-that-should-not-occur-in-header-file-name + * @name RULE-19-2-3: The ' or " or \ characters and the /* or // character sequences shall not occur in a header file + * @description The ' or " or \ characters and the /* or // character sequences shall not occur in a + * header file name. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-2-3 + * scope/single-translation-unit + * correctness + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.preprocessorincludesforbiddenheadernames.PreprocessorIncludesForbiddenHeaderNames + +class CharsThatShouldNotOccurInHeaderFileNameQuery extends PreprocessorIncludesForbiddenHeaderNamesSharedQuery +{ + CharsThatShouldNotOccurInHeaderFileNameQuery() { + this = ImportMisra23Package::charsThatShouldNotOccurInHeaderFileNameQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-3-1/AndPreprocessorOperatorsShouldNotBeUsed.ql b/cpp/misra/src/rules/RULE-19-3-1/AndPreprocessorOperatorsShouldNotBeUsed.ql new file mode 100644 index 0000000000..bc423a0051 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-3-1/AndPreprocessorOperatorsShouldNotBeUsed.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/and-preprocessor-operators-should-not-be-used + * @name RULE-19-3-1: The # and ## preprocessor operators should not be used + * @description The order of evaluation for the '#' and '##' operators may differ between compilers, + * which can cause unexpected behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-3-1 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.hashoperatorsused.HashOperatorsUsed + +class AndPreprocessorOperatorsShouldNotBeUsedQuery extends HashOperatorsUsedSharedQuery { + AndPreprocessorOperatorsShouldNotBeUsedQuery() { + this = ImportMisra23Package::andPreprocessorOperatorsShouldNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-3-2/MacroParameterFollowingHash.ql b/cpp/misra/src/rules/RULE-19-3-2/MacroParameterFollowingHash.ql new file mode 100644 index 0000000000..12e95ced04 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-3-2/MacroParameterFollowingHash.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/macro-parameter-following-hash + * @name RULE-19-3-2: A macro parameter immediately following a # operator shall not be immediately followed by a ## + * @description A macro parameter immediately following a # operator shall not be immediately + * followed by a ## operator. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-3-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.macroparameterfollowinghash.MacroParameterFollowingHash + +class MacroParameterFollowingHashQuery extends MacroParameterFollowingHashSharedQuery { + MacroParameterFollowingHashQuery() { + this = ImportMisra23Package::macroParameterFollowingHashQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-3-3/AMixedUseMacroArgumentSubjectToExpansion.ql b/cpp/misra/src/rules/RULE-19-3-3/AMixedUseMacroArgumentSubjectToExpansion.ql new file mode 100644 index 0000000000..9cb0a7e9c5 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-3-3/AMixedUseMacroArgumentSubjectToExpansion.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/a-mixed-use-macro-argument-subject-to-expansion + * @name RULE-19-3-3: The argument to a mixed-use macro parameter shall not be subject to further expansion + * @description The argument to a mixed-use macro parameter shall not be subject to further + * expansion. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-3-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.amixedusemacroargumentsubjecttoexpansion.AMixedUseMacroArgumentSubjectToExpansion + +class AMixedUseMacroArgumentSubjectToExpansionQuery extends AMixedUseMacroArgumentSubjectToExpansionSharedQuery +{ + AMixedUseMacroArgumentSubjectToExpansionQuery() { + this = ImportMisra23Package::aMixedUseMacroArgumentSubjectToExpansionQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-19-3-5/TokensThatLookLikeDirectivesInAMacroArgument.ql b/cpp/misra/src/rules/RULE-19-3-5/TokensThatLookLikeDirectivesInAMacroArgument.ql new file mode 100644 index 0000000000..2fa672e644 --- /dev/null +++ b/cpp/misra/src/rules/RULE-19-3-5/TokensThatLookLikeDirectivesInAMacroArgument.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/tokens-that-look-like-directives-in-a-macro-argument + * @name RULE-19-3-5: Tokens that look like a preprocessing directive shall not occur within a macro argument + * @description Arguments to a function-like macro shall not contain tokens that look like + * pre-processing directives or else behaviour after macro expansion is unpredictable. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-19-3-5 + * readability + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument + +class TokensThatLookLikeDirectivesInAMacroArgumentQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery +{ + TokensThatLookLikeDirectivesInAMacroArgumentQuery() { + this = ImportMisra23Package::tokensThatLookLikeDirectivesInAMacroArgumentQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-21-10-3/CsignalFacilitiesUsed.ql b/cpp/misra/src/rules/RULE-21-10-3/CsignalFacilitiesUsed.ql new file mode 100644 index 0000000000..3e8c58a8da --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-10-3/CsignalFacilitiesUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/csignal-facilities-used + * @name RULE-21-10-3: The facilities provided by the standard header file shall not be used + * @description Signal handling contains implementation-defined and undefined behaviour. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-21-10-3 + * maintainability + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.csignalfunctionsused.CsignalFunctionsUsed + +class CsignalFacilitiesUsedQuery extends CsignalFunctionsUsedSharedQuery { + CsignalFacilitiesUsedQuery() { this = ImportMisra23Package::csignalFacilitiesUsedQuery() } +} diff --git a/cpp/misra/src/rules/RULE-21-10-3/CsignalTypesShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-21-10-3/CsignalTypesShallNotBeUsed.ql new file mode 100644 index 0000000000..0fe1b1dfba --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-10-3/CsignalTypesShallNotBeUsed.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/csignal-types-shall-not-be-used + * @name RULE-21-10-3: The signal-handling types of shall not be used + * @description The types provided by the standard header file shall not be used. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-21-10-3 + * maintainability + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.csignaltypesused.CsignalTypesUsed + +class CsignalTypesShallNotBeUsedQuery extends CsignalTypesUsedSharedQuery { + CsignalTypesShallNotBeUsedQuery() { + this = ImportMisra23Package::csignalTypesShallNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-21-2-1/AtofAtoiAtolAndAtollUsed.ql b/cpp/misra/src/rules/RULE-21-2-1/AtofAtoiAtolAndAtollUsed.ql new file mode 100644 index 0000000000..e5b48d55a7 --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-2-1/AtofAtoiAtolAndAtollUsed.ql @@ -0,0 +1,20 @@ +/** + * @id cpp/misra/atof-atoi-atol-and-atoll-used + * @name RULE-21-2-1: The library functions atof, atoi, atol and atoll from shall not be used + * @description The library functions atof, atoi, atol and atoll from shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-2-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.atofatoiatolandatollused.AtofAtoiAtolAndAtollUsed + +class AtofAtoiAtolAndAtollUsedQuery extends AtofAtoiAtolAndAtollUsedSharedQuery { + AtofAtoiAtolAndAtollUsedQuery() { this = ImportMisra23Package::atofAtoiAtolAndAtollUsedQuery() } +} diff --git a/cpp/misra/src/rules/RULE-21-2-4/MacroOffsetofShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-21-2-4/MacroOffsetofShallNotBeUsed.ql new file mode 100644 index 0000000000..fa6df051ca --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-2-4/MacroOffsetofShallNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/macro-offsetof-shall-not-be-used + * @name RULE-21-2-4: The macro offsetof shall not be used + * @description The macro offsetof shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-2-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.macrooffsetofused.MacroOffsetofUsed + +class MacroOffsetofShallNotBeUsedQuery extends MacroOffsetofUsedSharedQuery { + MacroOffsetofShallNotBeUsedQuery() { + this = ImportMisra23Package::macroOffsetofShallNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-21-6-4/GlobalSizedOperatorDeleteShallBeDefined.ql b/cpp/misra/src/rules/RULE-21-6-4/GlobalSizedOperatorDeleteShallBeDefined.ql new file mode 100644 index 0000000000..eb9be3af15 --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-6-4/GlobalSizedOperatorDeleteShallBeDefined.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/global-sized-operator-delete-shall-be-defined + * @name RULE-21-6-4: Sized 'operator delete' must be defined globally if unsized 'operator delete' is defined globally + * @description If a project defines the unsized version of a global operator delete, then the sized + * version shall be defined. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-6-4 + * maintainability + * scope/system + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.globalsizedoperatordeletenotdefined.GlobalSizedOperatorDeleteNotDefined + +class GlobalSizedOperatorDeleteShallBeDefinedQuery extends GlobalSizedOperatorDeleteNotDefinedSharedQuery +{ + GlobalSizedOperatorDeleteShallBeDefinedQuery() { + this = ImportMisra23Package::globalSizedOperatorDeleteShallBeDefinedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-21-6-4/GlobalUnsizedOperatorDeleteShallBeDefined.ql b/cpp/misra/src/rules/RULE-21-6-4/GlobalUnsizedOperatorDeleteShallBeDefined.ql new file mode 100644 index 0000000000..8a80b36e3f --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-6-4/GlobalUnsizedOperatorDeleteShallBeDefined.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/global-unsized-operator-delete-shall-be-defined + * @name RULE-21-6-4: Unsized 'operator delete' must be defined globally if sized 'operator delete' is defined globally + * @description If a project defines the sized version of a global operator delete, then the unsized + * version shall be defined. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-6-4 + * maintainability + * scope/system + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.globalunsizedoperatordeletenotdefined.GlobalUnsizedOperatorDeleteNotDefined + +class GlobalUnsizedOperatorDeleteShallBeDefinedQuery extends GlobalUnsizedOperatorDeleteNotDefinedSharedQuery +{ + GlobalUnsizedOperatorDeleteShallBeDefinedQuery() { + this = ImportMisra23Package::globalUnsizedOperatorDeleteShallBeDefinedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-21-6-5/PointerToAnIncompleteClassTypeDeleted.ql b/cpp/misra/src/rules/RULE-21-6-5/PointerToAnIncompleteClassTypeDeleted.ql new file mode 100644 index 0000000000..29d20d5d07 --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-6-5/PointerToAnIncompleteClassTypeDeleted.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/pointer-to-an-incomplete-class-type-deleted + * @name RULE-21-6-5: A pointer to an incomplete class type shall not be deleted + * @description Do not delete pointers to incomplete classes to prevent undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-6-5 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.deleteofpointertoincompleteclass.DeleteOfPointerToIncompleteClass + +class PointerToAnIncompleteClassTypeDeletedQuery extends DeleteOfPointerToIncompleteClassSharedQuery +{ + PointerToAnIncompleteClassTypeDeletedQuery() { + this = ImportMisra23Package::pointerToAnIncompleteClassTypeDeletedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-25-5-2/PointersReturnedByLocaleFunctionsMustBeUsedAsConst.ql b/cpp/misra/src/rules/RULE-25-5-2/PointersReturnedByLocaleFunctionsMustBeUsedAsConst.ql new file mode 100644 index 0000000000..1feebdc67c --- /dev/null +++ b/cpp/misra/src/rules/RULE-25-5-2/PointersReturnedByLocaleFunctionsMustBeUsedAsConst.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/pointers-returned-by-locale-functions-must-be-used-as-const + * @name RULE-25-5-2: The pointers returned by environment functions should be treated as const + * @description The pointers returned by the C++ Standard Library functions localeconv, getenv, + * setlocale or strerror must only be used as if they have pointer to const-qualified + * type. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-25-5-2 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.constlikereturnvalue.ConstLikeReturnValue + +class PointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery extends ConstLikeReturnValueSharedQuery +{ + PointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery() { + this = ImportMisra23Package::pointersReturnedByLocaleFunctionsMustBeUsedAsConstQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersMisra.ql b/cpp/misra/src/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersMisra.ql new file mode 100644 index 0000000000..a38ce60039 --- /dev/null +++ b/cpp/misra/src/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersMisra.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/call-to-setlocale-invalidates-old-pointers-misra + * @name RULE-25-5-3: The pointer returned by the Standard Library env functions is invalid + * @description The pointer returned by the Standard Library functions asctime, ctime, gmtime, + * localtime, localeconv, getenv, setlocale or strerror may be invalid following a + * subsequent call to the same function. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-25-5-3 + * correctness + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.invalidatedenvstringpointers.InvalidatedEnvStringPointers + +class CallToSetlocaleInvalidatesOldPointersMisraQuery extends InvalidatedEnvStringPointersSharedQuery +{ + CallToSetlocaleInvalidatesOldPointersMisraQuery() { + this = ImportMisra23Package::callToSetlocaleInvalidatesOldPointersMisraQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersWarnMisra.ql b/cpp/misra/src/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersWarnMisra.ql new file mode 100644 index 0000000000..d0b4179412 --- /dev/null +++ b/cpp/misra/src/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersWarnMisra.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/call-to-setlocale-invalidates-old-pointers-warn-misra + * @name RULE-25-5-3: The pointer returned by the Standard Library env functions is invalid warning + * @description The pointer returned by the Standard Library functions asctime, ctime, gmtime, + * localtime, localeconv, getenv, setlocale or strerror may be invalid following a + * subsequent call to the same function. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-25-5-3 + * correctness + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.invalidatedenvstringpointerswarn.InvalidatedEnvStringPointersWarn + +class CallToSetlocaleInvalidatesOldPointersWarnMisraQuery extends InvalidatedEnvStringPointersWarnSharedQuery +{ + CallToSetlocaleInvalidatesOldPointersWarnMisraQuery() { + this = ImportMisra23Package::callToSetlocaleInvalidatesOldPointersWarnMisraQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-26-3-1/VectorShouldNotBeSpecializedWithBool.ql b/cpp/misra/src/rules/RULE-26-3-1/VectorShouldNotBeSpecializedWithBool.ql new file mode 100644 index 0000000000..90037b5f29 --- /dev/null +++ b/cpp/misra/src/rules/RULE-26-3-1/VectorShouldNotBeSpecializedWithBool.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/vector-should-not-be-specialized-with-bool + * @name RULE-26-3-1: std::vector should not be specialized with bool + * @description std::vector should not be specialized with bool. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-26-3-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.vectorshouldnotbespecializedwithbool.VectorShouldNotBeSpecializedWithBool + +class VectorShouldNotBeSpecializedWithBoolQuery extends VectorShouldNotBeSpecializedWithBoolSharedQuery +{ + VectorShouldNotBeSpecializedWithBoolQuery() { + this = ImportMisra23Package::vectorShouldNotBeSpecializedWithBoolQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-28-6-2/ForwardingReferencesAndForwardNotUsedTogether.ql b/cpp/misra/src/rules/RULE-28-6-2/ForwardingReferencesAndForwardNotUsedTogether.ql new file mode 100644 index 0000000000..dc407512cc --- /dev/null +++ b/cpp/misra/src/rules/RULE-28-6-2/ForwardingReferencesAndForwardNotUsedTogether.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/forwarding-references-and-forward-not-used-together + * @name RULE-28-6-2: Forwarding references and std::forward shall be used together + * @description Forwarding references and std::forward shall be used together. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-28-6-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.forwardingreferencesandforwardnotusedtogether.ForwardingReferencesAndForwardNotUsedTogether + +class ForwardingReferencesAndForwardNotUsedTogetherQuery extends ForwardingReferencesAndForwardNotUsedTogetherSharedQuery +{ + ForwardingReferencesAndForwardNotUsedTogetherQuery() { + this = ImportMisra23Package::forwardingReferencesAndForwardNotUsedTogetherQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-28-6-3/ObjectUsedWhileInPotentiallyMovedFromState.ql b/cpp/misra/src/rules/RULE-28-6-3/ObjectUsedWhileInPotentiallyMovedFromState.ql new file mode 100644 index 0000000000..db3bbbb700 --- /dev/null +++ b/cpp/misra/src/rules/RULE-28-6-3/ObjectUsedWhileInPotentiallyMovedFromState.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/object-used-while-in-potentially-moved-from-state + * @name RULE-28-6-3: An object shall not be used while in a potentially moved-from state + * @description Moved-from object shall not be read-accessed. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-28-6-3 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.movedfromobjectsunspecifiedstate.MovedFromObjectsUnspecifiedState + +class ObjectUsedWhileInPotentiallyMovedFromStateQuery extends MovedFromObjectsUnspecifiedStateSharedQuery +{ + ObjectUsedWhileInPotentiallyMovedFromStateQuery() { + this = ImportMisra23Package::objectUsedWhileInPotentiallyMovedFromStateQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-30-0-1/CstdioFunctionsShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-30-0-1/CstdioFunctionsShallNotBeUsed.ql new file mode 100644 index 0000000000..58c8a500f4 --- /dev/null +++ b/cpp/misra/src/rules/RULE-30-0-1/CstdioFunctionsShallNotBeUsed.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/cstdio-functions-shall-not-be-used + * @name RULE-30-0-1: The stream input/output library functions shall not be used + * @description The C Library input/output functions shall not be used. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-30-0-1 + * maintainability + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.cstdiofunctionsused.CstdioFunctionsUsed + +class CstdioFunctionsShallNotBeUsedQuery extends CstdioFunctionsUsedSharedQuery { + CstdioFunctionsShallNotBeUsedQuery() { + this = ImportMisra23Package::cstdioFunctionsShallNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-30-0-1/CstdioMacrosShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-30-0-1/CstdioMacrosShallNotBeUsed.ql new file mode 100644 index 0000000000..8f0b9438e3 --- /dev/null +++ b/cpp/misra/src/rules/RULE-30-0-1/CstdioMacrosShallNotBeUsed.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/cstdio-macros-shall-not-be-used + * @name RULE-30-0-1: The stream input/output library macros shall not be used + * @description The C Library input/output functions shall not be used. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-30-0-1 + * maintainability + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.cstdiomacrosused.CstdioMacrosUsed + +class CstdioMacrosShallNotBeUsedQuery extends CstdioMacrosUsedSharedQuery { + CstdioMacrosShallNotBeUsedQuery() { + this = ImportMisra23Package::cstdioMacrosShallNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-30-0-1/CstdioTypesShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-30-0-1/CstdioTypesShallNotBeUsed.ql new file mode 100644 index 0000000000..6966c85068 --- /dev/null +++ b/cpp/misra/src/rules/RULE-30-0-1/CstdioTypesShallNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/cstdio-types-shall-not-be-used + * @name RULE-30-0-1: The stream input/output library types shall not be used + * @description The C Library input/output functions shall not be used. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-30-0-1 + * maintainability + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.cstdiotypesused.CstdioTypesUsed + +class CstdioTypesShallNotBeUsedQuery extends CstdioTypesUsedSharedQuery { + CstdioTypesShallNotBeUsedQuery() { this = ImportMisra23Package::cstdioTypesShallNotBeUsedQuery() } +} diff --git a/cpp/misra/src/rules/RULE-30-0-2/ReadsAndWritesOnStreamNotSeparatedByPositioning.ql b/cpp/misra/src/rules/RULE-30-0-2/ReadsAndWritesOnStreamNotSeparatedByPositioning.ql new file mode 100644 index 0000000000..a5304c6708 --- /dev/null +++ b/cpp/misra/src/rules/RULE-30-0-2/ReadsAndWritesOnStreamNotSeparatedByPositioning.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/reads-and-writes-on-stream-not-separated-by-positioning + * @name RULE-30-0-2: Reads and writes on the same file stream shall be separated by a positioning operation + * @description Alternate input and output operations on a file stream shall not be used without an + * intervening flush or positioning call. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-30-0-2 + * correctness + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.iofstreammissingpositioning.IOFstreamMissingPositioning + +class ReadsAndWritesOnStreamNotSeparatedByPositioningQuery extends IOFstreamMissingPositioningSharedQuery +{ + ReadsAndWritesOnStreamNotSeparatedByPositioningQuery() { + this = ImportMisra23Package::readsAndWritesOnStreamNotSeparatedByPositioningQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-5-13-1/BackslashCharacterMisuse.ql b/cpp/misra/src/rules/RULE-5-13-1/BackslashCharacterMisuse.ql new file mode 100644 index 0000000000..fde97e062c --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-13-1/BackslashCharacterMisuse.ql @@ -0,0 +1,21 @@ +/** + * @id cpp/misra/backslash-character-misuse + * @name RULE-5-13-1: In character literals and non-raw string literals, \ shall only be used to form a defined escape + * @description In character literals and non-raw string literals, \ shall only be used to form a + * defined escape sequence or universal character name. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-13-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.backslashcharactermisuse.BackslashCharacterMisuse + +class BackslashCharacterMisuseQuery extends BackslashCharacterMisuseSharedQuery { + BackslashCharacterMisuseQuery() { this = ImportMisra23Package::backslashCharacterMisuseQuery() } +} diff --git a/cpp/misra/src/rules/RULE-5-13-2/NonTerminatedEscapeSequences.ql b/cpp/misra/src/rules/RULE-5-13-2/NonTerminatedEscapeSequences.ql new file mode 100644 index 0000000000..d21f1ef1f9 --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-13-2/NonTerminatedEscapeSequences.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/non-terminated-escape-sequences + * @name RULE-5-13-2: Octal escape sequences, hexadecimal escape sequences, and universal character names shall be + * @description Octal escape sequences, hexadecimal escape sequences, and universal character names + * shall be terminated. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-13-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.nonterminatedescapesequences.NonTerminatedEscapeSequences + +class NonTerminatedEscapeSequencesQuery extends NonTerminatedEscapeSequencesSharedQuery { + NonTerminatedEscapeSequencesQuery() { + this = ImportMisra23Package::nonTerminatedEscapeSequencesQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-5-13-3/OctalConstantsUsed.ql b/cpp/misra/src/rules/RULE-5-13-3/OctalConstantsUsed.ql new file mode 100644 index 0000000000..38bb96faac --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-13-3/OctalConstantsUsed.ql @@ -0,0 +1,20 @@ +/** + * @id cpp/misra/octal-constants-used + * @name RULE-5-13-3: Octal constants shall not be used + * @description Octal constants shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-13-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.useofnonzerooctalliteral.UseOfNonZeroOctalLiteral + +class OctalConstantsUsedQuery extends UseOfNonZeroOctalLiteralSharedQuery { + OctalConstantsUsedQuery() { this = ImportMisra23Package::octalConstantsUsedQuery() } +} diff --git a/cpp/misra/src/rules/RULE-5-13-4/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql b/cpp/misra/src/rules/RULE-5-13-4/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql new file mode 100644 index 0000000000..b3802cf0be --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-13-4/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/unsigned-integer-literals-not-appropriately-suffixed + * @name RULE-5-13-4: Unsigned integer literals shall be appropriately suffixed + * @description Unsigned integer literals shall be appropriately suffixed. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-13-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.unsignedintegerliteralsnotappropriatelysuffixed.UnsignedIntegerLiteralsNotAppropriatelySuffixed + +class UnsignedIntegerLiteralsNotAppropriatelySuffixedQuery extends UnsignedIntegerLiteralsNotAppropriatelySuffixedSharedQuery +{ + UnsignedIntegerLiteralsNotAppropriatelySuffixedQuery() { + this = ImportMisra23Package::unsignedIntegerLiteralsNotAppropriatelySuffixedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-5-13-5/LowercaseLStartsInLiteralSuffix.ql b/cpp/misra/src/rules/RULE-5-13-5/LowercaseLStartsInLiteralSuffix.ql new file mode 100644 index 0000000000..a47c0ded0c --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-13-5/LowercaseLStartsInLiteralSuffix.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/lowercase-l-starts-in-literal-suffix + * @name RULE-5-13-5: The lowercase form of L shall not be used as the first character in a literal suffix + * @description The lowercase form of L shall not be used as the first character in a literal + * suffix. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-13-5 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.lowercaselstartsinliteralsuffix.LowercaseLStartsInLiteralSuffix + +class LowercaseLStartsInLiteralSuffixQuery extends LowercaseLStartsInLiteralSuffixSharedQuery { + LowercaseLStartsInLiteralSuffixQuery() { + this = ImportMisra23Package::lowercaseLStartsInLiteralSuffixQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-5-7-1/CharacterSequenceUsedWithinACStyleComment.ql b/cpp/misra/src/rules/RULE-5-7-1/CharacterSequenceUsedWithinACStyleComment.ql new file mode 100644 index 0000000000..1bdb42de77 --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-7-1/CharacterSequenceUsedWithinACStyleComment.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/character-sequence-used-within-ac-style-comment + * @name RULE-5-7-1: The character sequence /* shall not be used within a C-style comment + * @description The character sequence /* shall not be used within a C-style comment. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-7-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.charactersequenceusedwithinacstylecomment.CharacterSequenceUsedWithinACStyleComment + +class CharacterSequenceUsedWithinACStyleCommentQuery extends CharacterSequenceUsedWithinACStyleCommentSharedQuery +{ + CharacterSequenceUsedWithinACStyleCommentQuery() { + this = ImportMisra23Package::characterSequenceUsedWithinACStyleCommentQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-5-7-3/LineSplicingUsedInComments.ql b/cpp/misra/src/rules/RULE-5-7-3/LineSplicingUsedInComments.ql new file mode 100644 index 0000000000..ae58fdcda9 --- /dev/null +++ b/cpp/misra/src/rules/RULE-5-7-3/LineSplicingUsedInComments.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/line-splicing-used-in-comments + * @name RULE-5-7-3: Line-splicing shall not be used in // comments + * @description Line-splicing shall not be used in // comments. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-5-7-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.linesplicingusedincomments.LineSplicingUsedInComments + +class LineSplicingUsedInCommentsQuery extends LineSplicingUsedInCommentsSharedQuery { + LineSplicingUsedInCommentsQuery() { + this = ImportMisra23Package::lineSplicingUsedInCommentsQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-0-3/GlobalNamespaceDeclarations.ql b/cpp/misra/src/rules/RULE-6-0-3/GlobalNamespaceDeclarations.ql new file mode 100644 index 0000000000..addd8f2eab --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-0-3/GlobalNamespaceDeclarations.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/global-namespace-declarations + * @name RULE-6-0-3: The only declarations in the global namespace should be main, namespace declarations and extern "C" + * @description The only declarations in the global namespace should be main, namespace declarations + * and extern "C" declarations. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-0-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.globalnamespacedeclarations.GlobalNamespaceDeclarations + +class GlobalNamespaceDeclarationsQuery extends GlobalNamespaceDeclarationsSharedQuery { + GlobalNamespaceDeclarationsQuery() { + this = ImportMisra23Package::globalNamespaceDeclarationsQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-0-4/NonGlobalFunctionMain.ql b/cpp/misra/src/rules/RULE-6-0-4/NonGlobalFunctionMain.ql new file mode 100644 index 0000000000..f9eb9e1d44 --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-0-4/NonGlobalFunctionMain.ql @@ -0,0 +1,21 @@ +/** + * @id cpp/misra/non-global-function-main + * @name RULE-6-0-4: The identifier main shall not be used for a function other than the global function main + * @description The identifier main shall not be used for a function other than the global function + * main. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-0-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.nonglobalfunctionmain.NonGlobalFunctionMain + +class NonGlobalFunctionMainQuery extends NonGlobalFunctionMainSharedQuery { + NonGlobalFunctionMainQuery() { this = ImportMisra23Package::nonGlobalFunctionMainQuery() } +} diff --git a/cpp/misra/src/rules/RULE-6-2-1/OneDefinitionRuleViolated.ql b/cpp/misra/src/rules/RULE-6-2-1/OneDefinitionRuleViolated.ql new file mode 100644 index 0000000000..dbef1e4d54 --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-2-1/OneDefinitionRuleViolated.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/one-definition-rule-violated + * @name RULE-6-2-1: The one-definition rule shall not be violated + * @description The one-definition rule specifies when there should be a single definition of an + * element and a violation of that rule leads to undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-2-1 + * correctness + * scope/system + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.onedefinitionruleviolation.OneDefinitionRuleViolation + +class OneDefinitionRuleViolatedQuery extends OneDefinitionRuleViolationSharedQuery { + OneDefinitionRuleViolatedQuery() { this = ImportMisra23Package::oneDefinitionRuleViolatedQuery() } +} diff --git a/cpp/misra/src/rules/RULE-6-4-1/VariableDeclaredInInnerScopeHidesOuterScope.ql b/cpp/misra/src/rules/RULE-6-4-1/VariableDeclaredInInnerScopeHidesOuterScope.ql new file mode 100644 index 0000000000..85ece40dc8 --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-4-1/VariableDeclaredInInnerScopeHidesOuterScope.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/variable-declared-in-inner-scope-hides-outer-scope + * @name RULE-6-4-1: A variable declared in an inner scope shall not hide a variable declared in an outer scope + * @description Use of an identifier declared in an inner scope with an identical name to an + * identifier in an outer scope can lead to inadvertent errors if the incorrect + * identifier is modified. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-4-1 + * readability + * maintainability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.identifierhidden.IdentifierHidden + +class VariableDeclaredInInnerScopeHidesOuterScopeQuery extends IdentifierHiddenSharedQuery { + VariableDeclaredInInnerScopeHidesOuterScopeQuery() { + this = ImportMisra23Package::variableDeclaredInInnerScopeHidesOuterScopeQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-4-2/DefinitionShallBeConsideredForUnqualifiedLookup.ql b/cpp/misra/src/rules/RULE-6-4-2/DefinitionShallBeConsideredForUnqualifiedLookup.ql new file mode 100644 index 0000000000..faa0857d62 --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-4-2/DefinitionShallBeConsideredForUnqualifiedLookup.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/definition-shall-be-considered-for-unqualified-lookup + * @name RULE-6-4-2: Using declaration followed by new definition + * @description A using declaration that makes a symbol available for unqualified lookup does not + * included definitions defined after the using declaration which can result in + * unexpected behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-4-2 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.definitionnotconsideredforunqualifiedlookup.DefinitionNotConsideredForUnqualifiedLookup + +class DefinitionShallBeConsideredForUnqualifiedLookupQuery extends DefinitionNotConsideredForUnqualifiedLookupSharedQuery +{ + DefinitionShallBeConsideredForUnqualifiedLookupQuery() { + this = ImportMisra23Package::definitionShallBeConsideredForUnqualifiedLookupQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-4-2/InheritedNonOverridableMemberFunction.ql b/cpp/misra/src/rules/RULE-6-4-2/InheritedNonOverridableMemberFunction.ql new file mode 100644 index 0000000000..b81f2a2c4f --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-4-2/InheritedNonOverridableMemberFunction.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/inherited-non-overridable-member-function + * @name RULE-6-4-2: Member function hides inherited member function + * @description A non-overriding member function definition that hides an inherited member function + * can result in unexpected behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-4-2 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.hiddeninheritednonoverridablememberfunction.HiddenInheritedNonOverridableMemberFunction + +class InheritedNonOverridableMemberFunctionQuery extends HiddenInheritedNonOverridableMemberFunctionSharedQuery +{ + InheritedNonOverridableMemberFunctionQuery() { + this = ImportMisra23Package::inheritedNonOverridableMemberFunctionQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-4-2/InheritedOverridableMemberFunction.ql b/cpp/misra/src/rules/RULE-6-4-2/InheritedOverridableMemberFunction.ql new file mode 100644 index 0000000000..9fa94560f4 --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-4-2/InheritedOverridableMemberFunction.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/inherited-overridable-member-function + * @name RULE-6-4-2: Member function hides inherited member function + * @description An overriding member function definition thats hides an overload of the overridden + * inherited member function can result in unexpected behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-4-2 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.hiddeninheritedoverridablememberfunction.HiddenInheritedOverridableMemberFunction + +class InheritedOverridableMemberFunctionQuery extends HiddenInheritedOverridableMemberFunctionSharedQuery +{ + InheritedOverridableMemberFunctionQuery() { + this = ImportMisra23Package::inheritedOverridableMemberFunctionQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThis.ql b/cpp/misra/src/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThis.ql new file mode 100644 index 0000000000..3d43b4134a --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThis.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/name-shall-be-referred-using-a-qualified-id-or-this + * @name RULE-6-4-3: In a class template with a dependent base, any name that may be found in that dependent base shall shall be referred to using a qualified-id or this-> + * @description Not using a qualified-id or `this->` syntax for identifiers used in a class template + * makes the code more difficult to understand. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-6-4-3 + * maintainability + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.namenotreferredusingaqualifiedidorthis.NameNotReferredUsingAQualifiedIdOrThis + +class NameShallBeReferredUsingAQualifiedIdOrThisQuery extends NameNotReferredUsingAQualifiedIdOrThisSharedQuery +{ + NameShallBeReferredUsingAQualifiedIdOrThisQuery() { + this = ImportMisra23Package::nameShallBeReferredUsingAQualifiedIdOrThisQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThisAudit.ql b/cpp/misra/src/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThisAudit.ql new file mode 100644 index 0000000000..df2180fc7b --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThisAudit.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/name-shall-be-referred-using-a-qualified-id-or-this-audit + * @name RULE-6-4-3: (Audit) In a class template with a dependent base, any name that may be found in that dependent base shall shall be referred to using a qualified-id or this-> + * @description Not using a qualified-id or `this->` syntax for identifiers used in a class template + * makes the code more difficult to understand. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-6-4-3 + * maintainability + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.namenotreferredusingaqualifiedidorthisaudit.NameNotReferredUsingAQualifiedIdOrThisAudit + +class NameShallBeReferredUsingAQualifiedIdOrThisAuditQuery extends NameNotReferredUsingAQualifiedIdOrThisAuditSharedQuery +{ + NameShallBeReferredUsingAQualifiedIdOrThisAuditQuery() { + this = ImportMisra23Package::nameShallBeReferredUsingAQualifiedIdOrThisAuditQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-8-1/ObjectAccessedAfterLifetimeMisra.ql b/cpp/misra/src/rules/RULE-6-8-1/ObjectAccessedAfterLifetimeMisra.ql new file mode 100644 index 0000000000..77483fdedb --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-8-1/ObjectAccessedAfterLifetimeMisra.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/object-accessed-after-lifetime-misra + * @name RULE-6-8-1: Access of object after lifetime (use-after-free) + * @description Accessing an object after its lifetime results in undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-6-8-1 + * correctness + * security + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.objectaccessedafterlifetime.ObjectAccessedAfterLifetime + +class ObjectAccessedAfterLifetimeMisraQuery extends ObjectAccessedAfterLifetimeSharedQuery { + ObjectAccessedAfterLifetimeMisraQuery() { + this = ImportMisra23Package::objectAccessedAfterLifetimeMisraQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-8-1/ObjectAccessedBeforeLifetimeMisra.ql b/cpp/misra/src/rules/RULE-6-8-1/ObjectAccessedBeforeLifetimeMisra.ql new file mode 100644 index 0000000000..e0e82f2396 --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-8-1/ObjectAccessedBeforeLifetimeMisra.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/object-accessed-before-lifetime-misra + * @name RULE-6-8-1: Access of uninitialized object + * @description Accessing an object before its lifetime can result in undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-6-8-1 + * correctness + * security + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.objectaccessedbeforelifetime.ObjectAccessedBeforeLifetime + +class ObjectAccessedBeforeLifetimeMisraQuery extends ObjectAccessedBeforeLifetimeSharedQuery { + ObjectAccessedBeforeLifetimeMisraQuery() { + this = ImportMisra23Package::objectAccessedBeforeLifetimeMisraQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-6-8-2/ReturnReferenceOrPointerToAutomaticLocalVariable.ql b/cpp/misra/src/rules/RULE-6-8-2/ReturnReferenceOrPointerToAutomaticLocalVariable.ql new file mode 100644 index 0000000000..bcf026cbba --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-8-2/ReturnReferenceOrPointerToAutomaticLocalVariable.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/return-reference-or-pointer-to-automatic-local-variable + * @name RULE-6-8-2: A function must not return a reference or a pointer to a local variable with automatic storage + * @description A function must not return a reference or a pointer to a local variable with + * automatic storage duration. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-8-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.returnreferenceorpointertoautomaticlocalvariable.ReturnReferenceOrPointerToAutomaticLocalVariable + +class ReturnReferenceOrPointerToAutomaticLocalVariableQuery extends ReturnReferenceOrPointerToAutomaticLocalVariableSharedQuery +{ + ReturnReferenceOrPointerToAutomaticLocalVariableQuery() { + this = ImportMisra23Package::returnReferenceOrPointerToAutomaticLocalVariableQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-7-11-1/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql b/cpp/misra/src/rules/RULE-7-11-1/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql new file mode 100644 index 0000000000..a0dfc63799 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-11-1/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/nullptr-not-the-only-form-of-the-null-pointer-constant + * @name RULE-7-11-1: nullptr shall be the only form of the null-pointer-constant + * @description nullptr shall be the only form of the null-pointer-constant. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-11-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.nullptrnottheonlyformofthenullpointerconstant.NullptrNotTheOnlyFormOfTheNullPointerConstant + +class NullptrNotTheOnlyFormOfTheNullPointerConstantQuery extends NullptrNotTheOnlyFormOfTheNullPointerConstantSharedQuery +{ + NullptrNotTheOnlyFormOfTheNullPointerConstantQuery() { + this = ImportMisra23Package::nullptrNotTheOnlyFormOfTheNullPointerConstantQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-7-11-2/ArrayPassedAsFunctionArgumentDecayToAPointer.ql b/cpp/misra/src/rules/RULE-7-11-2/ArrayPassedAsFunctionArgumentDecayToAPointer.ql new file mode 100644 index 0000000000..fed33c33de --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-11-2/ArrayPassedAsFunctionArgumentDecayToAPointer.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/array-passed-as-function-argument-decay-to-a-pointer + * @name RULE-7-11-2: An array passed as a function argument shall not decay to a pointer + * @description An array passed as a function argument shall not decay to a pointer. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-11-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.arraypassedasfunctionargumentdecaytoapointer.ArrayPassedAsFunctionArgumentDecayToAPointer + +class ArrayPassedAsFunctionArgumentDecayToAPointerQuery extends ArrayPassedAsFunctionArgumentDecayToAPointerSharedQuery +{ + ArrayPassedAsFunctionArgumentDecayToAPointerQuery() { + this = ImportMisra23Package::arrayPassedAsFunctionArgumentDecayToAPointerQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-18-2/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql b/cpp/misra/src/rules/RULE-8-18-2/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql new file mode 100644 index 0000000000..6c4b1a82ad --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-18-2/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/result-of-an-assignment-operator-should-not-be-used + * @name RULE-8-18-2: The result of an assignment operator should not be used + * @description The result of an assignment operator should not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-18-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.resultofanassignmentoperatorshouldnotbeused.ResultOfAnAssignmentOperatorShouldNotBeUsed + +class ResultOfAnAssignmentOperatorShouldNotBeUsedQuery extends ResultOfAnAssignmentOperatorShouldNotBeUsedSharedQuery +{ + ResultOfAnAssignmentOperatorShouldNotBeUsedQuery() { + this = ImportMisra23Package::resultOfAnAssignmentOperatorShouldNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-19-1/CommaOperatorShouldNotBeUsed.ql b/cpp/misra/src/rules/RULE-8-19-1/CommaOperatorShouldNotBeUsed.ql new file mode 100644 index 0000000000..df5be50dc0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-19-1/CommaOperatorShouldNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/comma-operator-should-not-be-used + * @name RULE-8-19-1: The comma operator should not be used + * @description The comma operator should not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-19-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.commaoperatorused.CommaOperatorUsed + +class CommaOperatorShouldNotBeUsedQuery extends CommaOperatorUsedSharedQuery { + CommaOperatorShouldNotBeUsedQuery() { + this = ImportMisra23Package::commaOperatorShouldNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-2-10/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql b/cpp/misra/src/rules/RULE-8-2-10/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql new file mode 100644 index 0000000000..ff0f397572 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-10/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/functions-call-themselves-either-directly-or-indirectly + * @name RULE-8-2-10: Functions shall not call themselves, either directly or indirectly + * @description Functions shall not call themselves, either directly or indirectly. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-10 + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.functionscallthemselveseitherdirectlyorindirectly.FunctionsCallThemselvesEitherDirectlyOrIndirectly + +class FunctionsCallThemselvesEitherDirectlyOrIndirectlyQuery extends FunctionsCallThemselvesEitherDirectlyOrIndirectlySharedQuery +{ + FunctionsCallThemselvesEitherDirectlyOrIndirectlyQuery() { + this = ImportMisra23Package::functionsCallThemselvesEitherDirectlyOrIndirectlyQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-2-3/CastRemovesConstOrVolatileFromPointerOrReference.ql b/cpp/misra/src/rules/RULE-8-2-3/CastRemovesConstOrVolatileFromPointerOrReference.ql new file mode 100644 index 0000000000..935050fdd9 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-3/CastRemovesConstOrVolatileFromPointerOrReference.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/cast-removes-const-or-volatile-from-pointer-or-reference + * @name RULE-8-2-3: A cast shall not remove any const or volatile qualification from the type accessed via a pointer or + * @description A cast shall not remove any const or volatile qualification from the type accessed + * via a pointer or by reference. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-3 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.removeconstorvolatilequalification.RemoveConstOrVolatileQualification + +class CastRemovesConstOrVolatileFromPointerOrReferenceQuery extends RemoveConstOrVolatileQualificationSharedQuery +{ + CastRemovesConstOrVolatileFromPointerOrReferenceQuery() { + this = ImportMisra23Package::castRemovesConstOrVolatileFromPointerOrReferenceQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-2-4/CastsBetweenAPointerToFunctionAndAnyOtherType.ql b/cpp/misra/src/rules/RULE-8-2-4/CastsBetweenAPointerToFunctionAndAnyOtherType.ql new file mode 100644 index 0000000000..37c258b722 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-4/CastsBetweenAPointerToFunctionAndAnyOtherType.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/casts-between-a-pointer-to-function-and-any-other-type + * @name RULE-8-2-4: Casts shall not be performed between a pointer to function and any other type + * @description Casts shall not be performed between a pointer to function and any other type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.castsbetweenapointertofunctionandanyothertype.CastsBetweenAPointerToFunctionAndAnyOtherType + +class CastsBetweenAPointerToFunctionAndAnyOtherTypeQuery extends CastsBetweenAPointerToFunctionAndAnyOtherTypeSharedQuery +{ + CastsBetweenAPointerToFunctionAndAnyOtherTypeQuery() { + this = ImportMisra23Package::castsBetweenAPointerToFunctionAndAnyOtherTypeQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-2-5/ReinterpretCastShallNotBeUsed.ql b/cpp/misra/src/rules/RULE-8-2-5/ReinterpretCastShallNotBeUsed.ql new file mode 100644 index 0000000000..685ebb7efd --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-5/ReinterpretCastShallNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/reinterpret-cast-shall-not-be-used + * @name RULE-8-2-5: reinterpret_cast shall not be used + * @description reinterpret_cast shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-5 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.reinterpretcastused.ReinterpretCastUsed + +class ReinterpretCastShallNotBeUsedQuery extends ReinterpretCastUsedSharedQuery { + ReinterpretCastShallNotBeUsedQuery() { + this = ImportMisra23Package::reinterpretCastShallNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-20-1/UnsignedOperationWithConstantOperandsWraps.ql b/cpp/misra/src/rules/RULE-8-20-1/UnsignedOperationWithConstantOperandsWraps.ql new file mode 100644 index 0000000000..e62acb8257 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-20-1/UnsignedOperationWithConstantOperandsWraps.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/unsigned-operation-with-constant-operands-wraps + * @name RULE-8-20-1: An unsigned arithmetic operation with constant operands should not wrap + * @description An unsigned arithmetic operation with constant operands should not wrap. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-20-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.unsignedoperationwithconstantoperandswraps.UnsignedOperationWithConstantOperandsWraps + +class UnsignedOperationWithConstantOperandsWrapsQuery extends UnsignedOperationWithConstantOperandsWrapsSharedQuery +{ + UnsignedOperationWithConstantOperandsWrapsQuery() { + this = ImportMisra23Package::unsignedOperationWithConstantOperandsWrapsQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-8-3-1/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql b/cpp/misra/src/rules/RULE-8-3-1/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql new file mode 100644 index 0000000000..c847348d1c --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-3-1/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/built-in-unary-operator-applied-to-unsigned-expression + * @name RULE-8-3-1: The built-in unary - operator should not be applied to an expression of unsigned type + * @description The built-in unary - operator should not be applied to an expression of unsigned + * type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-3-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.builtinunaryoperatorappliedtounsignedexpression.BuiltInUnaryOperatorAppliedToUnsignedExpression + +class BuiltInUnaryOperatorAppliedToUnsignedExpressionQuery extends BuiltInUnaryOperatorAppliedToUnsignedExpressionSharedQuery +{ + BuiltInUnaryOperatorAppliedToUnsignedExpressionQuery() { + this = ImportMisra23Package::builtInUnaryOperatorAppliedToUnsignedExpressionQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-9-3-1/LoopBodyCompoundCondition.ql b/cpp/misra/src/rules/RULE-9-3-1/LoopBodyCompoundCondition.ql new file mode 100644 index 0000000000..b87009d8c1 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-3-1/LoopBodyCompoundCondition.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/loop-body-compound-condition + * @name RULE-9-3-1: The statement forming the body of a loop shall be a compound statement + * @description If the body of a loop is not enclosed in braces, then this can lead to incorrect + * execution, and hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-9-3-1 + * maintainability + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.loopcompoundcondition.LoopCompoundCondition + +class LoopBodyCompoundConditionQuery extends LoopCompoundConditionSharedQuery { + LoopBodyCompoundConditionQuery() { this = ImportMisra23Package::loopBodyCompoundConditionQuery() } +} diff --git a/cpp/misra/src/rules/RULE-9-3-1/SwitchBodyCompoundCondition.ql b/cpp/misra/src/rules/RULE-9-3-1/SwitchBodyCompoundCondition.ql new file mode 100644 index 0000000000..7bee3c027e --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-3-1/SwitchBodyCompoundCondition.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/switch-body-compound-condition + * @name RULE-9-3-1: The statement forming the body of a switch shall be a compound statement + * @description If the body of a switch is not enclosed in braces, then this can lead to incorrect + * execution, and hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-9-3-1 + * maintainability + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.switchcompoundcondition.SwitchCompoundCondition + +class SwitchBodyCompoundConditionQuery extends SwitchCompoundConditionSharedQuery { + SwitchBodyCompoundConditionQuery() { + this = ImportMisra23Package::switchBodyCompoundConditionQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-9-4-1/IfElseIfEndCondition.ql b/cpp/misra/src/rules/RULE-9-4-1/IfElseIfEndCondition.ql new file mode 100644 index 0000000000..5ce6ab6487 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-4-1/IfElseIfEndCondition.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/if-else-if-end-condition + * @name RULE-9-4-1: All if ... else if constructs shall be terminated with an else statement + * @description All if ... else if constructs shall be terminated with an else statement. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-4-1 + * readability + * maintainability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct + +class IfElseIfEndConditionQuery extends IfElseTerminationConstructSharedQuery { + IfElseIfEndConditionQuery() { this = ImportMisra23Package::ifElseIfEndConditionQuery() } +} diff --git a/cpp/misra/src/rules/RULE-9-6-1/GotoStatementShouldNotBeUsed.ql b/cpp/misra/src/rules/RULE-9-6-1/GotoStatementShouldNotBeUsed.ql new file mode 100644 index 0000000000..1751aa3c37 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-6-1/GotoStatementShouldNotBeUsed.ql @@ -0,0 +1,22 @@ +/** + * @id cpp/misra/goto-statement-should-not-be-used + * @name RULE-9-6-1: The goto statement should not be used + * @description The goto statement should not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-6-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.gotostatementshouldnotbeused.GotoStatementShouldNotBeUsed + +class GotoStatementShouldNotBeUsedQuery extends GotoStatementShouldNotBeUsedSharedQuery { + GotoStatementShouldNotBeUsedQuery() { + this = ImportMisra23Package::gotoStatementShouldNotBeUsedQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-9-6-2/GotoReferenceALabelInSurroundingBlock.ql b/cpp/misra/src/rules/RULE-9-6-2/GotoReferenceALabelInSurroundingBlock.ql new file mode 100644 index 0000000000..6e11a73f7f --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-6-2/GotoReferenceALabelInSurroundingBlock.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/goto-reference-a-label-in-surrounding-block + * @name RULE-9-6-2: A goto statement shall reference a label in a surrounding block + * @description A goto statement shall reference a label in a surrounding block. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-6-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.gotoreferencealabelinsurroundingblock.GotoReferenceALabelInSurroundingBlock + +class GotoReferenceALabelInSurroundingBlockQuery extends GotoReferenceALabelInSurroundingBlockSharedQuery +{ + GotoReferenceALabelInSurroundingBlockQuery() { + this = ImportMisra23Package::gotoReferenceALabelInSurroundingBlockQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-9-6-3/GotoShallJumpToLabelDeclaredLaterInTheFunction.ql b/cpp/misra/src/rules/RULE-9-6-3/GotoShallJumpToLabelDeclaredLaterInTheFunction.ql new file mode 100644 index 0000000000..5ce80af9e7 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-6-3/GotoShallJumpToLabelDeclaredLaterInTheFunction.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/goto-shall-jump-to-label-declared-later-in-the-function + * @name RULE-9-6-3: The goto statement shall jump to a label declared later in the function body + * @description Jumping back to an earlier section in the code can lead to accidental iterations. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-6-3 + * maintainability + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition + +class GotoShallJumpToLabelDeclaredLaterInTheFunctionQuery extends GotoStatementConditionSharedQuery { + GotoShallJumpToLabelDeclaredLaterInTheFunctionQuery() { + this = ImportMisra23Package::gotoShallJumpToLabelDeclaredLaterInTheFunctionQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-9-6-4/FunctionDeclaredWithTheNoreturnAttributeReturn.ql b/cpp/misra/src/rules/RULE-9-6-4/FunctionDeclaredWithTheNoreturnAttributeReturn.ql new file mode 100644 index 0000000000..f0ac8dc9bf --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-6-4/FunctionDeclaredWithTheNoreturnAttributeReturn.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/function-declared-with-the-noreturn-attribute-return + * @name RULE-9-6-4: A function declared with the [[noreturn]] attribute shall not return + * @description A function with the [[noreturn]] attribute that returns leads to undefined + * behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-6-4 + * correctness + * scope/system + * external/misra/enforcement/undecidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.functionnoreturnattributecondition.FunctionNoReturnAttributeCondition + +class FunctionDeclaredWithTheNoreturnAttributeReturnQuery extends FunctionNoReturnAttributeConditionSharedQuery +{ + FunctionDeclaredWithTheNoreturnAttributeReturnQuery() { + this = ImportMisra23Package::functionDeclaredWithTheNoreturnAttributeReturnQuery() + } +} diff --git a/cpp/misra/src/rules/RULE-9-6-5/NonVoidFunctionShallReturnAValueOnAllPaths.ql b/cpp/misra/src/rules/RULE-9-6-5/NonVoidFunctionShallReturnAValueOnAllPaths.ql new file mode 100644 index 0000000000..444356350a --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-6-5/NonVoidFunctionShallReturnAValueOnAllPaths.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/non-void-function-shall-return-a-value-on-all-paths + * @name RULE-9-6-5: A function with non-void return type shall return a value on all paths + * @description A function with non-void return type that does not exit via a return statement can + * result in undefined behaviour. An exception to this rule is exiting via exception + * handling. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-6-5 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.nonvoidfunctiondoesnotreturn.NonVoidFunctionDoesNotReturn + +class NonVoidFunctionShallReturnAValueOnAllPathsQuery extends NonVoidFunctionDoesNotReturnSharedQuery +{ + NonVoidFunctionShallReturnAValueOnAllPathsQuery() { + this = ImportMisra23Package::nonVoidFunctionShallReturnAValueOnAllPathsQuery() + } +} diff --git a/cpp/misra/test/codeql-pack.lock.yml b/cpp/misra/test/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/misra/test/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/cpp/misra/test/options b/cpp/misra/test/options new file mode 100644 index 0000000000..59fc70d386 --- /dev/null +++ b/cpp/misra/test/options @@ -0,0 +1 @@ +semmle-extractor-options:--clang -std=c++17 -nostdinc++ -I../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/misra/test/qlpack.yml b/cpp/misra/test/qlpack.yml index efcf229ecd..fb0cc1201c 100644 --- a/cpp/misra/test/qlpack.yml +++ b/cpp/misra/test/qlpack.yml @@ -1,4 +1,6 @@ -name: misra-cpp-coding-standards-tests -version: 2.9.0 -libraryPathDependencies: misra-cpp-coding-standards +name: codeql/misra-cpp-coding-standards-tests +version: 2.49.0-dev extractor: cpp +license: MIT +dependencies: + codeql/misra-cpp-coding-standards: '*' \ No newline at end of file diff --git a/cpp/misra/test/rules/DIR-0-3-1/PossibleMisuseOfInfiniteFloatingPointValue.testref b/cpp/misra/test/rules/DIR-0-3-1/PossibleMisuseOfInfiniteFloatingPointValue.testref new file mode 100644 index 0000000000..952d461d00 --- /dev/null +++ b/cpp/misra/test/rules/DIR-0-3-1/PossibleMisuseOfInfiniteFloatingPointValue.testref @@ -0,0 +1 @@ +cpp/common/test/rules/misuseofinfinitefloatingpointvalue/MisuseOfInfiniteFloatingPointValue.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/DIR-0-3-1/PossibleMisuseOfNaNFloatingPointValue.testref b/cpp/misra/test/rules/DIR-0-3-1/PossibleMisuseOfNaNFloatingPointValue.testref new file mode 100644 index 0000000000..2cd2de067d --- /dev/null +++ b/cpp/misra/test/rules/DIR-0-3-1/PossibleMisuseOfNaNFloatingPointValue.testref @@ -0,0 +1 @@ +cpp/common/test/rules/misuseofnanfloatingpointvalue/MisuseOfNaNFloatingPointValue.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/DIR-15-8-1/CopyAndMoveAssignmentsShallHandleSelfAssignment.testref b/cpp/misra/test/rules/DIR-15-8-1/CopyAndMoveAssignmentsShallHandleSelfAssignment.testref new file mode 100644 index 0000000000..65fc614121 --- /dev/null +++ b/cpp/misra/test/rules/DIR-15-8-1/CopyAndMoveAssignmentsShallHandleSelfAssignment.testref @@ -0,0 +1 @@ +cpp/common/test/rules/copyandmoveassignmentsshallhandleselfassignment/CopyAndMoveAssignmentsShallHandleSelfAssignment.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-4-4/SectionsOfCodeShallNotBeCommentedOut.testref b/cpp/misra/test/rules/DIR-5-7-2/SectionsOfCodeShouldNotBeCommentedOut.testref similarity index 100% rename from c/misra/test/rules/RULE-4-4/SectionsOfCodeShallNotBeCommentedOut.testref rename to cpp/misra/test/rules/DIR-5-7-2/SectionsOfCodeShouldNotBeCommentedOut.testref diff --git a/cpp/misra/test/rules/RULE-10-0-1/UseSingleGlobalOrMemberDeclarators.testref b/cpp/misra/test/rules/RULE-10-0-1/UseSingleGlobalOrMemberDeclarators.testref new file mode 100644 index 0000000000..434cb47456 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-0-1/UseSingleGlobalOrMemberDeclarators.testref @@ -0,0 +1 @@ +cpp/common/test/rules/multipleglobalormemberdeclarators/MultipleGlobalOrMemberDeclarators.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-0-1/UseSingleLocalDeclarators.testref b/cpp/misra/test/rules/RULE-10-0-1/UseSingleLocalDeclarators.testref new file mode 100644 index 0000000000..be7c9ac352 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-0-1/UseSingleLocalDeclarators.testref @@ -0,0 +1 @@ +cpp/common/test/rules/multiplelocaldeclarators/MultipleLocalDeclarators.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-2-1/EnumerationNotDefinedWithAnExplicitUnderlyingType.testref b/cpp/misra/test/rules/RULE-10-2-1/EnumerationNotDefinedWithAnExplicitUnderlyingType.testref new file mode 100644 index 0000000000..d7a73fd488 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-1/EnumerationNotDefinedWithAnExplicitUnderlyingType.testref @@ -0,0 +1 @@ +cpp/common/test/rules/enumerationnotdefinedwithanexplicitunderlyingtype/EnumerationNotDefinedWithAnExplicitUnderlyingType.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-4-1/AsmDeclarationShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-10-4-1/AsmDeclarationShallNotBeUsed.testref new file mode 100644 index 0000000000..f643f6a9c7 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-4-1/AsmDeclarationShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/asmdeclarationused/AsmDeclarationUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-11-3-2/DeclarationOfAnObjectIndirectionsLevel.testref b/cpp/misra/test/rules/RULE-11-3-2/DeclarationOfAnObjectIndirectionsLevel.testref new file mode 100644 index 0000000000..3b46dca736 --- /dev/null +++ b/cpp/misra/test/rules/RULE-11-3-2/DeclarationOfAnObjectIndirectionsLevel.testref @@ -0,0 +1 @@ +cpp/common/test/rules/donotusemorethantwolevelsofpointerindirection/DoNotUseMoreThanTwoLevelsOfPointerIndirection.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-11-6-3/NonUniqueEnumerationConstant.testref b/cpp/misra/test/rules/RULE-11-6-3/NonUniqueEnumerationConstant.testref new file mode 100644 index 0000000000..6606e891ab --- /dev/null +++ b/cpp/misra/test/rules/RULE-11-6-3/NonUniqueEnumerationConstant.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonuniqueenumerationconstant/NonUniqueEnumerationConstant.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-12-2-2/BitFieldShallHaveAnAppropriateType.testref b/cpp/misra/test/rules/RULE-12-2-2/BitFieldShallHaveAnAppropriateType.testref new file mode 100644 index 0000000000..9e4a9a69c7 --- /dev/null +++ b/cpp/misra/test/rules/RULE-12-2-2/BitFieldShallHaveAnAppropriateType.testref @@ -0,0 +1 @@ +cpp/common/test/rules/bitfieldshallhaveanappropriatetype/BitFieldShallHaveAnAppropriateType.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-12-2-3/SignedIntegerNamedBitFieldHaveALengthOfOneBit.testref b/cpp/misra/test/rules/RULE-12-2-3/SignedIntegerNamedBitFieldHaveALengthOfOneBit.testref new file mode 100644 index 0000000000..5dd7991a37 --- /dev/null +++ b/cpp/misra/test/rules/RULE-12-2-3/SignedIntegerNamedBitFieldHaveALengthOfOneBit.testref @@ -0,0 +1 @@ +cpp/common/test/rules/namedbitfieldswithsignedintegertype/NamedBitFieldsWithSignedIntegerType.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-13-1-2/VirtualAndNonVirtualClassInTheHierarchy.testref b/cpp/misra/test/rules/RULE-13-1-2/VirtualAndNonVirtualClassInTheHierarchy.testref new file mode 100644 index 0000000000..fe57c50fe3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-1-2/VirtualAndNonVirtualClassInTheHierarchy.testref @@ -0,0 +1 @@ +cpp/common/test/rules/virtualandnonvirtualclassinthehierarchy/VirtualAndNonVirtualClassInTheHierarchy.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-13-3-2/OverridingShallSpecifyDifferentDefaultArguments.testref b/cpp/misra/test/rules/RULE-13-3-2/OverridingShallSpecifyDifferentDefaultArguments.testref new file mode 100644 index 0000000000..7e06403515 --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-3-2/OverridingShallSpecifyDifferentDefaultArguments.testref @@ -0,0 +1 @@ +cpp/common/test/rules/overridingshallspecifydifferentdefaultarguments/OverridingShallSpecifyDifferentDefaultArguments.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-13-3-4/PotentiallyVirtualPointerOnlyComparesToNullptr.testref b/cpp/misra/test/rules/RULE-13-3-4/PotentiallyVirtualPointerOnlyComparesToNullptr.testref new file mode 100644 index 0000000000..ca8eab9681 --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-3-4/PotentiallyVirtualPointerOnlyComparesToNullptr.testref @@ -0,0 +1 @@ +cpp/common/test/rules/potentiallyvirtualpointeronlycomparestonullptr/PotentiallyVirtualPointerOnlyComparesToNullptr.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-1-1/ObjectsDynamicTypeUsedFromConstructorOrDestructor.testref b/cpp/misra/test/rules/RULE-15-1-1/ObjectsDynamicTypeUsedFromConstructorOrDestructor.testref new file mode 100644 index 0000000000..596f74b010 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-1-1/ObjectsDynamicTypeUsedFromConstructorOrDestructor.testref @@ -0,0 +1 @@ +cpp/common/test/rules/objectsdynamictypeusedfromconstructorordestructor/ObjectsDynamicTypeUsedFromConstructorOrDestructor.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-1-2/InitializeAllVirtualBaseClasses.testref b/cpp/misra/test/rules/RULE-15-1-2/InitializeAllVirtualBaseClasses.testref new file mode 100644 index 0000000000..ac8c5e1a83 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-1-2/InitializeAllVirtualBaseClasses.testref @@ -0,0 +1 @@ +cpp/common/test/rules/initializeallvirtualbaseclasses/InitializeAllVirtualBaseClasses.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-1-5/InitializerListConstructorIsTheOnlyConstructor.testref b/cpp/misra/test/rules/RULE-15-1-5/InitializerListConstructorIsTheOnlyConstructor.testref new file mode 100644 index 0000000000..49b73d06a9 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-1-5/InitializerListConstructorIsTheOnlyConstructor.testref @@ -0,0 +1 @@ +cpp/common/test/rules/initializerlistconstructoristheonlyconstructor/InitializerListConstructorIsTheOnlyConstructor.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-16-5-2/AddressOfOperatorOverloaded.testref b/cpp/misra/test/rules/RULE-16-5-2/AddressOfOperatorOverloaded.testref new file mode 100644 index 0000000000..1f2a126671 --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-5-2/AddressOfOperatorOverloaded.testref @@ -0,0 +1 @@ +cpp/common/test/rules/addressofoperatoroverloaded/AddressOfOperatorOverloaded.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-17-8-1/FunctionTemplatesExplicitlySpecialized.testref b/cpp/misra/test/rules/RULE-17-8-1/FunctionTemplatesExplicitlySpecialized.testref new file mode 100644 index 0000000000..6a284e2cbb --- /dev/null +++ b/cpp/misra/test/rules/RULE-17-8-1/FunctionTemplatesExplicitlySpecialized.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functiontemplatesexplicitlyspecialized/FunctionTemplatesExplicitlySpecialized.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-18-1-1/ExceptionObjectHavePointerType.testref b/cpp/misra/test/rules/RULE-18-1-1/ExceptionObjectHavePointerType.testref new file mode 100644 index 0000000000..24d4229225 --- /dev/null +++ b/cpp/misra/test/rules/RULE-18-1-1/ExceptionObjectHavePointerType.testref @@ -0,0 +1 @@ +cpp/common/test/rules/exceptionobjecthavepointertype/ExceptionObjectHavePointerType.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-18-1-2/EmptyThrowOnlyWithinACatchHandler.testref b/cpp/misra/test/rules/RULE-18-1-2/EmptyThrowOnlyWithinACatchHandler.testref new file mode 100644 index 0000000000..f3c961d8f1 --- /dev/null +++ b/cpp/misra/test/rules/RULE-18-1-2/EmptyThrowOnlyWithinACatchHandler.testref @@ -0,0 +1 @@ +cpp/common/test/rules/emptythrowonlywithinacatchhandler/EmptyThrowOnlyWithinACatchHandler.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-18-3-3/HandlersReferToNonStaticMembersFromTheirClass.testref b/cpp/misra/test/rules/RULE-18-3-3/HandlersReferToNonStaticMembersFromTheirClass.testref new file mode 100644 index 0000000000..7d4f5826b0 --- /dev/null +++ b/cpp/misra/test/rules/RULE-18-3-3/HandlersReferToNonStaticMembersFromTheirClass.testref @@ -0,0 +1 @@ +cpp/common/test/rules/destroyedvaluereferencedindestructorcatchblock/DestroyedValueReferencedInDestructorCatchBlock.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-18-5-1/NoexceptFunctionShouldNotPropagateToTheCaller.testref b/cpp/misra/test/rules/RULE-18-5-1/NoexceptFunctionShouldNotPropagateToTheCaller.testref new file mode 100644 index 0000000000..76dc55827f --- /dev/null +++ b/cpp/misra/test/rules/RULE-18-5-1/NoexceptFunctionShouldNotPropagateToTheCaller.testref @@ -0,0 +1 @@ +cpp/common/test/rules/noexceptfunctionshouldnotpropagatetothecaller/NoexceptFunctionShouldNotPropagateToTheCaller.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-0-2/FunctionLikeMacrosDefined.testref b/cpp/misra/test/rules/RULE-19-0-2/FunctionLikeMacrosDefined.testref new file mode 100644 index 0000000000..1f07b047a6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-0-2/FunctionLikeMacrosDefined.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functionlikemacrosdefined/FunctionLikeMacrosDefined.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-0-3/IncludeDirectivesPrecededByPreprocessorDirectives.testref b/cpp/misra/test/rules/RULE-19-0-3/IncludeDirectivesPrecededByPreprocessorDirectives.testref new file mode 100644 index 0000000000..7992898cfc --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-0-3/IncludeDirectivesPrecededByPreprocessorDirectives.testref @@ -0,0 +1 @@ +cpp/common/test/rules/preprocessorincludespreceded/PreprocessorIncludesPreceded.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-1-3/IdentifiersUsedInTheControllingExpressionOf.testref b/cpp/misra/test/rules/RULE-19-1-3/IdentifiersUsedInTheControllingExpressionOf.testref new file mode 100644 index 0000000000..73eb246867 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-1-3/IdentifiersUsedInTheControllingExpressionOf.testref @@ -0,0 +1 @@ +cpp/common/test/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-2-3/CharsThatShouldNotOccurInHeaderFileName.testref b/cpp/misra/test/rules/RULE-19-2-3/CharsThatShouldNotOccurInHeaderFileName.testref new file mode 100644 index 0000000000..6be2f4f7ba --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-2-3/CharsThatShouldNotOccurInHeaderFileName.testref @@ -0,0 +1 @@ +cpp/common/test/rules/preprocessorincludesforbiddenheadernames/PreprocessorIncludesForbiddenHeaderNames.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-3-1/AndPreprocessorOperatorsShouldNotBeUsed.testref b/cpp/misra/test/rules/RULE-19-3-1/AndPreprocessorOperatorsShouldNotBeUsed.testref new file mode 100644 index 0000000000..eec0b94b11 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-3-1/AndPreprocessorOperatorsShouldNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/hashoperatorsused/HashOperatorsUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-3-2/MacroParameterFollowingHash.testref b/cpp/misra/test/rules/RULE-19-3-2/MacroParameterFollowingHash.testref new file mode 100644 index 0000000000..a5eb010410 --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-3-2/MacroParameterFollowingHash.testref @@ -0,0 +1 @@ +cpp/common/test/rules/macroparameterfollowinghash/MacroParameterFollowingHash.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-3-3/AMixedUseMacroArgumentSubjectToExpansion.testref b/cpp/misra/test/rules/RULE-19-3-3/AMixedUseMacroArgumentSubjectToExpansion.testref new file mode 100644 index 0000000000..8061bfd2ec --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-3-3/AMixedUseMacroArgumentSubjectToExpansion.testref @@ -0,0 +1 @@ +cpp/common/test/rules/amixedusemacroargumentsubjecttoexpansion/AMixedUseMacroArgumentSubjectToExpansion.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-19-3-5/TokensThatLookLikeDirectivesInAMacroArgument.testref b/cpp/misra/test/rules/RULE-19-3-5/TokensThatLookLikeDirectivesInAMacroArgument.testref new file mode 100644 index 0000000000..1e15c636ee --- /dev/null +++ b/cpp/misra/test/rules/RULE-19-3-5/TokensThatLookLikeDirectivesInAMacroArgument.testref @@ -0,0 +1 @@ +cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-10-3/CsignalFacilitiesUsed.testref b/cpp/misra/test/rules/RULE-21-10-3/CsignalFacilitiesUsed.testref new file mode 100644 index 0000000000..2342517408 --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-10-3/CsignalFacilitiesUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/csignalfacilitiesused/CsignalFacilitiesUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-10-3/CsignalTypesShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-21-10-3/CsignalTypesShallNotBeUsed.testref new file mode 100644 index 0000000000..3d398d799b --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-10-3/CsignalTypesShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-10-3/CsignalTypesUsed.testref b/cpp/misra/test/rules/RULE-21-10-3/CsignalTypesUsed.testref new file mode 100644 index 0000000000..3d398d799b --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-10-3/CsignalTypesUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/csignaltypesused/CsignalTypesUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-2-1/AtofAtoiAtolAndAtollUsed.testref b/cpp/misra/test/rules/RULE-21-2-1/AtofAtoiAtolAndAtollUsed.testref new file mode 100644 index 0000000000..1b12920284 --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-2-1/AtofAtoiAtolAndAtollUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/atofatoiatolandatollused/AtofAtoiAtolAndAtollUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-2-4/MacroOffsetofShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-21-2-4/MacroOffsetofShallNotBeUsed.testref new file mode 100644 index 0000000000..022fef6071 --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-2-4/MacroOffsetofShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/macrooffsetofused/MacroOffsetofUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-6-4/GlobalSizedOperatorDeleteShallBeDefined.testref b/cpp/misra/test/rules/RULE-21-6-4/GlobalSizedOperatorDeleteShallBeDefined.testref new file mode 100644 index 0000000000..4d1e21d4cb --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-4/GlobalSizedOperatorDeleteShallBeDefined.testref @@ -0,0 +1 @@ +cpp/common/test/rules/globalsizedoperatordeletenotdefined/GlobalSizedOperatorDeleteNotDefined.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-6-4/GlobalUnsizedOperatorDeleteShallBeDefined.testref b/cpp/misra/test/rules/RULE-21-6-4/GlobalUnsizedOperatorDeleteShallBeDefined.testref new file mode 100644 index 0000000000..f2fcc2eded --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-4/GlobalUnsizedOperatorDeleteShallBeDefined.testref @@ -0,0 +1 @@ +cpp/common/test/rules/globalunsizedoperatordeletenotdefined/GlobalUnsizedOperatorDeleteNotDefined.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-6-5/PointerToAnIncompleteClassTypeDeleted.testref b/cpp/misra/test/rules/RULE-21-6-5/PointerToAnIncompleteClassTypeDeleted.testref new file mode 100644 index 0000000000..3f4895b1c4 --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-5/PointerToAnIncompleteClassTypeDeleted.testref @@ -0,0 +1 @@ +cpp/common/test/rules/deleteofpointertoincompleteclass/DeleteOfPointerToIncompleteClass.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-25-5-2/PointersReturnedByLocaleFunctionsMustBeUsedAsConst.testref b/cpp/misra/test/rules/RULE-25-5-2/PointersReturnedByLocaleFunctionsMustBeUsedAsConst.testref new file mode 100644 index 0000000000..febf2e9d50 --- /dev/null +++ b/cpp/misra/test/rules/RULE-25-5-2/PointersReturnedByLocaleFunctionsMustBeUsedAsConst.testref @@ -0,0 +1 @@ +cpp/common/test/rules/constlikereturnvalue/ConstLikeReturnValue.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersMisra.testref b/cpp/misra/test/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersMisra.testref new file mode 100644 index 0000000000..74cb92bd88 --- /dev/null +++ b/cpp/misra/test/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersMisra.testref @@ -0,0 +1 @@ +cpp/common/test/rules/invalidatedenvstringpointers/InvalidatedEnvStringPointers.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersWarnMisra.testref b/cpp/misra/test/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersWarnMisra.testref new file mode 100644 index 0000000000..1628a12aa9 --- /dev/null +++ b/cpp/misra/test/rules/RULE-25-5-3/CallToSetlocaleInvalidatesOldPointersWarnMisra.testref @@ -0,0 +1 @@ +cpp/common/test/rules/invalidatedenvstringpointerswarn/InvalidatedEnvStringPointersWarn.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-26-3-1/VectorShouldNotBeSpecializedWithBool.testref b/cpp/misra/test/rules/RULE-26-3-1/VectorShouldNotBeSpecializedWithBool.testref new file mode 100644 index 0000000000..a934690acb --- /dev/null +++ b/cpp/misra/test/rules/RULE-26-3-1/VectorShouldNotBeSpecializedWithBool.testref @@ -0,0 +1 @@ +cpp/common/test/rules/vectorshouldnotbespecializedwithbool/VectorShouldNotBeSpecializedWithBool.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-28-6-2/ForwardingReferencesAndForwardNotUsedTogether.testref b/cpp/misra/test/rules/RULE-28-6-2/ForwardingReferencesAndForwardNotUsedTogether.testref new file mode 100644 index 0000000000..d56acb8415 --- /dev/null +++ b/cpp/misra/test/rules/RULE-28-6-2/ForwardingReferencesAndForwardNotUsedTogether.testref @@ -0,0 +1 @@ +cpp/common/test/rules/forwardingreferencesandforwardnotusedtogether/ForwardingReferencesAndForwardNotUsedTogether.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-28-6-3/ObjectUsedWhileInPotentiallyMovedFromState.testref b/cpp/misra/test/rules/RULE-28-6-3/ObjectUsedWhileInPotentiallyMovedFromState.testref new file mode 100644 index 0000000000..5ae8b65a71 --- /dev/null +++ b/cpp/misra/test/rules/RULE-28-6-3/ObjectUsedWhileInPotentiallyMovedFromState.testref @@ -0,0 +1 @@ +cpp/common/test/rules/movedfromobjectsunspecifiedstate/MovedFromObjectsUnspecifiedState.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-30-0-1/CstdioFunctionsShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-30-0-1/CstdioFunctionsShallNotBeUsed.testref new file mode 100644 index 0000000000..5f8b3d8a9a --- /dev/null +++ b/cpp/misra/test/rules/RULE-30-0-1/CstdioFunctionsShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/cstdiofunctionsused/CstdioFunctionsUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-30-0-1/CstdioMacrosShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-30-0-1/CstdioMacrosShallNotBeUsed.testref new file mode 100644 index 0000000000..a1ba376c3b --- /dev/null +++ b/cpp/misra/test/rules/RULE-30-0-1/CstdioMacrosShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/cstdiomacrosused/CstdioMacrosUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-30-0-1/CstdioTypesShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-30-0-1/CstdioTypesShallNotBeUsed.testref new file mode 100644 index 0000000000..4c08a75cfe --- /dev/null +++ b/cpp/misra/test/rules/RULE-30-0-1/CstdioTypesShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/cstdiotypesused/CstdioTypesUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-30-0-2/ReadsAndWritesOnStreamNotSeparatedByPositioning.testref b/cpp/misra/test/rules/RULE-30-0-2/ReadsAndWritesOnStreamNotSeparatedByPositioning.testref new file mode 100644 index 0000000000..0a8adf7272 --- /dev/null +++ b/cpp/misra/test/rules/RULE-30-0-2/ReadsAndWritesOnStreamNotSeparatedByPositioning.testref @@ -0,0 +1 @@ +cpp/common/test/rules/iofstreammissingpositioning/IOFstreamMissingPositioning.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-1/BackslashCharacterMisuse.testref b/cpp/misra/test/rules/RULE-5-13-1/BackslashCharacterMisuse.testref new file mode 100644 index 0000000000..924122e38e --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-1/BackslashCharacterMisuse.testref @@ -0,0 +1 @@ +cpp/common/test/rules/backslashcharactermisuse/BackslashCharacterMisuse.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-2/NonTerminatedEscapeSequences.testref b/cpp/misra/test/rules/RULE-5-13-2/NonTerminatedEscapeSequences.testref new file mode 100644 index 0000000000..bfed44b1fd --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-2/NonTerminatedEscapeSequences.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonterminatedescapesequences/NonTerminatedEscapeSequences.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-3/OctalConstantsUsed.testref b/cpp/misra/test/rules/RULE-5-13-3/OctalConstantsUsed.testref new file mode 100644 index 0000000000..97c466a866 --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-3/OctalConstantsUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/useofnonzerooctalliteral/UseOfNonZeroOctalLiteral.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-4/UnsignedIntegerLiteralsNotAppropriatelySuffixed.testref b/cpp/misra/test/rules/RULE-5-13-4/UnsignedIntegerLiteralsNotAppropriatelySuffixed.testref new file mode 100644 index 0000000000..9133a84ce4 --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-4/UnsignedIntegerLiteralsNotAppropriatelySuffixed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unsignedintegerliteralsnotappropriatelysuffixed/UnsignedIntegerLiteralsNotAppropriatelySuffixed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-13-5/LowercaseLStartsInLiteralSuffix.testref b/cpp/misra/test/rules/RULE-5-13-5/LowercaseLStartsInLiteralSuffix.testref new file mode 100644 index 0000000000..760d407a2d --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-13-5/LowercaseLStartsInLiteralSuffix.testref @@ -0,0 +1 @@ +cpp/common/test/rules/lowercaselstartsinliteralsuffix/LowercaseLStartsInLiteralSuffix.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-7-1/CharacterSequenceUsedWithinACStyleComment.testref b/cpp/misra/test/rules/RULE-5-7-1/CharacterSequenceUsedWithinACStyleComment.testref new file mode 100644 index 0000000000..971b1953f7 --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-7-1/CharacterSequenceUsedWithinACStyleComment.testref @@ -0,0 +1 @@ +cpp/common/test/rules/charactersequenceusedwithinacstylecomment/CharacterSequenceUsedWithinACStyleComment.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-5-7-3/LineSplicingUsedInComments.testref b/cpp/misra/test/rules/RULE-5-7-3/LineSplicingUsedInComments.testref new file mode 100644 index 0000000000..7874a476a0 --- /dev/null +++ b/cpp/misra/test/rules/RULE-5-7-3/LineSplicingUsedInComments.testref @@ -0,0 +1 @@ +cpp/common/test/rules/linesplicingusedincomments/LineSplicingUsedInComments.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-0-3/GlobalNamespaceDeclarations.testref b/cpp/misra/test/rules/RULE-6-0-3/GlobalNamespaceDeclarations.testref new file mode 100644 index 0000000000..8f71738005 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-0-3/GlobalNamespaceDeclarations.testref @@ -0,0 +1 @@ +cpp/common/test/rules/globalnamespacedeclarations/GlobalNamespaceDeclarations.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-0-4/NonGlobalFunctionMain.testref b/cpp/misra/test/rules/RULE-6-0-4/NonGlobalFunctionMain.testref new file mode 100644 index 0000000000..e149f3a33b --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-0-4/NonGlobalFunctionMain.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonglobalfunctionmain/NonGlobalFunctionMain.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-2-1/OneDefinitionRuleViolated.testref b/cpp/misra/test/rules/RULE-6-2-1/OneDefinitionRuleViolated.testref new file mode 100644 index 0000000000..b51950abaa --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-2-1/OneDefinitionRuleViolated.testref @@ -0,0 +1 @@ +cpp/common/test/rules/onedefinitionruleviolation/OneDefinitionRuleViolation.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-4-1/VariableDeclaredInInnerScopeHidesOuterScope.testref b/cpp/misra/test/rules/RULE-6-4-1/VariableDeclaredInInnerScopeHidesOuterScope.testref new file mode 100644 index 0000000000..2f41afee3b --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-4-1/VariableDeclaredInInnerScopeHidesOuterScope.testref @@ -0,0 +1 @@ +cpp/common/test/rules/identifierhidden/IdentifierHidden.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-4-2/DefinitionShallBeConsideredForUnqualifiedLookup.testref b/cpp/misra/test/rules/RULE-6-4-2/DefinitionShallBeConsideredForUnqualifiedLookup.testref new file mode 100644 index 0000000000..7a5ae74d2e --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-4-2/DefinitionShallBeConsideredForUnqualifiedLookup.testref @@ -0,0 +1 @@ +cpp/common/test/rules/definitionnotconsideredforunqualifiedlookup/DefinitionNotConsideredForUnqualifiedLookup.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-4-2/InheritedNonOverridableMemberFunction.testref b/cpp/misra/test/rules/RULE-6-4-2/InheritedNonOverridableMemberFunction.testref new file mode 100644 index 0000000000..2fb9608ee8 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-4-2/InheritedNonOverridableMemberFunction.testref @@ -0,0 +1 @@ +cpp/common/test/rules/hiddeninheritednonoverridablememberfunction/HiddenInheritedNonOverridableMemberFunction.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-4-2/InheritedOverridableMemberFunction.testref b/cpp/misra/test/rules/RULE-6-4-2/InheritedOverridableMemberFunction.testref new file mode 100644 index 0000000000..e768ced8d3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-4-2/InheritedOverridableMemberFunction.testref @@ -0,0 +1 @@ +cpp/common/test/rules/hiddeninheritedoverridablememberfunction/HiddenInheritedOverridableMemberFunction.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThis.testref b/cpp/misra/test/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThis.testref new file mode 100644 index 0000000000..ad5590bc1f --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThis.testref @@ -0,0 +1 @@ +cpp/common/test/rules/namenotreferredusingaqualifiedidorthis/NameNotReferredUsingAQualifiedIdOrThis.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThisAudit.testref b/cpp/misra/test/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThisAudit.testref new file mode 100644 index 0000000000..f7ff9100a6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-4-3/NameShallBeReferredUsingAQualifiedIdOrThisAudit.testref @@ -0,0 +1 @@ +cpp/common/test/rules/namenotreferredusingaqualifiedidorthisaudit/NameNotReferredUsingAQualifiedIdOrThisAudit.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-8-1/ObjectAccessedAfterLifetimeMisra.testref b/cpp/misra/test/rules/RULE-6-8-1/ObjectAccessedAfterLifetimeMisra.testref new file mode 100644 index 0000000000..979e12ac8c --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-8-1/ObjectAccessedAfterLifetimeMisra.testref @@ -0,0 +1 @@ +cpp/common/test/rules/objectaccessedafterlifetime/ObjectAccessedAfterLifetime.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-8-1/ObjectAccessedBeforeLifetimeMisra.testref b/cpp/misra/test/rules/RULE-6-8-1/ObjectAccessedBeforeLifetimeMisra.testref new file mode 100644 index 0000000000..3f22c45632 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-8-1/ObjectAccessedBeforeLifetimeMisra.testref @@ -0,0 +1 @@ +cpp/common/test/rules/objectaccessedbeforelifetime/ObjectAccessedBeforeLifetime.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-8-2/ReturnReferenceOrPointerToAutomaticLocalVariable.testref b/cpp/misra/test/rules/RULE-6-8-2/ReturnReferenceOrPointerToAutomaticLocalVariable.testref new file mode 100644 index 0000000000..45dbffde00 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-8-2/ReturnReferenceOrPointerToAutomaticLocalVariable.testref @@ -0,0 +1 @@ +cpp/common/test/rules/returnreferenceorpointertoautomaticlocalvariable/ReturnReferenceOrPointerToAutomaticLocalVariable.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-11-1/NullptrNotTheOnlyFormOfTheNullPointerConstant.testref b/cpp/misra/test/rules/RULE-7-11-1/NullptrNotTheOnlyFormOfTheNullPointerConstant.testref new file mode 100644 index 0000000000..aeb655a341 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-1/NullptrNotTheOnlyFormOfTheNullPointerConstant.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nullptrnottheonlyformofthenullpointerconstant/NullptrNotTheOnlyFormOfTheNullPointerConstant.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-11-2/ArrayPassedAsFunctionArgumentDecayToAPointer.testref b/cpp/misra/test/rules/RULE-7-11-2/ArrayPassedAsFunctionArgumentDecayToAPointer.testref new file mode 100644 index 0000000000..06f2ec8fbb --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-2/ArrayPassedAsFunctionArgumentDecayToAPointer.testref @@ -0,0 +1 @@ +cpp/common/test/rules/arraypassedasfunctionargumentdecaytoapointer/ArrayPassedAsFunctionArgumentDecayToAPointer.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-18-2/ResultOfAnAssignmentOperatorShouldNotBeUsed.testref b/cpp/misra/test/rules/RULE-8-18-2/ResultOfAnAssignmentOperatorShouldNotBeUsed.testref new file mode 100644 index 0000000000..1e29dba140 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-18-2/ResultOfAnAssignmentOperatorShouldNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/resultofanassignmentoperatorshouldnotbeused/ResultOfAnAssignmentOperatorShouldNotBeUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-19-1/CommaOperatorShouldNotBeUsed.testref b/cpp/misra/test/rules/RULE-8-19-1/CommaOperatorShouldNotBeUsed.testref new file mode 100644 index 0000000000..845133096b --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-19-1/CommaOperatorShouldNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/commaoperatorused/CommaOperatorUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-10/FunctionsCallThemselvesEitherDirectlyOrIndirectly.testref b/cpp/misra/test/rules/RULE-8-2-10/FunctionsCallThemselvesEitherDirectlyOrIndirectly.testref new file mode 100644 index 0000000000..f459a29bf1 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-10/FunctionsCallThemselvesEitherDirectlyOrIndirectly.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-3/CastRemovesConstOrVolatileFromPointerOrReference.testref b/cpp/misra/test/rules/RULE-8-2-3/CastRemovesConstOrVolatileFromPointerOrReference.testref new file mode 100644 index 0000000000..000469493a --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-3/CastRemovesConstOrVolatileFromPointerOrReference.testref @@ -0,0 +1 @@ +cpp/common/test/rules/removeconstorvolatilequalification/RemoveConstOrVolatileQualification.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-4/CastsBetweenAPointerToFunctionAndAnyOtherType.testref b/cpp/misra/test/rules/RULE-8-2-4/CastsBetweenAPointerToFunctionAndAnyOtherType.testref new file mode 100644 index 0000000000..e7bde2ea08 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-4/CastsBetweenAPointerToFunctionAndAnyOtherType.testref @@ -0,0 +1 @@ +cpp/common/test/rules/castsbetweenapointertofunctionandanyothertype/CastsBetweenAPointerToFunctionAndAnyOtherType.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-5/ReinterpretCastShallNotBeUsed.testref b/cpp/misra/test/rules/RULE-8-2-5/ReinterpretCastShallNotBeUsed.testref new file mode 100644 index 0000000000..81f18c2d9c --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-5/ReinterpretCastShallNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/reinterpretcastused/ReinterpretCastUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-20-1/UnsignedOperationWithConstantOperandsWraps.testref b/cpp/misra/test/rules/RULE-8-20-1/UnsignedOperationWithConstantOperandsWraps.testref new file mode 100644 index 0000000000..148997676e --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-20-1/UnsignedOperationWithConstantOperandsWraps.testref @@ -0,0 +1 @@ +cpp/common/test/rules/unsignedoperationwithconstantoperandswraps/UnsignedOperationWithConstantOperandsWraps.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-3-1/BuiltInUnaryOperatorAppliedToUnsignedExpression.testref b/cpp/misra/test/rules/RULE-8-3-1/BuiltInUnaryOperatorAppliedToUnsignedExpression.testref new file mode 100644 index 0000000000..bd12c39fbd --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-3-1/BuiltInUnaryOperatorAppliedToUnsignedExpression.testref @@ -0,0 +1 @@ +cpp/common/test/rules/builtinunaryoperatorappliedtounsignedexpression/BuiltInUnaryOperatorAppliedToUnsignedExpression.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-3-1/LoopBodyCompoundCondition.testref b/cpp/misra/test/rules/RULE-9-3-1/LoopBodyCompoundCondition.testref new file mode 100644 index 0000000000..84dc7caf76 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-3-1/LoopBodyCompoundCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/loopcompoundcondition/LoopCompoundCondition.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-3-1/SwitchBodyCompoundCondition.testref b/cpp/misra/test/rules/RULE-9-3-1/SwitchBodyCompoundCondition.testref new file mode 100644 index 0000000000..f02b02ba85 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-3-1/SwitchBodyCompoundCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/switchcompoundcondition/SwitchCompoundCondition.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-4-1/IfElseIfEndCondition.testref b/cpp/misra/test/rules/RULE-9-4-1/IfElseIfEndCondition.testref new file mode 100644 index 0000000000..d7ca04a26e --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-4-1/IfElseIfEndCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-6-1/GotoStatementShouldNotBeUsed.testref b/cpp/misra/test/rules/RULE-9-6-1/GotoStatementShouldNotBeUsed.testref new file mode 100644 index 0000000000..44d306f80c --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-6-1/GotoStatementShouldNotBeUsed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/gotostatementshouldnotbeused/GotoStatementShouldNotBeUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-6-2/GotoReferenceALabelInSurroundingBlock.testref b/cpp/misra/test/rules/RULE-9-6-2/GotoReferenceALabelInSurroundingBlock.testref new file mode 100644 index 0000000000..7502d9431c --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-6-2/GotoReferenceALabelInSurroundingBlock.testref @@ -0,0 +1 @@ +cpp/common/test/rules/gotoreferencealabelinsurroundingblock/GotoReferenceALabelInSurroundingBlock.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-6-3/GotoShallJumpToLabelDeclaredLaterInTheFunction.testref b/cpp/misra/test/rules/RULE-9-6-3/GotoShallJumpToLabelDeclaredLaterInTheFunction.testref new file mode 100644 index 0000000000..b4f807e8e2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-6-3/GotoShallJumpToLabelDeclaredLaterInTheFunction.testref @@ -0,0 +1 @@ +cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-6-4/FunctionDeclaredWithTheNoreturnAttributeReturn.testref b/cpp/misra/test/rules/RULE-9-6-4/FunctionDeclaredWithTheNoreturnAttributeReturn.testref new file mode 100644 index 0000000000..dec8006f15 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-6-4/FunctionDeclaredWithTheNoreturnAttributeReturn.testref @@ -0,0 +1 @@ +cpp/common/test/rules/functionnoreturnattributecondition/FunctionNoReturnAttributeCondition.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-6-5/NonVoidFunctionShallReturnAValueOnAllPaths.testref b/cpp/misra/test/rules/RULE-9-6-5/NonVoidFunctionShallReturnAValueOnAllPaths.testref new file mode 100644 index 0000000000..ef9b3c1fc2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-6-5/NonVoidFunctionShallReturnAValueOnAllPaths.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql \ No newline at end of file diff --git a/cpp/options b/cpp/options index f1c6ec672f..44267e9323 100644 --- a/cpp/options +++ b/cpp/options @@ -1 +1 @@ -semmle-extractor-options:--clang -std=c++14 -nostdinc++ -I../../../../common/test/includes/standard-library \ No newline at end of file +semmle-extractor-options:--clang -std=c++14 -nostdinc++ -I../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library diff --git a/cpp/report/src/Diagnostics/ExtractionErrors.qll b/cpp/report/src/Diagnostics/ExtractionErrors.qll index 55e1c96461..58757ca544 100644 --- a/cpp/report/src/Diagnostics/ExtractionErrors.qll +++ b/cpp/report/src/Diagnostics/ExtractionErrors.qll @@ -60,10 +60,10 @@ class ExtractionError extends TExtractionError { /** Gets the error message for this error. */ string getErrorMessage() { none() } - /** Gets the file this error occured in. */ + /** Gets the file this error occurred in. */ File getFile() { none() } - /** Gets the location this error occured in. */ + /** Gets the location this error occurred in. */ Location getLocation() { none() } /** Gets the SARIF severity of this error. */ diff --git a/cpp/report/src/codeql-pack.lock.yml b/cpp/report/src/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/cpp/report/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/cpp/report/src/qlpack.yml b/cpp/report/src/qlpack.yml index 01ab377d7b..1574721c0f 100644 --- a/cpp/report/src/qlpack.yml +++ b/cpp/report/src/qlpack.yml @@ -1,3 +1,5 @@ -name: report-cpp-coding-standards -version: 2.9.0 -libraryPathDependencies: codeql-cpp +name: codeql/report-cpp-coding-standards +version: 2.49.0-dev +license: MIT +dependencies: + codeql/cpp-all: 4.0.3 diff --git a/docs/design/detection_of_genenated_infinities_and_nans.md b/docs/design/detection_of_genenated_infinities_and_nans.md new file mode 100644 index 0000000000..62396eea99 --- /dev/null +++ b/docs/design/detection_of_genenated_infinities_and_nans.md @@ -0,0 +1,294 @@ +# Coding Standards: Detection of generated Infinities and NaNs + +- [Coding Standards: Detection of generated Infinities and NaNs](#coding-standards-detection-of-generated-infinities-and-nans) + - [Document management](#document-management) + - [Background](#background) + - [Critical problems](#critical-problems) + - [TL;DR](#tldr) + - [Range / Source Analysis, In Detail](#range-source-analysis-in-detail) + - [Mathematical Operations](#mathematical-operations) + - [Range / Source Proposal #1 (Recommended)](#range-source-proposal-1-recommended) + - [Range / Source Proposal #2 (Not Recommended)](#range-source-proposal-2-not-recommended) + - [Range / Source Proposal #3 (Not Recommended)](#range-source-proposal-3-not-recommended) + - [Range / Source Proposal #4 (Not Recommended)](#range-source-proposal-4-not-recommended) + - [Detection / Sink Analysis, In Detail](#detection-sink-analysis-in-detail) + - [Detection / Sink Proposal #1 (Recommended)](#range-source-proposal-1-recommended) + - [Detection / Sink Proposal #2 (Not Recommended)](#range-source-proposal-2-not-recommended) + - [Case study examples](#case-study-examples) + +## Document management + +**ID**: codeql-coding-standards/design/detection-infinities-nans
+**Status**: Draft + +| Version | Date | Author(s) | Reviewer (s) | +| ------- | ---------- | -------------- | ---------------------------------------------------------------------------------------------- | +| 0.1 | 12/13/2024 | Mike Fairhurst | Robert C. Seacord, J.F. Bastien, Luke Cartey, Vincent Mailhol, Fernando Jose, Rakesh Pothengil | + +## Background + +Directive 4-15 of MISRA-C 2023 states that a program shall not have undetected generation of Infinities and NaNs. It also states that infinities and NaNs may propagate across various FLOPs, but may not propagate into sections of code not designed to handle infinities and NaNs. + +This directive is intentionally open to a large degree of interpretation. This document is intended to help guide the decision making for how to implement this directive in the most useful way. + +## Critical problems + +There are two fundamental problems to decide on before implementing this directive: +- **Range / source analysis**, even a simple expression like `a + b` can be a source of infinity or NaN if there is no estimated value for `a` and/or `b`, which violates developer expectations and produces false positives. +- **Detection / sink analysis**, how we decide which sources need to be reported to users. This can be flow analysis with sinks, or it can be modeled as a resource leak analysis where certain actions (`isnan(x), x < 10`) are handled as freeing the NaN/infinite value. + +## TL;DR + +This document proposes to create a float-specialized copy of standard range analysis which assumes most values are in the range of +/-1e15, which covers most valid program use cases, and allows `a * b` without generating `Infinity`. Then standard flow analysis will be used to detect when these values flow into underflowing operations (`x / infinity` and `x % infinity`, etc.), and when NaNs flow into comparison operations (`>`, `>=`, `<`, `<=`). If the query is noisy, we may ignore NaNs and/or infinities that come from the mostly safe basic operations (`+`, `-`, `*`). + +## Range / Source Analysis, In Detail: + +Default CodeQL range analysis is limited for performance reasons (etc): + +- Range analysis is local, not interprocedural +- Global variables are assumed to be in the range of [-Infinity, Infinity] +- Struct members, array values are assumed to be in the range of [-Infinity, Infinity] +- Guards (e.g. `x != 0 ? y / x : ...`) are not always tracked + +This creates a scenario where even `a + b` can be `Infinity` or `NaN`; if either `a` or `b` is `Inf` then the expression is `Inf`, and if `a` is `+Inf` while `b` is `-Inf` then the result is `NaN`. + +Perhaps the flaw is in assuming `a` or `b` may be an infinite value. However, if the analysis considered `a` and `b` to be between the largest positive and negative finite floating values, then still `a + b` can produce an infinity, and only offers a single step of protection from false positives and negatives. + +There are a few proposals to handle this. + +#### Mathematical Operations + +IEEE 754-1985 specifies floating point semantics for “add, subtract, multiply, divide, square root, remainder, and compare operations,” including invalid use of these operators that produce NaN, propagating NaNs, and overflows that produce +/- Infinity. These semantics shall be used in the analysis. + +The C17 standard states that implementations compiling with IEEE 754-1985 shall define `__STDC_IEC_559__`. Under this proposal we will detect compilations where this macro is not defined and report a warning. + +Beyond the standard binary operators defined by IEEE 754-1985, programs may generate Infinity and/or NaN in the standard library mathematical functions. Note that technically, the c spec is vague in certain ways about when range errors, pole errors, and invalid operation errors in the standard math functions produce infinities or NaNs. We propose to assume IEEE 754-2019 behavior in this regard as a practical matter, though there is no guarantee that is the case. An alternative approach which we do not plan to take would be to broadly assume that all range errors, pole errors, invalid operation errors produce both Infinities and NaNs. This alternative would increase false positives. + +### Range / Source Proposal #1 (Recommended): + +We will create a new float-specific version of range analysis. The actual values stored in floating point variables in real programs are very unlikely to be close to the limits of finite floating point numbers (+/- 3.4e38 for floats, 1.8e308 for doubles). This proposal is that we, for the purposes of this rule, create a new range analysis for floats where otherwise undeterminable values are assumed to be a very large range that is small compared to the range of floating point values, such as +/-1e15. + +Creating a new version of float analysis rather than extending previous analysis is likely to have better performance than extending standard range analysis. When floats and integers interact, the integer from standard range analysis can be used. + +Implications of this approach (assuming values `a` and `b` have no estimated range): +- `a + b` will be finite +- `a * b` will be finite +- `a * b * c` will be possibly infinite +- `a / b` will be possibly infinite, as the range includes small values such as 1e-30, as well as zero +- `acos(a)` will be considered out of domain + +**Additional option**: If this query is still noisy, we may simply exclude reporting NaN’s and Infinities that come from a basic float operation such as `+`, `-`, or `*`. We would most likely still choose to report `/`, as divide-by-zero errors are the most common and most important to catch. + +### Range / Source Proposal #2 (Not Recommended): + +This proposal mirrors Proposal #1 except that otherwise undeterminable values will be treated as the max finite value +/-3.4e38 (floats) or 1.7e308 (doubles). + +The implications are as above except: +- `a + b` will be possibly infinite +- `a * b` will be possibly infinite + +### Range / Source Proposal #3 (Not Recommended): + +Under this proposal, standard CodeQL range analysis is used to detect generation of NaN/Infinity. All uses of a global or otherwise undeterminable value will be considered possibly infinite. + +### Range / Source Proposal #4 (Recommended): + +Under this proposal, standard CodeQL range analysis is extended to provide the support of proposal #1. While this should mostly have the same results, it will likely create performance problems, as it would rerun all range analysis code on every expression in order to have different findings in a subset of them. + +## Detection / Sink Analysis, In Detail: + +The directive states that: + +- Generated Infinities and NaNs may not be unchecked +- Infinities and NaNs may propagate to delay NaN/Infinity checking for performance reasons +- Infinities and NaNs may not reach sections of code not designed to handle them + +This leaves open some questions. For instance, is `printf("%f", a * b)` possibly sending an infinity to code prepared to handle it? + +### Detection / Sink Proposal #1 (recommended): + +This proposal is to identify sinks that should not accept Infinity or NaN, and then rely on standard flow analysis as the backbone of supporting this directive. + +If a valid propagation of a NaN or an Infinity can be distinguished from cases where a program was not prepared to receive a NaN or Infinity, then flow analysis is the only thing that is needed, and a resource-leak approach is not necessary. This proposes that the following cases are detected as sinks, such that if NaN or Infinity flows into them they are reported. + +**Case 1**: _NaNs shall not be compared, except to themselves_ +```c +void f(float x, float y) { + float z = x / y; // Could be 0.0 / 0.0 which produces NaN + + if (x < 10) { ... } // Not allowed + if (x != x) { ... } // OK +} +``` + +**Case 2**: _NaNs and infinities shall not be cast to integers_ +```c +void f(float x, float y) { + int z = x / y; // 0.0 / 0.0 may produce Infinity or NaN +} +``` + +**Case 3**: _Infinite values shall not underflow or otherwise produce finite values_ +```c +float f(void) { + float x = ...; // Could be a positive number / 0.0, which produces Infinity + 1 / x; // If x is Infinity, this underflows to 0.0 + 1 % x; // If x is Infinity, this is defined to produce 1. +} +``` + +**Case 4**: _Functions shall not return NaNs and infinities_ +```c +void f(float* p) { + float local1 = ...; // Could be infinity + + return local1; +} +``` + +**Case 5**: _NaNs and infinities shall only be stored in local stack variables_ +```c +float global; +void f(float* p) { + float local1 = ...; // Could be infinity + + // The following assignments could store an infinity in the heap: + global = local1; + extern_function(local1); + *p = local1; + + // The following cases should be possible to analyze correctly as well + // with modest effort: + float arr[10] = ...; + struct my_struct = ...; + arr[0] = local2; + my_struct.member = local1; +} +``` + +**Case 6 (not planned, compiler specific)**: _Functions can use assume() to declare they are not prepared for Infinity or NaN_ +```c +void f(float x, float[] y, struct foo z) { + assume(!isnan(x)); // May be supportable, not planned + assert(!isnan(y[0])); // Not supportable + assert(!isnan(z.member)); // Not supportable +} +``` + +With these cases specified, we can detect invalid usage of Infinity and NaN with simple flow analysis. + +## Detection / Sink Proposal #2 (not recommended): + +This proposed solution takes inspiration from resource leak detection. In this metaphor, a generated infinity or NaN is treated like a resource that must be disposed. [There is a draft WIP of this approach here.](https://github.com/github/codeql-coding-standards/compare/main...michaelrfairhurst/implement-floatingtype-package) + +The advantage of this solution is that we do not need to define every way in which a NaN or an Infinity could be misused. Rather, we only need to define a few ways that a NaN or Infinity can be checked, and then find possible Infinities and NaNs that are not checked (or propagated to a value that is checked). + +Under this proposal, the following are echecks for infinity and NaN: + +- The macros `isnan(x)`, `isinf(x)`, `isfinite(x)` should be considered checks for infinity. +- Reflexive equality checks (`x == x` or `x != x`) should be considered checks for NaN. +- Any comparison operation (`>`, `>=`, `<`, and `<=`) should be considered a check on both positive and negative and positive infinities for an operand if the other is finite. + - If `a` may only be positive infinity, `a < b` and `a > b` both create a branch of the code where `a` is not positive infinity. + - If `a` may only be negative infinity, the same as above is the case for negative infinity cases. + - If `a` may be both positive or negative infinity, then a single check is not sufficient, however detecting an appropriate pair of checks would be a much more difficult implementation + +Only local leak detection analysis is feasible at this time. Therefore, this proposal suggests that an infinite or NaN value should be flagged if it goes out of scope before it is checked. _(In other leak detection problems, this would typically be considered a free event to avoid false positives, and that is an option here as well)_. + +```c +float g; +void f(void) { + float l = 1 / 0; // Must be checked + g = l; // May send Infinity to code not prepared to handle it + isinf(l); // check occurs too late +} +``` + +Overall, this option is not recommended for the following reasons: + +- Slower performance than Proposal #1 +- Limited benefits over Proposal #1 +- Detecting out-of-scope cases heavily resembles Proposal #1 +- Unused values will be flagged, which is not useful to developers +- Intraprocedural analysis will be difficult to support +- High false positive rate if too few checks are detected, as opposed to the alternative where missing sinks do not create false positives + +In this analysis, a method or function call which can generate an infinity, such as `x / y` is treated somewhat like opening a file descriptor, and calls to `isinf(x)` or `isnan(x)` are treated as closing that file descriptor. _There are some differences between how we would approach this and how an actual resource leak detection would be modeled. For instance, we are not searching for use-after-free or double-free bugs, in this metaphor._ + +Note that resource leak detection is not the same as standard CodeQL flow analysis. For instance, if the below example is analyzed with flow analysis, CodeQL will detect that the result of `fopen` flows into a call to `fclose`. However, this only means it is possible that the program will close the file, it does not mean the file descriptor cannot leak. + +```c +void f(bool p) { + FILE* fd = fopen(...); + if (p) { + fclose(fd); + } +} +``` + +The drafted leak detection algorithm follows [this paper](https://arxiv.org/html/2312.01912v2/#S2.SS2). In this approach, the program flow from the exit of `f()` is walked backwards. The walk stops upon reaching a call to `fclose()`, and if a call to `fopen()` is reached by this iterative process then that resource could leak. + +_Note that this approach still uses flow analysis to determine that fclose(fd) is referring to an initial fopen() call. In the paper, flow analysis is used to find aliases of resources, so that disposing an alias of a resource is handled correctly._ + +This approach is still neither 100% accurate nor precise. It can generate both false positives and false negatives, though it is hopefully accurate and precise enough for our purposes: + +```c +// FALSE POSITIVE: See fprintf call marked (1). Not all successors from (1) + // call fclose(), and not all predecessors of (1) call fclose() either. + // All paths dispose fd, but this algorithm does not see that. + fd = fopen(...); + if (!cond) { + fclose(fd); + } + fprintf(...); // (1) + if (cond) { + fclose(fd); + } + + // FALSE NEGATIVE: The file descriptor opened at (2) flows into the dispose + // call at (3) if the values of x and y are not known. However, the resource + // is only closed when x == y, which is not necessarily the case. + fds[x] = fopen(...); // (2) + fclose(fds[y]); // (3) +``` + +Nevertheless, their approach is sensible and likely good enough. + +Lastly, this approach has the unfortunate downside that unused float values which could be NaN or Infinity will be reported, when they do not have any negative effect on a program (as opposed to the negative effects of leaking unused file descriptors, or unused memory, etc). + +## Case study examples + +The following is an interesting set of examples and code snippets that come from the open source project [pandas](https://github.com/commaai/panda), which aims to be MISRA compliant and is used for self-driving cars. + +These examples are hand picked results from a query that selected expressions with a floating point type along with their upper and lower bounds. + +**Example 1**: +```c +float filtered_pcm_speed = + ((to_push->data[6] << 8) | to_push->data[7]) + * 0.01 / 3.6; +// Disable controls if speeds from ABS and PCM ECUs are too far apart. +bool is_invalid_speed = ABS(filtered_pcm_speed + - ((float)vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR)) + > FORD_MAX_SPEED_DELTA; +``` + +While `filter_pcm_speed` cannot be infinity or NaN, it is interesting to see how this value is sanity checked. If this code were refactored such that it could produce NaN, the greater-than check would return false, resulting in a bug. If the condition were flipped (check inside valid range, rather than outside), it would handle NaN correctly. This cannot be captured via static analysis. + +**Example 2**: +```c + float x0 = xy.x[i]; + float y0 = xy.y[i]; + float dx = xy.x[i+1] - x0; + float dy = xy.y[i+1] - y0; + // dx should not be zero as xy.x is supposed to be monotonic + dx = MAX(dx, 0.0001); + ret = (dy * (x - x0) / dx) + y0; +``` + +This is an [interpolation function](https://github.com/commaai/panda/blob/dec9223f9726e400e4a4eb91ca19fffcd745f97a/board/safety.h#L538), where `xy` is a struct parameter, with array members `x` and `y` that represent points in the domain and range to interpolate across. + +Range analysis is performed with local information only, and therefore, the expression `xy.x[i]` is given the range [-Infinity, Infinity]. This is not a generated infinity. However, the computations of `dx` and `dy` could generate a positive or negative infinity (if both numbers are finite and the result exceeds the maximum float value), they could propagate a positive or negative infinity, and/or they could generate a NaN (if an infinite value is subtracted from itself). + +The call to `MAX()` will not check if `dx` = positive infinity, and is unsafe to use with NaN. It prevents a divide-by-zero error, but `ret` could still propagate or generate a NaN or one of the infinities since we know so little about `dy`, `x0`, and `y0`. + +It’s worth noting that if `dx` is positive Infinity, then `(x - x0) / dx` will produce zero, rather than propagating the infinity. This may be worth flagging. \ No newline at end of file diff --git a/docs/design/guideline_recategorization.md b/docs/design/guideline_recategorization.md new file mode 100644 index 0000000000..e488cf88de --- /dev/null +++ b/docs/design/guideline_recategorization.md @@ -0,0 +1,179 @@ +# Coding Standards: Guideline Recategorization + +- [Coding Standards: Guideline Recategorization](#coding-standards-guideline-recategorization) + - [Document management](#document-management) + - [Introduction](#introduction) + - [Design](#design) + - [Guideline Recategorization Plan](#guideline-recategorization-plan) + - [Implementation](#implementation) + - [Specification and deviation](#specification-and-deviation) + - [Specification validation](#specification-validation) + - [SARIF rewriting](#sarif-rewriting) + - [Non-MISRA standards](#non-misra-standards) + +## Document management + +**ID**: codeql-coding-standards/design/grp
+**Status**: Draft + +| Version | Date | Author(s) | Reviewer (s) | +| ------- | ---------- | --------------- | ------------ | +| 0.1 | 08/10/2022 | Remco Vermeulen | \ | +| 0.2 | 10/25/2022 | Remco Vermeulen | Mauro Baludo, John Singleton | +| 0.3 | 11/30/2022 | Remco Vermeulen | Robert C. Seacord | + +## Introduction + +Each MISRA guideline belongs to a category that defines a policy to be followed to determine whether a guideline may be violated or not and whether a deviation is required. +The document [MISRA Compliance:2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) defines the following guideline categories, and their associated policies, in addition to a mechanism to recategorize guidelines. + +- Mandatory guidelines - guidelines for which violation is never permitted. +- Required guidelines - guidelines which can only be violated when supported by a deviation. +- Advisory guidelines - recommendations for which violations are identified but are not required to be supported by a deviation. + +Guideline recategorization is possible by means of a guideline recategorization plan (GRP). A GRP is a contract between the acquirer and supplier to determine how guidelines are applied. +The GRP defines the additional category Disapplied to be used for Advisory guidelines which are to be ignored. Any other category can be recategorized into stricter categories to ensure that a guideline adheres to the associated policy. +The following table summarizes the possible recategorizations. + +| Category | Recategorizations | +| --------- | ------------------------------- | +| Mandatory | | +| Required | Mandatory | +| Advisory | Disapplied, Required, Mandatory | + +Other recategorizations are invalid, not applied, and reported to the user. + +## Design + +CodeQL Coding Standards includes a GRP, logic to apply the category policy to associated guidelines, and a SARIF result rewriter to reflect the new category in the results. +The application of a policy will modify the behavior of a CodeQL queries implementing guidelines as follows: + +| Category | Effect | +| ---------- | -------------------------------------------------------------------- | +| Mandatory | Violations are reported, even if a deviation is applicable! | +| Required | Violations are reported unless there exists an applicable deviation. | +| Advisory | Violations are reported unless there exists an applicable deviation. | +| Disapplied | Violations are not reported. | + +The SARIF rewrite updates the guideline category in a SARIF result file by updating the query's tag information. + +### Guideline Recategorization Plan + +The GRE builds upon the configuration specification introduced for deviations by adding the additional primary section `guideline-recategorizations` to the `codeql-standards.yml` configuration file. +The `guideline-recategorizations` section will be a series of compact mappings in YAML with the keys: + +- `rule-id` - the recategorized rule identifier. +- `category` - the category assigned to the rule identified by rule-id + +For example: + +```yaml +guideline-recategorizations: +- rule-id: “M5-0-17” + category: “mandatory” +``` + +## Implementation + +This section discusses the implementation of the [design](#design). + +### Specification and deviation + +The implementation relies on the existing rule meta-data and query exclusion mechanisms to apply policies associated with a rule’s category. +The rule meta-data already includes both the `query-id` and `rule-id` associated with a query and is available during query evaluation. +The rule meta-data must be extended with a category that contains the guideline’s category. + +For example: + +```ql + query = + // `Query` instance for the `pointerSubtractionOnDifferentArrays` query + PointersPackage::pointerSubtractionOnDifferentArraysQuery() and + queryId = + // `@id` for the `pointerSubtractionOnDifferentArrays` query + "cpp/autosar/pointer-subtraction-on-different-arrays" and + ruleId = "M5-0-17" and + category = “required” +``` + +The category defined by the rule meta-data and the category defined in the `guideline-recategorizations` of the applicable `codeql-standards.yml` configuration file specifies the *effective category* of a query. +The *effective category* is the category whose policy is applied during the evaluation of a query. +The policy of a category dictates if a result can be deviated from and implements the effect described in the design section. +The existing exclusion mechanism implemented in the predicate `isExcluded` defined in the `Exclusions.qll` library will be updated to consider the applicable policy of a guideline. + +Note: This changes the behavior of deviations which will no longer have an impact on Mandatory MISRA guidelines! + +### Specification validation + +To assist users with correctly specifying a GRP specification we can implement two validations mechanisms that validate the specification at two different points in a GRP life cycle. +The first validation mechanism performs syntax validation of the specification provided in the guideline-recategorizations section of a `codeql-standards.yml` configuration file and can provide feedback in any editor that supports JSON schemas published at the [JSON schema store](https://www.schemastore.org/json/). +A schema for `codeql-standards.yml` can be extended with the definition of `guideline-category` and the property `guideline-recategorizations`: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "definitions": { + "guideline-category": { + "enum": [ + "mandatory", + "required", + "advisory", + "disapplied" + ] + } + }, + "properties": { + "report-deviated-alerts": {...}, + "deviations": {...}, + "deviation-permits": {...}, + "guideline-recategorizations": { + "description": "A set of guideline recategorizations", + "type": "array", + "items": { + "type": "object", + "properties": { + "rule-id": { + "type": "string" + }, + "category": { + "$ref": "#/definitions/guideline-category" + } + } + } + } + }, + "required": [], + "type": "object" +} +``` + +The second validation mechanism is the generation of a `guideline-recategorization-plan-report.md` containing alerts on semantically incorrect recategorizations. +That is, possible recategorizations that are not described as valid in the introduction. +Semantically invalid recategorizations are detected by examining a query’s categorization and its effective categorization (i.e., its applied recategorization). + +In addition, an update to the `deviations_report.md` report’s invalidate deviations table provides feedback to users that apply deviations to guidelines with an effective category equal to `mandatory` which cannot be deviated from. +The changes to generate the new report and update the existing report will be made in the report generation script `scripts/reports/analysis_report.py`. + +### SARIF rewriting + +The *effective category* of a guideline is a runtime property that is not reflected in the SARIF result file and therefore is not visible in any viewer used to view the results (e.g., [Code Scanning](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning)). +To ensure that users can view the *effective category* we need to rewrite the `@tags` query metadata property in the SARIF result file. +The `@tags` value is a JSON array located at the [JSON path](https://datatracker.ietf.org/wg/jsonpath/about/): + +`$.runs[?(@.tool.driver.name="CodeQL")].tool.driver.rules[*].properties.tags` + +The category tag has the form `external//obligation/` +Each guideline has an `external//id/` tag that can be used to determine if a recategorization is applicable by performing a case insensitive compare on the `` extracted from the query’s tags array and the value of the rule-id key in a `guideline-recategorizations` section. +The rewriting replaces the `` part in `external//obligation/` with the newly specified category and adds a new tag `external//original-obligation/` with the rule’s original category. + +The rewrite process translates each entry in the guideline recategorization specification into a [JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) specific to the processed SARIF file. The JSON Patch is SARIF file specific due to its reliance on [JSON Pointer](https://www.rfc-editor.org/rfc/rfc6901) to locate the obligation tags. + +A new SARIF file is created by applying the JSON Patch to the processed SARIF file. + +## Non-MISRA standards + +Guideline recategorization applies to rules adhering to the MISRA categorizations. +For standards that deviate from these conventions the rules have an *effective category* equivalent to MISRA’s *required* category. + +CERT rules, for example, are handled in the same way as MISRA's rules recategorized to *required*. diff --git a/docs/development_handbook.md b/docs/development_handbook.md index 898fed7542..83670dbbc8 100644 --- a/docs/development_handbook.md +++ b/docs/development_handbook.md @@ -4,51 +4,56 @@ **Document ID:** codeql-coding-standards/developer-handbook -| Version | Date | Author | Changes | -| ------- | ---------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0.1.0 | 2021-02-02 | Luke Cartey | Initial version. | -| 0.2.0 | 2021-02-19 | Luke Cartey | Add section on Python environment preparation. | -| 0.3.0 | 2021-04-13 | Michael Hohn | Add cookbook section documenting common procedures. | -| 0.4.0 | 2021-04-13 | Mario Campos | Add submodule out of date tip to the cookbook section. | -| 0.5.0 | 2021-04-30 | Luke Cartey | Add query style guide. | -| 0.6.0 | 2021-05-05 | John Singleton | Add task automation files. | -| 0.7.0 | 2021-05-10 | Luke Cartey | Explain non-constant alert messages. | -| 0.8.0 | 2021-05-27 | Luke Cartey | Clarify the `short_name` property. | -| 0.9.0 | 2021-09-06 | Luke Cartey |
  • Update code review requirements.
  • Update release process documentation.
| -| 0.10.0 | 2021-09-08 | Luke Cartey | Update tool qualification section. | -| 0.11.0 | 2021-09-10 | Luke Cartey | Add reporting and deviations to scope of work. | -| 0.12.0 | 2021-09-18 | Luke Cartey |
  • Document conditions for overriding PR checks.
  • Clarify that LGTM and GHAS are out of scope.
  • Document our used versioning control system.
| -| 0.13.0 | 2021-09-22 | Remco Vermeulen | Document rule package schema. | -| 0.14.0 | 2021-10-11 | Luke Cartey | Document how to update dependencies. | -| 0.15.0 | 2021-10-26 | John Singleton | Document false positive triage process. | -| 0.16.0 | 2021-11-29 | Remco Vermeulen | Add document management section. | -| 0.17.0 | 2021-11-29 | Remco Vermeulen |
  • Explain the process of determining if a guideline is amenable to automated static analysis.
  • Document the supported language.
  • Document the `short_name` property update process.
  • Describe guidelines for splitting a rule into multiple queries.
| -| 0.18.0 | 2022-02-16 | Remco Vermeulen | Address mistake in point 2 in section *Splitting a rule into multiple queries*. | -| 0.19.0 | 2022-06-15 | Remco Vermeulen | Replace references and steps related to Markdown help files. | -| 0.20.0 | 2022-07-05 | Remco Vermeulen | Expand scope of work to include CERT-C and MISRA C. | -| 0.21.0 | 2022-07-05 | Remco Vermeulen | Update architecture section to include the supported languages C90, C99, and C11. | -| 0.22.0 | 2022-07-05 | Remco Vermeulen | Update section `Generation of query templates from rule specifications` to include external help files. | -| 0.23.0 | 2022-07-05 | Remco Vermeulen | Update text to consider both the C++ and the C standards. | -| 0.24.0 | 2022-07-05 | Remco Vermeulen | Update release process to include steps for external help files. | -| 0.25.0 | 2022-07-22 | Jeroen Ketema | Document the existence and purpose of the `next` branch. | -| 0.26.0 | 2022-08-10 | Remco Vermeulen | Address incorrect package file generation command. This was missing the required language argument. | +| Version | Date | Author | Changes | +| ------- | ---------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0.1.0 | 2021-02-02 | Luke Cartey | Initial version. | +| 0.2.0 | 2021-02-19 | Luke Cartey | Add section on Python environment preparation. | +| 0.3.0 | 2021-04-13 | Michael Hohn | Add cookbook section documenting common procedures. | +| 0.4.0 | 2021-04-13 | Mario Campos | Add submodule out of date tip to the cookbook section. | +| 0.5.0 | 2021-04-30 | Luke Cartey | Add query style guide. | +| 0.6.0 | 2021-05-05 | John Singleton | Add task automation files. | +| 0.7.0 | 2021-05-10 | Luke Cartey | Explain non-constant alert messages. | +| 0.8.0 | 2021-05-27 | Luke Cartey | Clarify the `short_name` property. | +| 0.9.0 | 2021-09-06 | Luke Cartey |
  • Update code review requirements.
  • Update release process documentation.
| +| 0.10.0 | 2021-09-08 | Luke Cartey | Update tool qualification section. | +| 0.11.0 | 2021-09-10 | Luke Cartey | Add reporting and deviations to scope of work. | +| 0.12.0 | 2021-09-18 | Luke Cartey |
  • Document conditions for overriding PR checks.
  • Clarify that LGTM and GHAS are out of scope.
  • Document our used versioning control system.
| +| 0.13.0 | 2021-09-22 | Remco Vermeulen | Document rule package schema. | +| 0.14.0 | 2021-10-11 | Luke Cartey | Document how to update dependencies. | +| 0.15.0 | 2021-10-26 | John Singleton | Document false positive triage process. | +| 0.16.0 | 2021-11-29 | Remco Vermeulen | Add document management section. | +| 0.17.0 | 2021-11-29 | Remco Vermeulen |
  • Explain the process of determining if a guideline is amenable to automated static analysis.
  • Document the supported language.
  • Document the `short_name` property update process.
  • Describe guidelines for splitting a rule into multiple queries.
| +| 0.18.0 | 2022-02-16 | Remco Vermeulen | Address mistake in point 2 in section *Splitting a rule into multiple queries*. | +| 0.19.0 | 2022-06-15 | Remco Vermeulen | Replace references and steps related to Markdown help files. | +| 0.20.0 | 2022-07-05 | Remco Vermeulen | Expand scope of work to include CERT-C and MISRA C. | +| 0.21.0 | 2022-07-05 | Remco Vermeulen | Update architecture section to include the supported languages C90, C99, and C11. | +| 0.22.0 | 2022-07-05 | Remco Vermeulen | Update section `Generation of query templates from rule specifications` to include external help files. | +| 0.23.0 | 2022-07-05 | Remco Vermeulen | Update text to consider both the C++ and the C standards. | +| 0.24.0 | 2022-07-05 | Remco Vermeulen | Update release process to include steps for external help files. | +| 0.25.0 | 2022-07-14 | David Bartolomeo | Add section on installing QL dependencies and update CLI commands to account for the migration to CodeQL packs. | +| 0.25.0 | 2022-07-22 | Jeroen Ketema | Document the existence and purpose of the `next` branch. | +| 0.26.0 | 2022-08-10 | Remco Vermeulen | Address incorrect package file generation command. This was missing the required language argument. | +| 0.27.0 | 2022-11-08 | Luke Cartey | Update the versions of C we intend to support to exclude C90, which reflects the intended scope at the outset of the project. | +| 0.28.0 | 2023-08-14 | Luke Cartey | Remove references to LGTM which is now a legacy product. | +| 0.29.0 | 2023-10-11 | Remco Vermeulen | Update release process. | +| 0.29.1 | 2023-10-11 | Remco Vermeulen | Address Markdown linter problems. | +| 0.30.0 | 2023-11-14 | Remco Vermeulen | Clarify release steps in case of a hotfix release. | +| 0.31.0 | 2024-02-23 | Remco Vermeulen | Clarify the required use of Python version 3.9 | +| 0.32.0 | 2024-05-01 | Luke Cartey | Refer to the user manual for the list of supported standards. | +| 0.33.0 | 2024-07-30 | Kristen Newbury | Remove out dated references to codeql modules directory usage. | +| 0.34.0 | 2024-08-22 | Kristen Newbury | Remove out dated references to git submodules usage. | +| 0.35.0 | 2025-01-15 | Mike Fairhurst | Add guidance for the addition of 'strict' queries. | ## Scope of work -A _coding standard_ is a set of rules or guidelines which restrict or prohibit the use of certain dangerous or confusing coding patterns or language features. This repository contains CodeQL queries (and supporting processes) which implement a number of different coding standards. The currently supported standards are: +A *coding standard* is a set of rules or guidelines which restrict or prohibit the use of certain dangerous or confusing coding patterns or language features. This repository contains CodeQL queries (and supporting processes) which implement a number of different coding standards. The currently supported standards are documented in the [user manual](user_manual.md). -| Standard | Version | Total rules | Total supportable rules | Status | Notes | -| -------------------------------------------------------------------------------------------------------------------- | ------- | ----------- | ----------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [AUTOSAR C++](https://www.autosar.org/fileadmin/user_upload/standards/adaptive/20-11/AUTOSAR_RS_CPP14Guidelines.pdf) | 20-11 | 397 | 375 | Implemented | -| [CERT-C++](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-cpp-coding-standard-2016-v01.pdf) | 2016 | 83 | 83 | Implemented | AUTOSAR includes a sub-set of rules take from MISRA C++ 2008, which can be purchased for a small fee from [the MISRA website](https://misra.org.uk/shop). | -| [CERT-C](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-c-coding-standard-2016-v01.pdf) | 2016 | 120 | 99 | In development | The implementation excludes rules not part of 2016, but that are added to the [CERT-C wiki](https://wiki.sei.cmu.edu/confluence/display/c/) | -| [MISRA C](https://www.misra.org.uk/product/misra-c2012-third-edition-first-revision/ ) | 2012 | 172 | 172 | In development | This includes the [MISRA C:2012 Amendment 2](https://www.misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD2.pdf) | - - -Each coding standard consists of a list of "guidelines", however not all the guidelines in all the standards will be amenable to automated static analysis. The AUTOSAR C++ standard categorizes the guidelines according to enforcement by static analysis tools in section _5.1.3 Rule classification according to enforcement by static analysis_ of the standard. The CERT-C++ standard does not provide such categorization, but frequently has a [documented](https://wiki.sei.cmu.edu/confluence/display/cplusplus/How+this+Coding+Standard+Is+Organized#HowthisCodingStandardIsOrganized-AutomatedDetection) automated detection section for guidelines that documents tools, including their limitations, that can verify the guidelines in question. We have therefore carefully reviewed each supported standard. For each guidelines that is not categorized as automatic enforceable we have determined,in conjunction with end users, what parts of the guideline can be supported in which capacity with CodeQL. +Each coding standard consists of a list of "guidelines", however not all the guidelines in all the standards will be amenable to automated static analysis. The AUTOSAR C++ standard categorizes the guidelines according to enforcement by static analysis tools in section *5.1.3 Rule classification according to enforcement by static analysis* of the standard. The CERT-C++ standard does not provide such categorization, but frequently has a [documented](https://wiki.sei.cmu.edu/confluence/display/cplusplus/How+this+Coding+Standard+Is+Organized#HowthisCodingStandardIsOrganized-AutomatedDetection) automated detection section for guidelines that documents tools, including their limitations, that can verify the guidelines in question. We have therefore carefully reviewed each supported standard. For each guidelines that is not categorized as automatic enforceable we have determined,in conjunction with end users, what parts of the guideline can be supported in which capacity with CodeQL. For some of the rules which are not amenable to static analysis, we may opt to provide a query which aids with "auditing" the rules. For example, AUTOSAR includes a rule (A10-0-1) "Public inheritance shall be used to implement 'is-a' relationship.". This is not directly amenable to static analysis, because it requires external context around the concept being modeled. However, we can provide an "audit" rule which reports all the public and private inheritance relationships in the program, so they can be manually verified. +For other rules, there may be means of indicating that a contravention is intentional, and where requiring a _devation report_ may be extra burdensome on developers and require double-entry. These results should be reported under a "strict" query. For instance, `RULE-2-8` "A project should not contain unused object definitions," where adding `__attribute__((unused))` may be preferable in order to suppress compiler warnings (which _deviation reports_ do not do) and are highly indicative of an intentional contravention by a developer. + For each rule which will be implemented with a query we have assigned a "rule package". Rule packages represent sets of rules, possibly across standards, that will be implemented together. Examples of rule packages include "Exceptions", "Naming", "Pointers" and so forth. By implementing queries for related rules together, we intend to maximize the amount of code shared between queries, and to ensure query developers can gain a deep understanding of that specific topic. The canonical list of rules, with implementation categorization and assigned rule packages, are stored in this repository in the `rules.csv` file. @@ -59,8 +64,8 @@ A common use case for the coding standards specified above is to to help in the To support the functional safety use case, the scope of work for this project also includes: - - _Analysis reporting_ - producing reports for functional safety purposes that summarize the findings and highlight any issues during analysis that could compromise the integrity of those findings. - - _Deviations_ - a process for suppressing valid results, and maintaining metadata +- *Analysis reporting* - producing reports for functional safety purposes that summarize the findings and highlight any issues during analysis that could compromise the integrity of those findings. +- *Deviations* - a process for suppressing valid results, and maintaining metadata The requirements for these additional components are taken from the [MISRA Compliance 2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) document. Further details of these use cases can be found in the [user manual](user_manual.md). @@ -68,30 +73,34 @@ The requirements for these additional components are taken from the [MISRA Compl ### Overview - * For each selected rule we will write one or more CodeQL queries that implement the rule (see section _Splitting a rule into multiple queries_). - * Queries will be grouped into CodeQL packs, according to the coding standard the rule comes from. - * To ensure consistency and increase the speed of development, we generate outline query files from the `rules.csv` specification file. - * Where a rule is duplicated across different standards, we will still create separate queries for each standard, but the implementation may be shared between the standards. This allows each version to provide different metadata, and to be enabled/disabled individually. - +- For each selected rule we will write one or more CodeQL queries that implement the rule (see section *Splitting a rule into multiple queries*). +- Queries will be grouped into CodeQL packs, according to the coding standard the rule comes from. +- To ensure consistency and increase the speed of development, we generate outline query files from the `rules.csv` specification file. +- Where a rule is duplicated across different standards, we will still create separate queries for each standard, but the implementation may be shared between the standards. This allows each version to provide different metadata, and to be enabled/disabled individually. + ### Architecture For each supported coding standard we will provide: + 1. A CodeQL query pack containing the queries that implement the designated rules. 2. A CodeQL query pack containing the unit tests ("qltests") for each of the queries. These packs will be organized by supported language. The current supported languages are: + - C++14 standardized by [ISO/IEC 14882:2014](https://www.iso.org/standard/64029.html) located in the directory `cpp`. -- C90 standardized by [ISO/IEC 9899:1990](https://www.iso.org/standard/17782.html), [C99] standardized by [ISO/IEC 9899:1999](https://www.iso.org/standard/29237.html), and C11 standardized by [ISO/IEC 9899:2011](https://www.iso.org/standard/57853.html). All are located in the directory `c`. +- [C99] standardized by [ISO/IEC 9899:1999](https://www.iso.org/standard/29237.html) and C11 standardized by [ISO/IEC 9899:2011](https://www.iso.org/standard/57853.html). All are located in the directory `c`. For each language, we will also include: + 1. A CodeQL query pack containing "common" libraries, which provide support. 2. A CodeQL query pack containing tests for the "common" libraries. The standards packs will depend on the "common" pack for the given language. This will allow the different standards to share implementation libraries. In the repository, this will be organized as follows: -``` + +```text / / src/ @@ -138,9 +147,10 @@ The decision to split a rule into multiple queries should be driven by the follo In order to speed up rule development and ensure implementation consistency we have created a series of scripts that generate templated rule files based on the `rules.csv` rule specification file. This generation process works on a per-rule package basis, and is driven by the creation of a "rule package description file", describing the mapping from rules to queries which will implement those rules. For this, there is a three step process: - 1. Generate a rule package description file for a given rule package. - 2. Review each entry in the rule package description file, updating the names and properties of the queries that will be written to implement these rules. - 3. Generate rule files from the rule package description file for a given rule package. + +1. Generate a rule package description file for a given rule package. +2. Review each entry in the rule package description file, updating the names and properties of the queries that will be written to implement these rules. +3. Generate rule files from the rule package description file for a given rule package. After these scripts have been run each query specified in the rule package description file will have: @@ -153,6 +163,7 @@ These files will be ready for query implementation. #### Step 0: Prepare the Python environment +The tooling standardizes on Python 3.9 and requires the use of version 3.9 to run all tooling. The `scripts` directory contains the pip package specification file `requirements.txt` that contains the dependencies our generation scripts rely upon. The dependencies can be installed as follows: @@ -161,44 +172,45 @@ The dependencies can be installed as follows: pip3.9 install -r scripts/requirements.txt ``` -It is advisable to use a Python virtual environment which needs to be created and activated before installing the dependencies. This can be done as follows: +It is advisable to use a Python 3.9 virtual environment which needs to be created and activated before installing the dependencies. This can be done as follows: ```bash python3.9 -mvenv scripts/.venv . scripts/.venv/bin/activate -pip install -r scripts/requirements.txt +pip3.9 install -r scripts/requirements.txt ``` #### Step 1: Generate rule package description file To generate the rule package description file, run the following script from the root of the repository: -``` -python3.9 scripts/generate_rules/generate_package_description.py +```bash +python3.9 scripts/generate_rules/generate_package_description.py ``` -This will produce a `.json` file in the `rule_packages` directory with the name of the rule package (e.g. `rule_packages/Literals.json`). +This will produce a `.json` file in the `rule_packages` directory with the name of the rule package (e.g. `rule_packages/Literals.json`). For example, `python3.9 scripts/generate_rules/generate_package_description.py c Types` creates `rule_packages/c/Types.json`. #### Step 2: Review and update the rule package description file The rule package description file produced in previous step is a `json` file which has the following structure: - - * A rule package object, with properties for each coding standard. - * A coding standard object, with properties for each implemented rule. - * A rule object, with: - * A `properties` property specifying some key-value pairs describing properties of the rule. - * A `title`s property specifying the rule title (also known as the rule "headline"). - * A `queries` property, specifying an array of query objects - * A query object, with: - * A `description` property, which will be used to populate the `@description` query metadata property value for this query. - * A `kind` property, which will be used to populate the `@kind` query metadata property value for this query. - * A `name` property, which will be used to populate the `@name` query metadata property value for this query. - * A `precision` property, which will be used to populate the `@precision` query metadata property value for this query. - * A `severity` property, which will be used to populate the `@severity` query metadata property value for this query. - * A `short_name` property, which will be used in the filenames for each file generated for this query, most notable as the name of the generated `.ql` query file, as well as the query id. - * A `tags` property, which will be used to populate the `@tags` query metadata property value for this query. + +- A rule package object, with properties for each coding standard. +- A coding standard object, with properties for each implemented rule. +- A rule object, with: + - A `properties` property specifying some key-value pairs describing properties of the rule. + - A `title`s property specifying the rule title (also known as the rule "headline"). + - A `queries` property, specifying an array of query objects +- A query object, with: + - A `description` property, which will be used to populate the `@description` query metadata property value for this query. + - A `kind` property, which will be used to populate the `@kind` query metadata property value for this query. + - A `name` property, which will be used to populate the `@name` query metadata property value for this query. + - A `precision` property, which will be used to populate the `@precision` query metadata property value for this query. + - A `severity` property, which will be used to populate the `@severity` query metadata property value for this query. + - A `short_name` property, which will be used in the filenames for each file generated for this query, most notable as the name of the generated `.ql` query file, as well as the query id. + - A `tags` property, which will be used to populate the `@tags` query metadata property value for this query. For example, this is the first part of the `Exceptions2.json` package file: + ```json { "AUTOSAR": { @@ -233,70 +245,74 @@ The query metadata instructs the CodeQL how to handle the query and display its The `generate_package_description.py` script provides a "best-effort" approach to setting each of the properties. For that reason, the rule package description file must be reviewed and updated. For each rule: - - Review the rule text in the relevant standard, and determine the number of queries - - For each `query` object review and update the following properties: - - `description` - **_must not be empty and end with a full stop_** - will be blank, unless the rule headline was too long to fit in the `name` property, in which case it will contain the rule headline. If the `description` is blank, fill it in explaining _why_ this could be a problem by explaining the consequences (see the CodeQL [metadata descriptions](https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md#query-descriptions-description) documentation for more details). - - `kind` - pre-populated to `problem`. Modify to `path-problem` if this query is likely to use path explanations - for example, to explain data flow path. - - `name` - will be pre-populated the first 100 characters of the rule headline text, truncated at a sensible point. This should be a single sentence, and **_must not end in a full stop_**. - - `precision` - pre-populated based on a "difficulty" column present in the `rules.csv`. Set according to the definition specified in the CodeQL [metadata properties](https://codeql.github.com/docs/writing-codeql-queries/metadata-for-codeql-queries/#metadata-properties) documentation. - - `severity` - will be pre-populated to `error`, but should be adjusted based on the query. The criteria is that if the query does report a true positive - - `error` - if the reported issue is either directly a security vulnerability, or directly causes a bug or crash in the program. - - `warning` - if the reported issue is not an error, but could indirectly lead to a security vulnerability or a bug or crash in the program. - - `recommendation` - if the reported issue is primarily a stylistic or maintainability issue. - - `short_name` - must be a PascalCase string without spaces, which will be used for the name of the query file and to generate a query id. Pre-populated heuristically from from the rule headline text. Make adjustments as appropriate: - - The short name must not exceed 50 characters. - - Consider whether the query can be described more succinctly. For example `OnlyInstancesOfTypesDerivedFromExceptionShouldBeThrown` can be summarized more clearly as `OnlyThrowStdExceptionDerivedTypes`. - - `tags` - Apply at least one tag from the possible values listed below. If you want to use a query that is not listed a new tag can be added through a PR that modifies the possible tag values in the `query` sub-schema located in `schemas/rule-package.schema.json` and updates the list of possible values described below. - - `correctness` - if the query identifies incorrect program behavior. - - `security` - if the query identifies a potential security vulnerability. - - `readability` - if the query identifies an issue which makes the code harder to read. - - `maintainability` - if the query identifies an issue which makes the code harder to maintain. - - `performance` - if the query identifies an issue which has a negative impact on the performance of the code. - - `concurrency` - if the query identifies a concurrency issue. - - Validate the rule package description file using the `validate-rule-package.py` script that validates the rule package descriptions against the schema `rule-package.schema.json` located in the `schemas` directory. - - `python3 scripts/validate-rule-package.py ` - -#### Step 3: +- Review the rule text in the relevant standard, and determine the number of queries +- For each `query` object review and update the following properties: + - `description` - ***must not be empty and end with a full stop*** - will be blank, unless the rule headline was too long to fit in the `name` property, in which case it will contain the rule headline. If the `description` is blank, fill it in explaining *why* this could be a problem by explaining the consequences (see the CodeQL [metadata descriptions](https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md#query-descriptions-description) documentation for more details). + - `kind` - pre-populated to `problem`. Modify to `path-problem` if this query is likely to use path explanations - for example, to explain data flow path. + - `name` - will be pre-populated the first 100 characters of the rule headline text, truncated at a sensible point. This should be a single sentence, and ***must not end in a full stop***. + - `precision` - pre-populated based on a "difficulty" column present in the `rules.csv`. Set according to the definition specified in the CodeQL [metadata properties](https://codeql.github.com/docs/writing-codeql-queries/metadata-for-codeql-queries/#metadata-properties) documentation. + - `severity` - will be pre-populated to `error`, but should be adjusted based on the query. The criteria is that if the query does report a true positive + - `error` - if the reported issue is either directly a security vulnerability, or directly causes a bug or crash in the program. + - `warning` - if the reported issue is not an error, but could indirectly lead to a security vulnerability or a bug or crash in the program. + - `recommendation` - if the reported issue is primarily a stylistic or maintainability issue. + - `short_name` - must be a PascalCase string without spaces, which will be used for the name of the query file and to generate a query id. Pre-populated heuristically from from the rule headline text. Make adjustments as appropriate: + - The short name must not exceed 50 characters. + - Consider whether the query can be described more succinctly. For example `OnlyInstancesOfTypesDerivedFromExceptionShouldBeThrown` can be summarized more clearly as `OnlyThrowStdExceptionDerivedTypes`. + - `tags` - Apply at least one tag from the possible values listed below. If you want to use a query that is not listed a new tag can be added through a PR that modifies the possible tag values in the `query` sub-schema located in `schemas/rule-package.schema.json` and updates the list of possible values described below. + - `correctness` - if the query identifies incorrect program behavior. + - `security` - if the query identifies a potential security vulnerability. + - `readability` - if the query identifies an issue which makes the code harder to read. + - `maintainability` - if the query identifies an issue which makes the code harder to maintain. + - `performance` - if the query identifies an issue which has a negative impact on the performance of the code. + - `concurrency` - if the query identifies a concurrency issue. + - Validate the rule package description file using the `validate-rule-package.py` script that validates the rule package descriptions against the schema `rule-package.schema.json` located in the `schemas` directory. + - `python3.9 scripts/validate-rule-package.py ` + +#### Step 3 Ensure that the repository [codeql-coding-standards-help](https://github.com/github/codeql-coding-standards-help) cloned as a sibling of the [codeql-coding-standards](https://github.com/github/codeql-coding-standards) repository switched to a branch that matches the branch your are working on. To generate the rule package files, run the following script from the root of the repository: -``` +```bash python3.9 scripts/generate_rules/generate_package_files.py ``` If the repository [codeql-coding-standards-help](https://github.com/github/codeql-coding-standards-help) is not cloned as a sibling, then run the script as follows: -``` +```bash python3.9 scripts/generate_rules/generate_package_files.py --external-help-dir ``` After running this script, the following files will be generated in the `//src/rules//` directory: - - A `.ql` query file with the query metadata pre-populated, and the standard imports included. - - A `.md` query help file with some boilerplate text describing the purpose of the query. + +- A `.ql` query file with the query metadata pre-populated, and the standard imports included. +- A `.md` query help file with some boilerplate text describing the purpose of the query. For the standards AUTOSAR and MISRA the help files will generated in the `//src/rules/` directory of the cloned [codeql-coding-standards-help](https://github.com/github/codeql-coding-standards-help) repository if available, otherwise the help file generation is skipped. In addition, the following files will be generated in the `//test/rules//` directory: - - An empty `test.cpp` or `test.c` file. - - A `.qlref` file, which refers to the generated query file. - - A `.expected` file, which contains some boiler plate text. This ensures that when qltest is run, it will not succeed, but it will allow the "Compare results" option in the CodeQL VS Code extension (which is only usually available with an `.expected` results file). + +- An empty `test.cpp` or `test.c` file. +- A `.qlref` file, which refers to the generated query file. +- A `.expected` file, which contains some boiler plate text. This ensures that when qltest is run, it will not succeed, but it will allow the "Compare results" option in the CodeQL VS Code extension (which is only usually available with an `.expected` results file). The script can be safely re-run, except in a few notable cases listed below. Re-running the script has the following effect: - - Overwrites`.qlref` file. - - Updates the autogenerated sections of the `.md` file. - - Touches the `test.cpp`, `test.c`, and `.expected` files, to ensure they exist on disk, but does not modify them if they exist. - - Updates the `.ql` query by overwriting the query metadata block only. The QL portion of the file is left untouched. + +- Overwrites`.qlref` file. +- Updates the autogenerated sections of the `.md` file. +- Touches the `test.cpp`, `test.c`, and `.expected` files, to ensure they exist on disk, but does not modify them if they exist. +- Updates the `.ql` query by overwriting the query metadata block only. The QL portion of the file is left untouched. The notable exceptions are: - - If a `query` object is deleted from the rule package description file, it will not be deleted on disk. - - If a `query` object has the `short_name` property modified in the rule package description file, query files will be created under the new name, but the query files for the old name will not be deleted. + +- If a `query` object is deleted from the rule package description file, it will not be deleted on disk. +- If a `query` object has the `short_name` property modified in the rule package description file, query files will be created under the new name, but the query files for the old name will not be deleted. ### Updating the query from the rule specification Updates to the rule specification require an update of the generated queries files. -As described in _step 3_ of the section _Generation of query templates from rule specifications_ the script `scripts/generate_rules/generate_package_files.py` can be safely re-run with the documented exceptions. +As described in *step 3* of the section *Generation of query templates from rule specifications* the script `scripts/generate_rules/generate_package_files.py` can be safely re-run with the documented exceptions. Each property of a query in the rule specification can be changed and the generated query files can be updated by rerunning the script `scripts/generate_rules/generate_package_files.py` with exception of the property `query.shortname`. Updating the `query.shortname` property is discussed in the next section. @@ -305,52 +321,60 @@ Each property of a query in the rule specification can be changed and the genera Changing the `query.shortname` property requires a manual update process with the following steps. 1. Determine the query who's `query.shortname` property needs to be updated. -2. Change the `query.shortname` value and generate the query files as described in _step 3_ of the section _Generation of query templates from rule specifications_. +2. Change the `query.shortname` value and generate the query files as described in *step 3* of the section *Generation of query templates from rule specifications*. 3. Migrate the query definition (excluding the query meta-data) from the previous query file to the new query file identified with the updated shortname. 4. Migrate the relevant sections from query help file from the previous query help file to the new help query file identified with the updated shortname. 5. Migrate the test case expected file identified by old `.expected` to the update `.expected` name. -6. Validate that the new test case passes by following the procedure described in the section _Running unit tests_. +6. Validate that the new test case passes by following the procedure described in the section *Running unit tests*. 7. Remove the following files with `git rm ` where `query.shortname` reflects the old shortname in the directory `//src/rules//`: - `.ql` - `.md` ### Query style guide -The following list describes the required style guides for a query that **must** be validated during the code-review process described in section _Code review and automated checks_. +The following list describes the required style guides for a query that **must** be validated during the code-review process described in section *Code review and automated checks*. A query **must** include: - - A use of the `isExcluded` predicate on the element reported as the primary location. This predicate ensures that we have a central mechanism for excluding results. This predicate may also be used on other elements relevant to the alert, but only if a suppression on that element should also cause alerts on the current element to be suppressed. - - A well formatted alert message: - - The message should be a complete standalone sentence, with punctuation and a full stop. - - The message should refer to this particular instance of the problem, rather than repeating the generic rule. e.g. "Call to banned function x." instead of "Do not use function x." - - Code elements should be placed in 'single quotes', unless they are formatted as links. - - Avoid value judgments such as "dubious" and "suspicious", and focus on factual statements about the problem. - - If possible, avoid constant alert messages. Either add placeholders and links (using $@), or concatenate element names to the alert message. Non-constant messages make it easier to find particular results, and links to other program elements can help provide additional context to help a developer understand the results. Examples: - - Instead of `Call to banned function.` prefer `Call to banned function foobar.`. - - Instead of `Return value from call is unused.` prefer `Return value from call to function [x] is unused.`, where `[x]` is a link to the function itself. - - Do not try to explain the solution in the message; instead that should be provided in the help for the query. + +- A use of the `isExcluded` predicate on the element reported as the primary location. This predicate ensures that we have a central mechanism for excluding results. This predicate may also be used on other elements relevant to the alert, but only if a suppression on that element should also cause alerts on the current element to be suppressed. +- A well formatted alert message: + - The message should be a complete standalone sentence, with punctuation and a full stop. + - The message should refer to this particular instance of the problem, rather than repeating the generic rule. e.g. "Call to banned function x." instead of "Do not use function x." + - Code elements should be placed in 'single quotes', unless they are formatted as links. + - Avoid value judgments such as "dubious" and "suspicious", and focus on factual statements about the problem. + - If possible, avoid constant alert messages. Either add placeholders and links (using $@), or concatenate element names to the alert message. Non-constant messages make it easier to find particular results, and links to other program elements can help provide additional context to help a developer understand the results. Examples: + - Instead of `Call to banned function.` prefer `Call to banned function foobar.`. + - Instead of `Return value from call is unused.` prefer `Return value from call to function [x] is unused.`, where `[x]` is a link to the function itself. + - Do not try to explain the solution in the message; instead that should be provided in the help for the query. All public predicates, classes, modules and files should be documented with QLDoc. All QLDoc should follow the [QLDoc style guide](https://github.com/github/codeql/blob/main/docs/qldoc-style-guide.md). +### Installing QL dependencies + +All of our query and library packs depend on the standard CodeQL library for C++, `codeql/cpp-all`. This dependency is specified in the `qlpack.yml` file for each of our packs. Before compiling, running, or testing any of our queries or libraries, you must download the proper dependencies by running `python3.9 scripts/install-packs.py`. This will download the appropriate version of the standard library from the public package registry, installing it in a cache in your `~/.codeql` directory. When compiling queries or running tests, the QL compiler will pick up the appropriate dependencies from this cache without any need to specify an additional library search path on the command line. + +Because the downloaded packs are cached, it is only necessary to run `install-packs.py` once each time we upgrade to a new standard library version. It does not hurt to run it more often; if all necessary packs are already in the download cache, then it will complete quickly without trying to download anything. + ### Unit testing Every query which implements a rule **must** include: -- One or more unit tests. -- One or more unit tests for every non-trivial library. -- For each unit test both "compliant" and "non-compliant" test cases, and should exercise each different logical condition uniquely provided in the query, where possible within the testing framework. The scope of each test should be those conditions specific to this query. In particular, functionality provided by the CodeQL Standard Library for C++ does not need to be tested. + +- One or more unit tests. +- One or more unit tests for every non-trivial library. +- For each unit test both "compliant" and "non-compliant" test cases, and should exercise each different logical condition uniquely provided in the query, where possible within the testing framework. The scope of each test should be those conditions specific to this query. In particular, functionality provided by the CodeQL Standard Library for C++ does not need to be tested. #### Running unit tests During query development in VS Code, the unit tests can be run using the [testing features](https://codeql.github.com/docs/codeql-for-visual-studio-code/testing-codeql-queries-in-visual-studio-code/) in the CodeQL extension. Unit tests can also be run on the command line using the CodeQL CLI. With an appropriate CodeQL CLI (as specified in the `supported_codeql_configs.json` at the root of the repository), you can run the following from the root of the repository: -``` -codeql test run --show-extractor-output --search-path . path/to/test/directory + +```bash +codeql test run --show-extractor-output path/to/test/directory ``` -* `--show-extractor-output` - this shows the output from the extractor. It is most useful when the test fails because the file is not valid C++, where the extractor output will include the compilation failure. This is not shown in VS Code. -* `--search-path .` - this allows the CodeQL CLI to discover all the QL packs within our repository. -* `path/to/test/directory` - this can be a qlref file (like `cpp/autosar/test/rules/A15-2-2/`), a rule directory (`cpp/autosar/test/rules/A15-2-2/`) or a test qlpack (`cpp/autosar/test/`). +- `--show-extractor-output` - this shows the output from the extractor. It is most useful when the test fails because the file is not valid C++, where the extractor output will include the compilation failure. This is not shown in VS Code. +- `path/to/test/directory` - this can be a qlref file (like `cpp/autosar/test/rules/A15-2-2/`), a rule directory (`cpp/autosar/test/rules/A15-2-2/`) or a test qlpack (`cpp/autosar/test/`). For more details on running unit tests with the CodeQL CLI see the [Testing custom queries](https://codeql.github.com/docs/codeql-cli/testing-custom-queries/) help topic. @@ -358,27 +382,31 @@ For more details on running unit tests with the CodeQL CLI see the [Testing cust The C++ test cases **must** be formatted with `clang_format`. - - Test functions should be called `test_`, where `` is a brief description of this test case. +- Test functions should be called `test_`, where `` is a brief description of this test case. If possible, use meaningful names for elements in test cases. Where arbitrary names are required, you may use the following: - - Local variables should be named `l`, with i incremented for each new variable. - - Global variables should be named `g`, with i incremented for each new variable. - - Functions should be named `f`, with i incremented for each new variable. - - Member variables should be named `m`, with i incremented for each new variable. +- Local variables should be named `l`, with i incremented for each new variable. +- Global variables should be named `g`, with i incremented for each new variable. +- Functions should be named `f`, with i incremented for each new variable. +- Member variables should be named `m`, with i incremented for each new variable. Test cases **must** be annotated with a line-ending comment in this format: -``` + +```regexp (COMPLIANT(\[FALSE_POSITIVE\])?|NON_COMPLIANT(\[FALSE_NEGATIVE\])?)( - .*)? ``` + Where: - - `COMPLIANT` is added if the line represents a "compliant" test case - - The annotation `[FALSE_POSITIVE]` is added if the query currently reports this result. - - `NON_COMPLIANT` is chosen if the line represents a non-compliant test case - - The annotation `[FALSE_NEGATIVE]` is added if the query currently does not report this result. + +- `COMPLIANT` is added if the line represents a "compliant" test case + - The annotation `[FALSE_POSITIVE]` is added if the query currently reports this result. +- `NON_COMPLIANT` is chosen if the line represents a non-compliant test case + - The annotation `[FALSE_NEGATIVE]` is added if the query currently does not report this result. For example: -``` + +```cpp "\s"; // NON_COMPLIANT[FALSE_NEGATIVE] "\n"; // COMPLIANT "\U00000024"; // COMPLIANT[FALSE_POSITIVE] @@ -387,11 +415,12 @@ For example: #### Copying test code Like the `github/codeql` repository, the contents of our test files should not be copied from external sources (third-party code, personal projects, standard libraries). The only exceptions to this rule are the copying of declarations from: - - [ISO/IEC Programming languages - C](https://www.iso.org/standard/74528.html) (all versions) - - [ISO/IEC Programming languages - C++](https://www.iso.org/standard/68564.html) (all versions) - - Code from existing queries and tests in the `github/codeql` repository. - - Code from existing queries and tests in this repository. - - Code in the public domain + +- [ISO/IEC Programming languages - C](https://www.iso.org/standard/74528.html) (all versions) +- [ISO/IEC Programming languages - C++](https://www.iso.org/standard/68564.html) (all versions) +- Code from existing queries and tests in the `github/codeql` repository. +- Code from existing queries and tests in this repository. +- Code in the public domain This policy is based on the public policy for `github/codeql` as specified at [github/codeql: C++ Unit Tests - Copying code](https://github.com/github/codeql/blob/main/cpp/ql/test/README.md#copying-code). @@ -407,8 +436,8 @@ We have therefore implemented a partial "stub" standard library in the `cpp/comm Each proposed changed to `main` or a release branch is required to go through a code review process. This involves: - - A review and explicit approval by at least one other team member with "Write" access to the repository. - - Running automated checks that validate and verify the change and ensuring they pass. +- A review and explicit approval by at least one other team member with "Write" access to the repository. +- Running automated checks that validate and verify the change and ensuring they pass. This is implemented by requiring that proposed changes are submitted as pull requests to the GitHub repository hosting the queries, and is enforced by enabling GitHub [branch protection](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches) policies on the `main` and the release branches. @@ -418,10 +447,10 @@ An approving review and a "passing" state from every "Required" automated check The following automated checks are run on every push and pull request to `main` and to the release branches: - * Running the CodeQL Coding Standard unit tests against supported CodeQL CLIs and CodeQL Standard Libraries for C++. - * Validating that release artifacts can be created for that branch. - * Validating style rules for queries and test files. - * Confirming that the query help files are valid. +- Running the CodeQL Coding Standard unit tests against supported CodeQL CLIs and CodeQL Standard Libraries for C++. +- Validating that release artifacts can be created for that branch. +- Validating style rules for queries and test files. +- Confirming that the query help files are valid. These automated checks should pass before the pull request is merged. @@ -438,18 +467,22 @@ For proposed changes that modify the released artifacts an entry must be include For proposed changes which only add new queries or support for new rules, this process is fully automated, by reviewing differences in rule package metadata files between releases. For proposed changes which change: - - The structure or layout of the release artifacts. - - The evaluation performance (memory, execution time) of an existing query. - - The results of an existing query. -A _change note_ must be added to the `change_notes` directory. The format of the change notes is to create a file with a name matching the following pattern: -``` +- The structure or layout of the release artifacts. +- The evaluation performance (memory, execution time) of an existing query. +- The results of an existing query. + +A *change note* must be added to the `change_notes` directory. The format of the change notes is to create a file with a name matching the following pattern: + +```bash YYYY-MM-DD-short-name-for-issue.md ``` + For example `2021-06-29-remove-incompatibility-codeql-cli-2.5.6.md`. The contents of the file should be a markdown list (using `-`) with a user facing message specifying the nature of the change. If the changes relate to specific queries, then the top-level entry should specify the rule and query, and should provide a nested list of the changes. For example: -``` + +```md - `A12-8-6` - `CopyAndMoveNotDeclaredProtected.ql`: - Fixed issue #174 - a result is now only reported when the declaring class is either used as a base class in the database, or where the class is abstract. - Fixed a bug where exclusions did not apply to invalid assignment operators. @@ -460,61 +493,90 @@ The contents of the file should be a markdown list (using `-`) with a user facin ### External dependencies There are two external dependencies required for running the coding standards queries: - 1. The CodeQL CLI, the command line tool for building CodeQL databases and running queries over those databases. - 2. The CodeQL Standard Library + +1. The CodeQL CLI, the command line tool for building CodeQL databases and running queries over those databases. +2. The CodeQL Standard Library For the purpose of this repository, and any tool qualification, we consider these external dependencies to be "black boxes" which require verification when upgrading. -To (a) clearly specify the supported versions of these external dependencies and to (b) enable automation around them, the repository contains a `supported_codeql_configs.json` which lists the sets of supported configurations. There are four fields: +To (a) clearly specify the supported versions of these external dependencies and to (b) enable automation around them, the repository contains a `supported_codeql_configs.json` which lists the sets of supported configurations under the `supported_environments` property. There are three fields: - * `codeql_cli` - this is the plain version number of the supported CodeQL CLI, e.g. `2.6.3`. - * `codeql_standard_library` - this is the name of a tag on the `github.com/github/codeql` repository. The tag should be compatible with the CodeQL CLI given above. For an enterprise release compatible with LGTM an `lgtm/v` should be chosen. For CodeQL CLI releases which are not tied to an enterprise release we would typically use `codeql-cli/v`, although any tag which is compatible is allowed. - * `codeql_cli_bundle` - (optional) - if present, describes the CodeQL CLI bundle version that is compatible. The bundle should include precisely the CodeQL CLI version and CodeQL Standard Library versions specified in the two mandatory fields. - * `ghes` - (optional) - if present describes the GitHub Enterprise Server release whose integrated copy of the CodeQL Action points to the CodeQL CLI bundle specified in the `codeql_cli_bundle` field. +- `codeql_cli` - this is the plain version number of the supported CodeQL CLI, e.g. `2.6.3`. +- `codeql_standard_library` - this is the name of a tag on the `github.com/github/codeql` repository. The tag should be compatible with the CodeQL CLI given above. This would typically use the `codeql-cli/v` tag for the release, although any tag which is compatible is allowed. +- `codeql_cli_bundle` - (optional) - if present, describes the CodeQL CLI bundle version that is compatible. The bundle should include precisely the CodeQL CLI version and CodeQL Standard Library versions specified in the two mandatory fields. #### Upgrading external dependencies To upgrade the CodeQL external dependencies: - 1. Determine appropriate versions of the CodeQL CLI and `github/codeql` repository, according to the release schedule and customer demands. - 2. Determine if there is a compatible CodeQL CLI bundle version by looking at the releases specified at https://github.com/github/codeql-action/releases. The bundle always includes the standard library at the version specified by the `codeql-cli/v` tag in the `github/codeql` repository. - 3. If you find a compatible CodeQL CLI bundle, determine whether that bundle was released in a GitHub Enterprise server release, by inspecting the `defaults.json` file at https://github.com/github/codeql-action/blob/main/lib/defaults.json#L2 for the CodeQL Action submitted with - 4. Populated the `supported_codeql_configs.json` file with the given values, ensuring to delete the optional fields if they are not populated. - 5. Update the `codeql_modules/codeql` submodule pointer to the `codeql_standard_library` tag identified. - 6. Submit a Pull Request to the `github/codeql-coding-standards` repository with the title `Upgrade `github/codeql` dependency to `. Use this template for the description, filling : - ``` - This PR updates the `supported_codeql_configs.json` file to target: - - - CodeQL CLI - - CodeQL Standard Library - - GHES - - CodeQL CLI Bundle - - and GitHub Enterprise Server > - - - ## CodeQL dependency upgrade checklist: - - - [ ] Reformat our CodeQL using the latest version (if required) - - [ ] 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 - ``` - 7. Follow the dependency upgrade checklist, confirming each step. The `.github/workflows/standard_library_upgrade_tests.yml` will trigger automation for running the `github/codeql` unit tests with the appropriate CLI version. - 8. Once all the automate tests have passed, and the checklist is complete, the PR can be merged. - 9. An internal notification should be shared with the development team. +1. Determine appropriate versions of the CodeQL CLI and `github/codeql` repository, according to the release schedule and customer demands. +2. Determine if there is a compatible CodeQL CLI bundle version by looking at the releases specified at [CodeQL Action releases](https://github.com/github/codeql-action/releases). The bundle always includes the standard library at the version specified by the `codeql-cli/v` tag in the `github/codeql` repository. + +If all components are being upgraded to a consistent veresion (e.g. CodeQL CLI v2.15.5, with `github/codeql` tag `codeql-cli/v2.15.5` and bundle `codeql-cli-bundle-v2.15.5`) then the following process can be used: + +1. Run the [upgrade_codeql_dependencies.yml](./github/workflows/upgrade_codeql_dependencies.yml) workflow, with the plain version number, e.g. `2.15.5`. This will: + - Download the specified version of the CodeQL CLI + - Run the [upgrade-codeql-dependencies.py](scripts/release/upgrade-codeql-dependencies.py) script, which + - Validates the version selected exists in all relevant places + - Updates the `supported_codeql_configs.json` file. + - Updates each `qlpack.yml` in the repository with an appropriate value for the `codeql/cpp-all` pack, consistent with the selected CodeQL CLI version. + - Updates each `codeql-lock.yml` file to upgrade to the new version. +2. Follow the dependency upgrade checklist, confirming each step. The `.github/workflows/standard_library_upgrade_tests.yml` will trigger automation for running the `github/codeql` unit tests with the appropriate CLI version. +3. Once all the automate tests have passed, and the checklist is complete, the PR can be merged. +4. An internal notification should be shared with the development team. + +If the upgrade is of mismatched versions you will need to manually create the upgrade following this process: + +1. Populate the `supported_codeql_configs.json` file with the given values, ensuring to delete the optional fields if they are not populated. +2. Submit a Pull Request to the `github/codeql-coding-standards` repository with the title `Upgrade `github/codeql` dependency to `. Use this template for the description, filling: + + ```md + This PR updates the `supported_codeql_configs.json` file to target CodeQL CLI . + + ## 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 + ``` + +3. Follow the dependency upgrade checklist, confirming each step. The `.github/workflows/standard_library_upgrade_tests.yml` will trigger automation for running the `github/codeql` unit tests with the appropriate CLI version. +4. Once all the automate tests have passed, and the checklist is complete, the PR can be merged. +5. An internal notification should be shared with the development team. + ### Release process -#### Version Numbering +The release process is a combination of release specific Action workflows and validation Action workflows executed on each PR. +The flowchart below provides an overview of the release process and how the release specific Action workflows are related. + +```mermaid +flowchart TD; + prepare-release["Prepare release (prepare-release.yml)"] + validate-release["Validate release (validate-release.yml)"] + compiler-validation["Compiler tests (release-engineering/release-compiler-validation.yml.)"] + performance-testing["Performance testing (release-engineering/release-performance-testing.yml)"] + existing-checks["Existing checks run on each PR"] + update-release["Update release (update-release.yml)"] + finalize-release["Finalize release (finalize-release.yml)"] + + prepare-release-->validate-release + validate-release-->compiler-validation-->update-release + validate-release-->performance-testing-->update-release + prepare-release-->existing-checks-->update-release + update-release-->finalize-release +``` + +#### Version Numbering -Version numbers follow semantic versioning and adhere to the following guidelines specific to Coding Standards. +Version numbers follow semantic versioning and adhere to the following guidelines specific to Coding Standards. Given the version `..`: 1. If the release only fixes bugs, increment the `PATCH` number only. -2. If a release contains additional queries, increment the `MINOR` version number and set the `PATCH` number to 0. Note this may also contain fixes in addition to new queries. +2. If a release contains additional queries, increment the `MINOR` version number and set the `PATCH` number to 0. Note this may also contain fixes in addition to new queries. 3. Otherwise, if the release contains breaking changes such as removing queries, increment the `MAJOR` version number and set `MINOR` and `PATCH` to zero. #### Release management @@ -523,64 +585,58 @@ We use the "Releases" feature in GitHub to manage and track our releases. This p To simplify the process of generating the release information, the repository contains a number of scripts and Action workflows: - - [`generate_release_notes.py`](../scripts/release/generate_release_notes.py) - a script for generating release notes based on the contents of the repository in comparison to the previous release. - - [`create_draft_release.sh`](../scripts/release/create_draft_release.sh) - a script for creating a release by: - 1. Downloading the appropriate artifacts - 2. Generating the release notes by calling `generate_release_notes.py` with appropriate parameters - 3. Generating the list of supported rules - 4. Creating a draft release on GitHub containing the artifacts from the previous steps - 5. Triggering integration testing on the new release. - - [`create-draft-release.yml`](../.github/workflows/create-draft-release.yml) - a GitHub Actions workflow for running the `create_draft_release.sh` on demand within the CI/CD environment. +- [prepare-release.yml](./github/workflows/prepare-release.yml): The entry point for starting a new release. When provided with a version and a Git reference this workflow will + - Create a release branch. + - Create a release PR that will contain all the changes required for a release and will validate the release using checks. + - Create a draft release that will be updated during various stages of the release. +- [update-release.yml](./github/workflows/update-release.yml): This workflow will update the draft release when all checks have passed successfully on the release PR. The draft release is updated to: + - Have the most recent release notes as generated by the [update-release-notes.py](scripts/release/update-release-notes.py) script. + - Have the most recent release assets as generated by the [update-release-assets.py](scripts/release/update-release-assets.py). +- [finalize-release.yml](.github/workflows/finalize-release.yml): This will update the release tag and mark the release public when the release PR is merged to successfully conclude the release. +- [update-release-status.yml](.github/workflows/update-release-status.yml): This workflow will update the status on the release by monitoring the status of individual validation steps. When all succeeded this will invoke the `update-release.yml` workflow. +- [update-check-run.yml](.github/workflows/update-check-run.yml): Utility workflow that allow authorized external workflows (i.e., workflows in other repositories) to update the status of check runs in the coding standards repository. +- [validate-release.yml](.github/workflows/validate-release.yml): Utility workflow that will start the performance and compiler compatibility testing that are orchestrated from the codeql-coding-standards-release-engineering repository. #### Branching workflow -Each new major or minor release should have a dedicated release branch, with the name `rc/.`. A new patch version should re-use the existing release branch for the release that is being patched. +Each release should have a dedicated release branch, with the name `rc/..`. A new patch version should branch from the existing release branch for the release that is being patched. Ensure that the same release branch is created in the [codeql-coding-standards-help](https://github.com/github/codeql-coding-standards-help) repository. -#### Artifact creation +#### Release assets -There is an automated CI/CD job ([Code Scanning Query Pack Generation](../.github/workflows/code-scanning-pack-gen.yml)) provided that generates the following release artifacts for Coding Standards: +There is an automated CI/CD job ([Update Release](../.github/workflows/update-release.yml)) that will automatically generate the release assets according to the [release layout specification](scripts/release/release-layout.yml). +Among the assets are: - - LGTM query pack - generates a query pack that can be deployed to LGTM. - - Code Scanning query pack - generates a zipped folder that can be used with the CodeQL CLI directly, or with GitHub Advanced Security. +- Certification kit containing the proof obligations for ISO26262 certification. +- Code Scanning query packs that can be used with the CodeQL CLI directly, or with GitHub Advanced Security. -**Use of LGTM and GitHub Advanced Security is not in scope for ISO 26262 tool qualification. See [user_manual.md#github-advanced-security-and-lgtm](user_manual.md#github-advanced-security-and-lgtm) for more information**. - -These run on every push to `main` and `rc/*`, and on every pull request, and are releasable without modification, assuming all other status checks succeed on the same commit. +**Use of Code Scanning within GitHub Advanced Security is not in scope for ISO 26262 tool qualification. See [user_manual.md#github-advanced-security](user_manual.md#github-advanced-security) for more information**. #### Creating a release +**NOTE**: If this is a hotfix release, make sure to invoke `prepare-release.yml` with `hotfix` set to `true`. + To create a new release: - 1. Create an internal "release checklist" issue. - 2. Determine the appropriate release version. Version numbers are generated + + 1. Determine the appropriate release version. Version numbers are generated according to the guidelines in the section "Version Numbering." - 3. If a new `MAJOR` version is necessary, create a new `rc/.0` branch off of `main`. Otherwise, reuse the existing `rc` branch and merge work from `main` into the `rc` branch you have selected. - 4. Ensure the same `rc` branch exists in the [codeql-coding-standards-help](https://github.com/github/codeql-coding-standards-help) repository. This branch will be used to include external help files. - 5. Submit a PR to update the `qlpack.yml` version numbers on the `main` branch to the next anticipated release. - 6. Submit a PR to update the `qlpack.yml` version numbers on the release branch to the new version. - 7. Trigger a [workflow dispatch event](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) for the [Create draft release](../.github/workflows/create-draft-release.yml) workflow, specifying the release branch. The output of this workflow should report a link to the draft release and a link to the integration testing workflow triggered for this release. - - In the event the workflow is unusable, the [`create_draft_release.sh`](../scripts/release/create_draft_release.sh) script can be run directly on a local machine. - 8. Run the following workflows with the new version number, e.g., `v2.0.0`: - - [Test Linux/x86_64](https://github.com/github/codeql-coding-standards-release-engineering/actions/workflows/test-release-performance-linux-x86_64.yml) - - [Test Windows/x86_64](https://github.com/github/codeql-coding-standards-release-engineering/actions/workflows/test-release-performance-windows-x86_64.yml) - - [Regenerate Performance Views](https://github.com/github/codeql-coding-standards-release-engineering/actions/workflows/regenerate-performance-views.yml) - 9. Confirm the integration testing workflow completes successfully, and that the execution time is comparable to previous releases, taking into account that the execution time is expected to increase proportionally as more queries are added for each release. Results may be viewed on the release engineering repo: https://github.com/github/codeql-coding-standards-release-engineering - 10. For release 1.0.0 and above, the integration testing results must be verified. For each "integration testing codebase": - - Download the SARIF result file - - Compare the results against the previously computed set of results for that integration testing codebase, and, for any new or changed results, spot check to confirm validity. - - For false positives and false negatives identified during this process issues should be opened on this repository to track the problems identified. - - For each issue opened, assess whether they are "significant" i.e. whether they are likely to cause problems in practice with customers. If so, consider Step 7. failed. - 11. If the release fails steps 7. or 8. (if applicable), retain the draft release, and rename it to `vminor.major.patch-rc`. Address the release blocking issues on the `rc/.` branch, and restart the release process at Step 7. - 12. If steps 7. and 8. (if applicable) succeeded, then the release can be marked as "published". - 13. Release artifacts can now be distributed to customers. - 14. Create an internal "release retrospective" issue, and document any pain points or other issues. - 15. Create a PR that merges the release candidate branch into `main`. + 2. Determine the appropriate [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References) to base the new release on. For new major or minor releases, this will be `main`. For patch releases this will be the release branch that is patched. + 3. Trigger a [workflow dispatch event](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) for the [Prepare CodeQL Coding Standards release](../.github/workflows/prepare-release.yml) workflow, specifying the release version for the input `version` and the Git reference for the input `ref`, and `hotfix` with the value `true` **if** it is a hotfix release. + 4. Validate the compiler and performance results linked from their respective check runs in the PR's checks overview. + 1. Validate the performance results by ensuring the release performance doesn't regresses from the previous release by more than a factor of 2 without a good reason. + 2. Validate the compiler results by ensuring there is an acceptable number of compatibility issues. + 5. Merge the PR that is created for the release, named `Release v..` where ``, ``, and `` match with the input `version` of the workflow [Prepare CodeQL Coding Standards release](../.github/workflows/prepare-release.yml) triggered in the previous step. + 6. Merge the PRs for the performance and compiler validation results on the release engineering repository. + +The release automation consists of many test and validation steps that can fail. These can be addressed and the release can be restarted from step 3. +A restart of a release (i.e., calling `prepare-release.yml`) **WILL RECREATE THE EXISTING RELEASE BRANCH AND RELEASE PR**. Any additional changes added to the PR **MUST** be reapplied. +If a release has been marked public, the release can no longer be restarted or re-released without removing the release manually. ## False Positive Triage Rubric -When triaging issues in Coding Standards, please refer to the following rubric for making classifications. +When triaging issues in Coding Standards, please refer to the following rubric for making classifications. -**Impact** +### Impact | Level | Definition | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -588,7 +644,7 @@ When triaging issues in Coding Standards, please refer to the following rubric f | Impact-Medium | Issue occurs in production code bases with relatively low to moderate frequency. Issue may or may not be considered disruptive to customer. | | Impact-Low | Issue may not occur in production code bases and may require hand crafted examples to surface. If the issue occurs in production code bases it occurs either infrequently or impacts only a few codebases. | -**Difficulty** +### Difficulty | Level | Definition | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -609,20 +665,19 @@ Requirements and project planning are maintained separately within an internal r ### Purpose ot the `next` branch -This git repository also has a [`next` branch](https://github.com/github/codeql-coding-standards/tree/next). The purpose of this branch is to track changes that that will become necessary when upgrading the CodeQL external dependencies as described in section _Upgrading external dependencies_. The changes on the `next` branch will undergo only light reviewing. As such, a full review as described in section _Code review and automated checks_ is required when merging these changes into `main`; no releases should be made from the `next` branch. We aim to ensure that the changes on the `next` branch are as complete as possible so that merging into `main` will be straightforward. +This git repository also has a [`next` branch](https://github.com/github/codeql-coding-standards/tree/next). The purpose of this branch is to track changes that that will become necessary when upgrading the CodeQL external dependencies as described in section *Upgrading external dependencies*. The changes on the `next` branch will undergo only light reviewing. As such, a full review as described in section *Code review and automated checks* is required when merging these changes into `main`; no releases should be made from the `next` branch. We aim to ensure that the changes on the `next` branch are as complete as possible so that merging into `main` will be straightforward. ## Task Automation -In the `.vscode` directory this repository comes with a `tasks.json` file which automates some of the tasks described in this document. To access them, in VSCode use `Ctrl+Shift+P` and select `Run Task`. +In the `.vscode` directory this repository comes with a `tasks.json` file which automates some of the tasks described in this document. To access them, in VSCode use `Ctrl+Shift+P` and select `Run Task`. Available Tasks: 1. 🔥 Standards Automation: Initialize: Sets up your Python environment. -2. 📏 Standards Automation: Generate Rule Description File: Generates the rule description file for a package. -3. 📦 Standards Automation: Generate Package Files: Re/generates the files for a package. This command will remember your last arguments so you can just do `Rerun Last Task` in vscode unless you wish to change the arguments. +2. 📏 Standards Automation: Generate Rule Description File: Generates the rule description file for a package. +3. 📦 Standards Automation: Generate Package Files: Re/generates the files for a package. This command will remember your last arguments so you can just do `Rerun Last Task` in vscode unless you wish to change the arguments. 4. 📝 Standards Automation: Format CodeQL: Formats the current file with the codeql formatter. -5. ⚡ Standards Automation: Generated Expected Output: Generates the expected output from the current `.qlref` file in your `tests/` directory. - +5. ⚡ Standards Automation: Generated Expected Output: Generates the expected output from the current `.qlref` file in your `tests/` directory. ## Cookbook @@ -668,7 +723,6 @@ ls cpp/cert/src/$(cat cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDecl # Run a test. See # https://github.com/github/codeql-coding-standards/blob/main/development_handbook.md#unit-testing codeql test run --show-extractor-output \ - --search-path . \ cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.qlref # Get a db error? Applying the recommended fix @@ -686,20 +740,9 @@ codeql test run --show-extractor-output \ # If the expected output is not yet present, it is printed as a diff: mv cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.expected foo -codeql test run --show-extractor-output --search-path . \ +codeql test run --show-extractor-output \ cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.qlref # The actual output can be accepted via codeql test accept (which moves some files): codeql test accept \ cpp/cert/test/rules/EXP52-CPP/DoNotRelyOnSideEffectsInDeclTypeOperand.qlref - - -``` - -### Troubleshooting: Unrecoverable mismatch between extractor and library dbschemes - -The following error could be indicative of the Git submodule _codeql-coding-standards/github_modules_ being out-of-date: - ->Could not upgrade the dataset in /path/to/codeql-coding-standards/cpp/autosar/test/rules/...: Unrecoverable mismatch between extractor and library dbschemes. - -To resolve the problem, update the submodule by executing `git submodule update`. diff --git a/docs/iso_26262_tool_qualification.md b/docs/iso_26262_tool_qualification.md index 9240ecefc9..53b5a4303d 100644 --- a/docs/iso_26262_tool_qualification.md +++ b/docs/iso_26262_tool_qualification.md @@ -11,6 +11,8 @@ | 0.3.0 | 2021-09-08 | Luke Cartey | Update the customer table. | | 0.4.0 | 2021-09-19 | Luke Cartey | Add more detail on approach to V&V. Update section around increased confidence from use. | | 0.5.0 | 2021-11-29 | Remco Vermeulen | Add document management section. | +| 0.6.0 | 2023-08-14 | Luke Cartey | Update use and testing statement after LGTM.com deprecation. | +| 0.7.0 | 2024-07-23 | Luke Cartey | Fix development handbook link | ## Introduction @@ -59,7 +61,7 @@ For the CodeQL Coding Standard queries, we intend to apply the following qualifi #### 1b. Evaluation of the tool development process in accordance with 11.4.8 -The development process is described in the [development handbook](../development_handbook.md). +The development process is described in the [development handbook](development_handbook.md). The project planning and requirements processes are described in our internal repository. @@ -79,7 +81,7 @@ In combination, these techniques ensure that the tool complies with the requirem - Rule review with subject matter experts ensures our interpretation of the rule is appropriate in uncertain cases. - Real world testing and external feedback ensures the interpretation of the rule is producing appropriate and reasonable results on real world code. -The development processes related to validation and verification are described in detail the [development handbook](../development_handbook.md). +The development processes related to validation and verification are described in detail the [development handbook](development_handbook.md). ### Qualification methods for CodeQL CLI and the CodeQL Standard Library for C++ @@ -100,9 +102,9 @@ The CodeQL CLI and CodeQL Standard Library for C++ are extensively used by both The versions of the CodeQL CLI and CodeQL Standard Library for C++ are identical to those shipped to both customers and open source users, and the use cases are comparable. -In terms of breadth of use, between the 4th September 2021 and 7th September 2021 11,788 open source C/C++ repositories were successfully analyzed on [LGTM.com](https://lgtm.com), a platform provided by GitHub for performing analysis of open source repositories[^1] using CodeQL. Each version of the CodeQL CLI and CodeQL Standard Library for C++ version will undergo similarly broad testing on LGTM.com before being adopted by the CodeQL Coding Standards. +In terms of breadth of use, between the 4th September 2021 and 7th September 2021 11,788 open source C/C++ repositories were successfully analyzed on [LGTM.com](https://lgtm.com), a platform provided by GitHub for performing analysis of open source repositories[^1] using CodeQL. In addition, at that time we also analyzed a further 748 C++ repos using CodeQL via the "Code Scanning" feature included in GitHub.com. This includes both private closed source and open source software. -In addition to testing on LGTM.com, we have also analyzed a further 748 C++ repos using CodeQL via the "Code Scanning" feature included in GitHub.com. This includes both private closed source and open source software. +Each version of the CodeQL CLI and CodeQL Standard Library for C++ version will undergo similarly broad testing on GitHub.com before being adopted by the CodeQL Coding Standards. In addition, the following companies have publicly described their use of CodeQL for C++: diff --git a/docs/user_manual.md b/docs/user_manual.md index 339d3e26b0..f4449082c7 100644 --- a/docs/user_manual.md +++ b/docs/user_manual.md @@ -17,19 +17,36 @@ | 0.9.0 | 2022-02-17 | Remco Vermeulen | Finalize scope deviation records | | 0.10.0 | 2022-02-28 | Remco Vermeulen | Describe database correctness in the Hazard and Risk Analysis (HARA). | | 0.11.0 | 2022-02-28 | Remco Vermeulen | Updated version to 1.1.0 | +| 0.12.0 | 2022-10-21 | Luke Cartey | Updated version to 2.10.0 | +| 0.13.0 | 2022-11-03 | Remco Vermeulen | Add missing deviation analysis report tables to section 'Producing an analysis report'. | +| 0.14.0 | 2022-11-03 | Remco Vermeulen | Add guideline recategorization plan. | +| 0.15.0 | 2023-05-24 | Mauro Baluda | Clarify AUTOSAR C++ supported versions. | +| 0.16.0 | 2023-07-03 | Luke Cartey | Remove reference to LGTM, update the name of the query pack | +| 0.17.0 | 2023-08-16 | Luke Cartey | Update list of supported compiler configurations. | +| 0.18.0 | 2024-01-30 | Luke Cartey | Update product description and coverage table. | +| 0.19.0 | 2024-02-23 | Remco Vermeulen | Clarify the required use of Python version 3.9. | +| 0.20.0 | 2024-02-23 | Remco Vermeulen | Add table describing the permitted guideline re-categorizations. | +| 0.21.0 | 2024-05-01 | Luke Cartey | Add MISRA C++ 2023 as under development, and clarify MISRA C 2012 coverage. | +| 0.22.0 | 2024-10-02 | Luke Cartey | Add MISRA C 2023 as under development, and clarify MISRA C 2012 coverage. | +| 0.23.0 | 2024-10-21 | Luke Cartey | Add assembly as a hazard. | +| 0.24.0 | 2024-10-22 | Luke Cartey | Add CodeQL packs as a usable output, update release artifacts list. | +| 0.25.0 | 2025-01-15 | Mike Fairhurst | Add guidance for the usage of 'strict' queries. | +| 0.26.0 | 2025-02-12 | Luke Cartey | Describe support for new deviation code identifier formats | +| 0.27.0 | 2025-05-15 | Luke Cartey | Documented completed support for MISRA C 2023. | ## Release information -This user manual documents release `1.1.0 ` of the coding standards located at https://github.com/github/codeql-coding-standards/releases/tag/v1.1.0 . +This user manual documents release `2.49.0-dev` of the coding standards located at [https://github.com/github/codeql-coding-standards](https://github.com/github/codeql-coding-standards). The release page documents the release notes and contains the following artifacts part of the release: -- `code-scanning-cpp-query-pack-anon-1.1.0 .zip`: coding standard queries and scripts to be used with GitHub Code Scanning or the CodeQL CLI as documented in the section _Operating manual_. -- `lgtm-cpp-query-pack-anon-v1.1.0.zip`: coding standard queries to be used with GitHub LGTM Enterprise as documented in the section _Operating manual_. -- `supported_rules_list_1.1.0 .csv`: A Comma Separated File (CSV) containing the supported rules per standard and the queries that implement the rule. -- `supported_rules_list_1.1.0 .md`: A Markdown formatted file with a table containing the supported rules per standard and the queries that implement the rule. -- `user_manual.md`: This user manual. +- `coding-standards-codeql-packs-2.37.0-dev.zip`: CodeQL packs that can be used with GitHub Code Scanning or the CodeQL CLI as documented in the section _Operating manual_. +- `code-scanning-cpp-query-pack-2.49.0-dev.zip`: Legacy packaging for the queries and scripts to be used with GitHub Code Scanning or the CodeQL CLI as documented in the section _Operating manual_. +- `supported_rules_list_2.49.0-dev.csv`: A Comma Separated File (CSV) containing the supported rules per standard and the queries that implement the rule. +- `supported_rules_list_2.49.0-dev.md`: A Markdown formatted file with a table containing the supported rules per standard and the queries that implement the rule. +- `user_manual_2.49.0-dev.md`: This user manual. - `Source Code (zip)`: A zip archive containing the contents of https://github.com/github/codeql-coding-standards - `Source Code (tar.gz)`: A GZip compressed tar archive containing the contents of https://github.com/github/codeql-coding-standards +- `checksums.txt`: A text file containing sha256 checksums for the aforementioned artifacts. ## Introduction @@ -43,20 +60,36 @@ A _coding standard_ is a set of rules or guidelines which restrict or prohibit t The _CodeQL Coding Standards_ product is a set of CodeQL queries for identifying contraventions of rules in the following coding standards: -| Standard | Version | Total rules | Total supportable rules | Status | -| -------------------------------------------------------------------------------------------------------------------- | ------- | ----------- | ----------------------- | ----------- | -| [AUTOSAR C++](https://www.autosar.org/fileadmin/user_upload/standards/adaptive/20-11/AUTOSAR_RS_CPP14Guidelines.pdf) | 20-11 | 397 | 375 | Implemented | -| [CERT-C++](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-cpp-coding-standard-2016-v01.pdf) | 2016 | 83 | 83 | Implemented | +| Standard | Version | Rules | Supportable rules | Implemented rules | Status | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ----------------- | ----------------- | ----------------- | +| AUTOSAR C++ | [^1] [R22-11](https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf), R21-11, R20-11, R19-11, R19-03 | 397 | 372 | 370[^2] | Implemented | +| CERT-C++ | [2016](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-cpp-coding-standard-2016-v01.pdf) | 83 | 82 | 82 | Implemented | +| CERT C | [2016](https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-c-coding-standard-2016-v01.pdf) | 99 | 97 | 97 | Implemented | +| MISRA C | [2012 Third Edition, First Revision](https://www.misra.org.uk/product/misra-c2012-third-edition-first-revision/), [Amendment 2](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD2.pdf) and TC2 | 175 | 164 | 162[^3] | Implemented | +| | [2012 Amendment 3](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD3.pdf) | 24 | 24 | 24 | Implemented | +| | [2012 Amendment 4](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD4.pdf) | 22 | 22 | 21[^4] | Implemented | +| | [2023 Third Edition, Second Revision](https://misra.org.uk/product/misra-c2023/) | 221 | 210 | 207[^5] | Implemented | +| MISRA C++ | [2023](https://misra.org.uk/product/misra-cpp2023/) | 179 | 176[^6] | - | Under development | + +Not all rules in these standards are amenable to static analysis by CodeQL - some rules require external or domain specific knowledge to validate, or refer to properties which are not present in our representation of the codebase under analysis. In addition, some rules are natively enforced by the supported compilers. As CodeQL requires that the program under analysis compiles, we are unable to implement queries for these rules, and doing so would be redundant. + +For each rule we therefore identify whether it is supportable or not. Furthermore, a rule can be supported in two ways: + +- **Automated** - the queries for the rule find contraventions directly. +- **Audit only** - the queries for the rule does not find contraventions directly, but instead report a list of _candidates_ that can be used as input into a manual audit. For example, `A10-0-1` (_Public inheritance shall be used to implement 'is-a' relationship_) is not directly amenable to static analysis, but CodeQL can be used to produce a list of all the locations that use public inheritance so they can be manually reviewed. +- **Strict only** - the queries for the rule find contraventions directly, but find results which are strongly indicated to be intentional, and where adding a _deviation report_ may be extra burden on developers. For example, in `RULE-2-8` (_A project should not contain unused object definitions_), declaring objects with `__attribute__((unused))` may be preferable to a _deviation report_, which will not suppress relevant compiler warnings, and therefore would otherwise require developer double-entry. -Not all rules in these standards are amenable to static analysis by CodeQL - some rules require external or domain specific knowledge to validate, or refer to properties which are not present in the our representation of the codebase under analysis. For each rule we therefore identify whether it is supportable or not. Furthermore, a rule can be supported in two ways: - - - **Automated** - the queries for the rule find contraventions directly. - - **Audit only** - the queries for the rule does not find contraventions directly, but instead report a list of _candidates_ that can be used as input into a manual audit. For example, `A10-0-1` (_Public inheritance shall be used to implement 'is-a' relationship_) is not directly amenable to static analysis, but CodeQL can be used to produce a list of all the locations that use public inheritance so they can be manually reviewed. - Each supported rule is implemented as one or more CodeQL queries, with each query covering an aspect of the rule. In many coding standards, the rules cover non-trivial semantic properties of the codebase under analysis. The datasheet _"CodeQL Coding Standards: supported rules"_, provided with each release, lists which rules are supported for that particular release, and the _scope of analysis_ for that rule. +[^1]: AUTOSAR C++ versions R22-11, R21-11, R20-11, R19-11 and R19-03 are all identical as indicated in the document change history. +[^2]: The unimplemented supportable AUTOSAR rules are `A7-1-8` and `A8-2-1`. These rules require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. +[^3]: The unimplemented supportable MISRA C 2012 rules are `Rule 9.5`, `Rule 17.13`. `Rule 9.5` and `Rule 17.13` require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. Note: `Dir 4.14` is covered by the default CodeQL queries, which identify potential security vulnerabilities caused by not validating external input. +[^4]: The unimplemented supportable MISRA C 2012 Amendment 4 rule is `Rule 9.6`. `Rule 9.6` requires additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of this rule. +[^5]: The unimplemented supportable MISRA C 2023 rules are `Rule 9.5`, `Rule 9.6`, `Rule 17.13`. `Rule 9.5`, `Rule 9.6` and `Rule 17.13` require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. Note: `Dir 4.14` is covered by the default CodeQL queries, which identify potential security vulnerabilities caused by not validating external input. +[^6]: The rules `5.13.7`, `19.0.1` and `19.1.2` are not planned to be implemented by CodeQL as they are compiler checked in all supported compilers. + ## Supported environment This section describes the supported environment for the product. @@ -65,8 +98,8 @@ This section describes the supported environment for the product. To run the "CodeQL Coding Standards" queries two additional components are required: - - The CodeQL CLI - this is the command line tool for creating CodeQL databases and running CodeQL queries. - - The CodeQL Standard Library for C++ - this provides the common CodeQL query libraries used in the implementation of the CodeQL Coding Standards queries. +- The CodeQL CLI - this is the command line tool for creating CodeQL databases and running CodeQL queries. +- The CodeQL Standard Library for C++ - this provides the common CodeQL query libraries used in the implementation of the CodeQL Coding Standards queries. Refer to the release notes for the selected release to determine which versions of these dependencies are supported or required. @@ -74,65 +107,122 @@ From a functional safety perspective, the use of these two components is only va ### Codebase requirements -The codebase under analysis must: - * Comply with C++14 - * Use one of the following supported compilers: - - Clang version 10 +In all scenarios, the codebase must comply with the language, platform and compiler requirements listed on the [CodeQL: Supported languages and frameworks](https://codeql.github.com/docs/codeql-overview/supported-languages-and-frameworks) in order to be successfully analyzed. + +In addition, the machine which performs the analysis must be able to complete a clean build of the codebase. + +#### C++ + +For C++ the codebase under analysis must comply with C++14 and use one of the following supported compiler configurations: + +| Compiler | Version | Standard library | Target architecture | Required flags | +| -------- | ------- | ------------------- | --------------------- | -------------------------------- | +| clang | 10.0.0 | libstdc++ (default) | x86_64-linux-gnu | -std=c++14 | +| gcc | 8.4.0 | libstdc++ (default) | x86_64-linux-gnu | -std=c++14 | +| qcc | 8.3.0 | libc++ (default) | gcc_ntoaarch64le_cxx | -std=c++14 -D_QNX_SOURCE -nopipe | Use of the queries outside these scenarios is possible, but not validated for functional safety. In particular: - - Use of the queries against codebases written with more recent versions of C++ (as supported by CodeQL) are not validated in the following circumstances: - - When new language features are used - - When language features are used which have a differing interpretation from C++14. - - Use of the queries against codebases which use other compilers or other compiler versions supported by CodeQL (e.g. gcc) is not tested or validated for functional safety. -In all scenarios, the codebase must comply with the language, platform and compiler requirements listed on the [CodeQL: Supported languages and frameworks](https://codeql.github.com/docs/codeql-overview/supported-languages-and-frameworks) in order to be successfully analyzed. +- Use of the queries against codebases written with more recent versions of C++ (as supported by CodeQL) are not validated in the following circumstances: + - When new language features are used + - When language features are used which have a differing interpretation from C++14. +- Use of the queries against codebases which use other compilers or other compiler versions supported by CodeQL is not tested or validated for functional safety. -In addition, the machine which performs the analysis must be able to complete a clean build of the codebase. +#### C + +For C the codebase under analysis must comply with C99 or C11 and use one of the following supported compiler configurations: + +| Compiler | Version | Standard library | Target architecture | Required Flags | +| -------- | ------- | ------------------- | --------------------- | -------------------------- | +| clang | 10.0.0 | glibc (default) | x86_64-linux-gnu | `-std=c11` or `-std=c99` | +| gcc | 8.4.0 | glibc (default) | x86_64-linux-gnu | `-std=c11` or `-std=c99` | +| qcc | 8.3.0 | glibc (default) | gcc_ntoaarch64le | `-std=c11 -nopipe` or `-std=c99 -nopipe` | + +Use of the queries outside these scenarios is possible, but not validated for functional safety. In particular: + +- Use of the queries against codebases written with more recent versions of C (as supported by CodeQL) are not validated in the following circumstances: + - When new language features are used + - When language features are used which have a differing interpretation from C11. +- Use of the queries against codebases which use other compilers or other compiler versions supported by CodeQL (e.g. gcc) is not tested or validated for functional safety. ### Analysis report requirements The Coding Standards ships with scripts to generate reports that summarizes: - - - The integrity and validity of the CodeQL database created for the project. - - The findings reported by the default queries for the selected Coding Standards, grouped by categories as specified by MISRA Compliance 2020. - - The CodeQL dependencies used for the analysis, and whether they comply with the stated requirements. + +- The integrity and validity of the CodeQL database created for the project. +- The findings reported by the default queries for the selected Coding Standards, grouped by categories as specified by MISRA Compliance 2020. +- The CodeQL dependencies used for the analysis, and whether they comply with the stated requirements. The environment used to generate these reports requires: - A Python interpreter version 3.9 - A CodeQL CLI version documented in the release artifact `supported_codeql_configs.json` + ## Operating manual This section describes how to operate the "CodeQL Coding Standards". + ### Command line #### Pre-requisite: downloading the CodeQL CLI -You must download a compatible version of the CodeQL CLI and CodeQL Standard Library for C++. +You must download a compatible version of the CodeQL CLI, as specified in the release notes for the release you are using. + +**Option 1:** Use the CodeQL CLI bundle, which includes both the CodeQL CLI and GitHub's default security queries: -**Option 1:** Use the CodeQL CLI bundle, which includes both required components: - 1. Download the CodeQL CLI bundle from the [`github/codeql-action` releases page](https://github.com/github/codeql-action/releases). + 1. Download the CodeQL CLI bundle from the [`github/codeql-action` releases page](https://github.com/github/codeql-action/releases). 2. Expand the compressed archive to a specified location on your machine. 3. [Optional] Add the CodeQL CLI to your user or system path. -**Option 2:** Fetch the components separately: +This approach is recommended if you wish to use the default queries provided by GitHub in addition to the Coding Standards queries. + +**Option 2:** Use the CodeQL CLI binary: + 1. Download the CodeQL CLI from the [`github/codeql-cli-binaries` releases page](https://github.com/github/codeql-cli-binaries/releases) 2. Expand the compressed archive to a specified location on your machine. - 3. Using `git`, clone the [`github/codeql`](https://github.com/github/codeql) repository to a sibling directory of the CodeQL CLI. The `github/codeql` repository contains the CodeQL Standard Library for C++. - 4. [Optional] Add the CodeQL CLI to your user or system path. +3. [Optional] Add the CodeQL CLI to your user or system path. + +#### Pre-requisite: downloading the Coding Standards queries + +The Coding Standards packs can be downloaded into the local CodeQL package cache using the following command: + +```bash +codeql pack download codeql/--coding-standards@ +``` + +The supported standards and languages are: + * `codeql/misra-c-coding-standards` - a CodeQL query pack for reporting violations of MISRA C. + * `codeql/cert-c-coding-standards` - a CodeQL query pack for reporting violations of CERT C. + * `codeql/misra-cpp-coding-standards` - a CodeQL query pack for reporting violations of MISRA C++. + * `codeql/cert-cpp-coding-standards` - a CodeQL query pack for reporting violations of CERT C++. + * `codeql/autosar-cpp-coding-standards` - - a CodeQL query pack for reporting violations of AUTOSAR for C++. + +Ensure that the `@` string matches the desired Coding Standards version. + +Alternatively, the packs can be downloaded directly from a release on the `github/codeql-coding-standards` repository by choosing the `coding-standards-codeql-packs.zip`, which contains the following files: -The release notes for the "CodeQL Coding Standards" pack you are using will specify the appropriate versions to use. + * `misra-c-coding-standards.tgz` - a CodeQL query pack for reporting violations of MISRA C. + * `cert-c-coding-standards.tgz` - a CodeQL query pack for reporting violations of CERT C. + * `cert-cpp-coding-standards.tgz` - a CodeQL query pack for reporting violations of CERT C++. + * `autosar-cpp-coding-standards.tgz` - a CodeQL query pack for reporting violations of AUTOSAR for C++. + * `common-cpp-coding-standards.tgz` - a CodeQL library pack, used if you are writing your own C++ queries against Coding Standards. + * `common-c-coding-standards.tgz` - a CodeQL library pack, used if you are writing your own C queries against Coding Standards. + * `report-coding-standards.tgz` - a CodeQL query pack for running diagnostics on databases. + +Each pack will need to be decompressed using the `tar` program, and placed in a known location. + +Finally, we provide a legacy single zip containing all the artifacts from a release, named `code-scanning-cpp-query-pack.zip`. This also contains the CodeQL packs listed above. #### Creating a CodeQL database In order to run the Coding Standards queries you must first build a CodeQL database representing the program. You will need the following pre-requisites: - - A machine with the source code available locally. - - A clean build command for the project, which compiles all relevant source code locally on the machine without failure. Incremental and distributed builds must be disabled. The build command must be tested prior to configuring the CodeQL CLI and confirmed to compile all relevant files and return a zero exit code to indicate success. +- A machine with the source code available locally. +- A clean build command for the project, which compiles all relevant source code locally on the machine without failure. Incremental and distributed builds must be disabled. The build command must be tested prior to configuring the CodeQL CLI and confirmed to compile all relevant files and return a zero exit code to indicate success. The database can be created using the CodeQL CLI like so: -```codeql +```bash codeql database create --language cpp --command ``` @@ -142,76 +232,141 @@ Reference: [CodeQL CLI: Creating a CodeQL database](https://codeql.github.com/do #### Running the default analysis for one or more Coding Standards -Once you have a CodeQL database for your project, you can run the "default" query suite. This will run all the "automated" queries for each implemented rule in the specified Coding Standards. +Once you have a CodeQL database for your project you can run the default analysis for a specified Coding Standard using the `codeql database analyze` command by specifying the names of the QL packs which you want to run as arguments, along with a version specifier: + +```bash +codeql database analyze --format=sarifv2.1.0 --output=.sarif path/to/ codeql/--coding-standard@version +``` + +For example, this command would run MISRA C and CERT C with the default query sets: + +```bash +codeql database analyze --format=sarifv2.1.0 --output=results.sarif path/to/ codeql/misra-c-coding-standard@version codeql/cert-c-coding-standard@version +``` +The output of this command will be a [SARIF file](https://sarifweb.azurewebsites.net/) called `.sarif`. -The query suites can be run by using the `codeql database analyze` command: +##### Locating the Coding Standards CodeQL packs +If you have downloaded a release artifact containing the packs, you will need to provide the `--search-path` parameter, pointing to each of the uncompressed query packs. ``` -codeql database analyze --format=sarifv2.1.0 --output=.sarif path/to/ path/to/codeql-coding-standards/cpp//src/codeql-suites/-default.qls... +--search-path path/to/pack1:path/to/pack2 ``` -For each Coding Standard you want to run, add a trailing entry in the following format: `path/to/codeql-coding-standards/cpp//src/codeql-suites/-default.qls`. + +Alternatively, the packs can be made available to CodeQL without specification on the comamnd line by placing them inside the distribution under the `qlpacks/codeql/` directory, or placed inside a directory adjacent to the folder containing the distribution. + +##### Alternative query sets + +Each supported standard includes a variety of query suites, which enable the running of different sets of queries based on specified properties. In addition, a custom query suite can be defined as specified by the CodeQL CLI documentation, in order to select any arbitrary sets of queries in this repository. To run + +```bash +codeql database analyze --format=sarifv2.1.0 --output=.sarif path/to/ codeql/--coding-standard@version:codeql-suites/.qls +``` + +If modifying the query suite, ensure that all Rules you expect to be covered by CodeQL in your Guideline Enforcement Plan (or similar) are included in the query suite, by running: + +```bash +codeql resolve queries codeql/--coding-standard@version:codeql-suites/.qls +``` + +##### Supported SARIF versions The only supported SARIF version for use in a functional safety environment is version 2.1.0. To select this SARIF version you **must** specify the flag `--format=sarifv2.1.0` when invoking the database analyze command `codeql database analyze ...` as shown in the above example. -Running the default analysis for one or more Coding Standards may require further performance customizations for larger codebases. -The following flags may be passed to the `database analyze` command to adjust the performance: - - `--ram` - to specify the maximum amount of RAM to use during the analysis as [documented](https://codeql.github.com/docs/codeql-cli/manual/database-analyze/#options-to-control-ram-usage) in the CodeQL CLI manual. - - `--thread` - to specify number of threads to use while evaluating as [documented](https://codeql.github.com/docs/codeql-cli/manual/database-analyze/#cmdoption-codeql-database-analyze-j) in the CodeQL CLI manual. +##### Performance optimizations -The output of this command will be a [SARIF file](https://sarifweb.azurewebsites.net/) called `.sarif`. +Running the default analysis for one or more Coding Standards may require further performance customizations for larger codebases. The following flags may be passed to the `database analyze` command to adjust the performance: + +- `--ram` - to specify the maximum amount of RAM to use during the analysis as [documented](https://docs.github.com/en/code-security/codeql-cli/codeql-cli-manual/database-analyze#options-to-control-ram-usage) in the CodeQL CLI manual. +- `--thread` - to specify number of threads to use while evaluating as [documented](https://docs.github.com/en/code-security/codeql-cli/codeql-cli-manual/database-analyze#-j---threadsnum) in the CodeQL CLI manual. -#### Running the analysis for audit level queries +##### Legacy approach -Optionally, you may want to run the "audit" level queries. These queries produce lists of results that do not directly highlight contraventions of the rule. Instead, they identify locations in the code that can be manually audited to verify the absence of problems for that particular rule. +If you have downloaded the legacy release artifact `code-scanning-query-pack.zip`, you can run the default query suite using the `codeql database analyze` command as follows: +```bash +codeql database analyze --format=sarifv2.1.0 --output=.sarif path/to/ path/to/codeql-coding-standards///src/codeql-suites/-default.qls... ``` + +For each Coding Standard you want to run, add a trailing entry in the following format: `path/to/codeql-coding-standards///src/codeql-suites/-default.qls`. Custom query suites can be run by specifying the appropriate paths. + +All other options discussed above are valid. + +#### Running the analysis for strict and/or audit level queries + +Optionally, you may want to run the "strict" or "audit" level queries. + +Audit queries produce lists of results that do not directly highlight contraventions of the rule. Instead, they identify locations in the code that can be manually audited to verify the absence of problems for that particular rule. + +```bash codeql database analyze --format=sarifv2.1.0 --output=.sarif path/to/ path/to/codeql-coding-standards/cpp//src/codeql-suites/-audit.qls... ``` -For each Coding Standard you want to run, add a trailing entry in the following format: `path/to/codeql-coding-standards/cpp//src/codeql-suites/-default.qls`. +Strict queries identify contraventions in the code that strongly suggest they are deliberate, and where adding an explicit _deviation report_ may be extra burden on developers. + +```bash +codeql database analyze --format=sarifv2.1.0 --output=.sarif path/to/ path/to/codeql-coding-standards/cpp//src/codeql-suites/-strict.qls... +``` #### Producing an analysis report In addition to producing a results file, an analysis report can be produced that summarizes: - - The integrity and validity of the CodeQL database created for the project. - - The findings reported by the default queries for the selected Coding Standards, grouped by categories as specified by MISRA Compliance 2020. - - The CodeQL dependencies used for the analysis, and whether they comply with the stated requirements. + +- The integrity and validity of the CodeQL database created for the project. +- The findings reported by the default queries for the selected Coding Standards, grouped by categories as specified by MISRA Compliance 2020. +- The CodeQL dependencies used for the analysis, and whether they comply with the stated requirements. To run this script, the CodeQL CLI part of a supported CodeQL Bundle and Python interpreter version 3.9 must be available on the system path. -``` -python3 scripts/reports/analysis_report.py path/to/ .sarif +```bash +python3.9 scripts/reports/analysis_report.py path/to/ .sarif ``` This will produce a directory (``) containing the following report files in markdown format: - - A **Guideline Compliance Summary** (GCS) which meets the requirements specified by the [MISRA Compliance 2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) document, and providing a summary of: - - Whether the analysis reports that the project is "Compliance". - - Which Coding Standards were applied. - - The versions of the CodeQL CLI, CodeQL Standard Library for C/C++ and the CodeQL Coding Standards queries used to perform the analysis. - - Count of violations of guidelines by guideline category ("Required", "Advisory") - - A list of the guidelines checked, and the status of each guideline ("Compliant", "Violations", "Deviations"). - - **Note:** The `Deviations` status is **only** shown when the database has been build with a configuration to *report deviated alerts* and analyzed with a *deviation alert suppression query*. The section on *Deviation records* outlines how this can be achieved. - - An **Analysis Integrity Report** which summarizes any issues that were identified in the creation of the database, which can be reviewed to determine the extent to which these issues may have impacted the generated results. This includes: - - A list of recoverable errors, where a specific piece of syntax was not handled, but the error could be recovered from. These a further sub-divided into "user code" errors and "third-party" errors. - - A list of unrecoverable errors, which affect either entire files or entire compilations. These are also further sub-divided into "user code" errors and "third-party" errors. - - A list of the files analyzed. - - A **Deviations Report** which reports the deviation records that where included during the creation of the database, which can be used to audit the applied deviations. The includes: - - A table of deviation records for which we list: - - An identifier for the coding standards rule the deviation applies to. - - The query identifier that implements the guideline. - - An inferred scope that shows the files or code-identifier the deviation is applied to. - - A textual description of the scope when the deviation can be applied. - - A textual justification of the deviation. - - A textual description of background information. - - A textual description of the requirements which must be satisfied to use the deviation. +- A **Guideline Compliance Summary** (GCS) which meets the requirements specified by the [MISRA Compliance 2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) document, and providing a summary of: + - Whether the analysis reports that the project is "Compliance". + - Which Coding Standards were applied. + - The versions of the CodeQL CLI, CodeQL Standard Library for C/C++ and the CodeQL Coding Standards queries used to perform the analysis. + - Count of violations of guidelines by guideline category ("Required", "Advisory") + - A list of the guidelines checked, and the status of each guideline ("Compliant", "Violations", "Deviations"). + - **Note:** The `Deviations` status is **only** shown when the database has been build with a configuration to _report deviated alerts_ and analyzed with a _deviation alert suppression query_. The section on _Deviation records_ outlines how this can be achieved. +- An **Analysis Integrity Report** which summarizes any issues that were identified in the creation of the database, which can be reviewed to determine the extent to which these issues may have impacted the generated results. This includes: + - A list of recoverable errors, where a specific piece of syntax was not handled, but the error could be recovered from. These a further sub-divided into "user code" errors and "third-party" errors. + - A list of unrecoverable errors, which affect either entire files or entire compilations. These are also further sub-divided into "user code" errors and "third-party" errors. + - A list of the files analyzed. +- A **Deviations Report** which reports the deviation records that where included during the creation of the database, which can be used to audit the applied deviations. The includes: + - A table of deviation records for which we list: + - An identifier for the coding standards rule the deviation applies to. + - The query identifier that implements the guideline. + - An inferred scope that shows the files or code-identifier the deviation is applied to. + - A textual description of the scope when the deviation can be applied. + - A textual justification of the deviation. + - A textual description of background information. + - A textual description of the requirements which must be satisfied to use the deviation. + - A table of invalid deviation records for which we list: + - The location of the invalid deviation record in the database. + - The reason why it is considered invalid. + - A table of deviation permits for which we list: + - An identifier that identifies the permit. + - An identifier for the coding standards rule the deviation applies to. + - The query identifier that implements the guideline. + - An inferred scope that shows the files or code-identifier the deviation is applied to. + - A textual description of the scope when the deviation can be applied. + - A textual justification of the deviation. + - A textual description of background information. + - A textual description of the requirements which must be satisfied to use the deviation. + - A table of invalid deviation permits for which we list: + - The location of the invalid permit in the database. + - The reason why it is considered invalid. #### Applying deviations The CodeQL Coding Standards supports the following features from the [MISRA Compliance 2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) document: - - _Deviation records_ - an entry that states a particular instance, or set of instances, of a rule should be considered permitted. - - _Deviation permit_ - an entry that provides authorization to apply a deviation to a project. + +- _Deviation records_ - an entry that states a particular instance, or set of instances, of a rule should be considered permitted. +- _Deviation permit_ - an entry that provides authorization to apply a deviation to a project. +- _Guideline re-categorization plan_ - an agreement on how the guidelines are applied. Whether a guideline may be violated, deviated from, or must always be applied. ##### Deviation records @@ -226,15 +381,16 @@ The rational for the default behavior is that GitHub Code Scanning does not supp **Note:** It is important to create a database with the property `report-deviated-alerts: true` set and analyzed with the alert suppression query `path/to/codeql-coding-standards/cpp/common/src/codingstandards/cpp/deviations/DeviationsSuppression.ql` when the **Guideline Compliance Summary Report** **must** include deviation statuses! The current implementation of the `coding-standards.yml` specification supports the `deviations` section with the following keys: + - `rule-id` - An identifier for the coding standards rule the deviation applies to. This matches the rule id format specified in the documentation (e.g., `A1-0-1`) -- `query-id` - An identifier for the query (as specified by the `@id` property of the query) that can be used to specify a deviation for *sub-category* of rule (as defined by a query). If the `query-id` is specified , the `rule-id` property should also be specified. +- `query-id` - An identifier for the query (as specified by the `@id` property of the query) that can be used to specify a deviation for _sub-category_ of rule (as defined by a query). If the `query-id` is specified , the `rule-id` property should also be specified. - `justification` - An short textual justification of the deviation. -- `scope` - An *optional* short textual description of when this deviation can be applied. This will be combined with any automatically deduced scope for the deviation. +- `scope` - An _optional_ short textual description of when this deviation can be applied. This will be combined with any automatically deduced scope for the deviation. - `background` - Any relevant background information. -- `requirements` - One or more *requirements* which must be satisfied to use this deviation. -- `paths` - An *optional* set of paths, relative to the deviations file, specify either a directory or file to which this deviation should be applied. -- `code-identifier` - An *optional* identifier which can be placed in the source code at locations where this deviation should be applied. -- `permit-id` - An *optional* identifier which links to a deviation permit, from which some of the properties can be inherited. +- `requirements` - One or more _requirements_ which must be satisfied to use this deviation. +- `paths` - An _optional_ set of paths, relative to the deviations file, specify either a directory or file to which this deviation should be applied. +- `code-identifier` - An _optional_ identifier which can be placed in the source code at locations where this deviation should be applied. +- `permit-id` - An _optional_ identifier which links to a deviation permit, from which some of the properties can be inherited. - `raised-by` - A compact mapping, if specified requires the specification of `approved-by`, that includes: - `name` - The name, handle or other identifier of the user who raised the request - `date` - The date on which they raised the request. @@ -260,26 +416,110 @@ deviations: ``` The example describes three ways of scoping a deviation: + 1. The deviation for `A18-1-1` applies to any source file in the same or a child directory of the directory containing the example `coding-standards.yml`. 2. The deviation for `A18-5-1` applies to any source file in the directory `foo/bar` or a child directory of `foo/bar` relative to the directory containing the `coding-standards.yml`. -3. The deviation for `A0-4-2` applies to any source element that has a comment residing on **the same line** containing the identifier specified in `code-identifier`. +3. The deviation for `A0-4-2` applies to any source element that marked by a comment containing the identifier specified in `code-identifier`. The different acceptable formats are discussed in the next section. The activation of the deviation mechanism requires an extra step in the database creation process. -This extra step is the invocation of the Python script `path/to/codeql-coding-standards/scripts/deviations/process_coding_standards_config.py` that is part of the coding standards code scanning pack. +This extra step is the invocation of the Python script `path/to/codeql-coding-standards/scripts/configuration/process_coding_standards_config.py` that is part of the coding standards code scanning pack. +To run this script, a Python interpreter version 3.9 must be available on the system path. The script should be invoked as follows: -```codeql -codeql database create --language cpp --command 'python3 path/to/codeql-coding-standards/scripts/deviations/process_coding_standards_config.py' --command +```bash +codeql database create --language cpp --command 'python3 path/to/codeql-coding-standards/scripts/configuration/process_coding_standards_config.py' --command ``` The `process_coding_standards_config.py` has a dependency on the package `pyyaml` that can be installed using the provided PIP package manifest by running the following command: -`pip install -r path/to/codeql-coding-standards/scripts/deviations/requirements.txt` +`pip3 install -r path/to/codeql-coding-standards/scripts/configuration/requirements.txt` + +##### Deviation code identifier attributes + +A code identifier specified in a deviation record can be applied to certain results in the code by adding a C or C++ attribute of the following format: + +``` +[[codeql::_deviation("code-identifier")]] +``` + +For example `[[codeql::autosar_deviation("a1-2-4")]]` would apply a deviation of a rule in the AUTOSAR standard, using the code identifier `a1-2-4`. The supported standard names are `misra`, `autosar` and `cert`. + +This attribute may be added to the following program elements: + + * Functions + * Statements + * Variables + +Deviation attributes are inherited from parents in the code structure. For example, a deviation attribute applied to a function will apply the deviation to all code within the function. + +Multiple code identifiers may be passed in a single attribute to apply multiple deviations, for example: + +``` +[[codeql::misra_deviation("code-identifier-1", "code-identifier-2")]] +``` + +Note - considation should be taken to ensure the use of custom attributes for deviations is compatible with your chosen language version, compiler, compiler configuration and coding standard. + +**Use of attributes in C Coding Standards**: The C Standard introduces attributes in C23, however some compilers support attributes as a language extension in prior versions. You should: + * Confirm that your compiler supports attributes for your chosen compiler configuration, if necessary as a language extension. + * Confirm that unknown attributes are ignored by the compiler. + * For MISRA C, add a project deviation against "Rule 1.2: Language extensions should not be used", if attribute support is a language extension in your language version. + +**Use of attributes in C++ Coding Standards**: The C++ Standard supports attributes in C++14, however the handling of unknown attributes is implementation defined. From C++17 onwards, unknown attributes are mandated to be ignored. Unknown attributes will usually raise an "unknown attribute" warning. You should: + * If using C++14, confirm that your compiler ignores unknown attributes. + * If using AUTOSAR and a compiler which produces warnings on unknown attributes, the compiler warning should be disabled (as per `A1-1-2: A warning level of the compilation process shall be set in compliance with project policies`), to ensure compliance with `A1-4-3: All code should compiler free of compiler warnings`. + +If you cannot satisfy these condition, please use the deviation code identifier comment format instead. + +##### Deviation code identifier comments + +As an alternative to attributes, a code identifier specified in a deviation record can be applied to certain results in the code by adding a comment marker consisting of a `code-identifier` with some optional annotations. The supported marker annotation formats are: + + - `` - the deviation applies to results on the current line. + - `codeql::_deviation()` - the deviation applies to results on the current line. + - `codeql::_deviation_next_line()` - this deviation applies to results on the next line. + - `codeql::_deviation_begin()` - marks the beginning of a range of lines where the deviation applies. + - `codeql::_deviation_end()` - marks the end of a range of lines where the deviation applies. + +Here are some examples, using the deviation record with the `a-0-4-2-deviation` code-identifier specified above: +```cpp + long double x1; // NON_COMPLIANT + + long double x2; // a-0-4-2-deviation - COMPLIANT + long double x3; // COMPLIANT - a-0-4-2-deviation + + long double x4; // codeql::autosar_deviation(a-0-4-2-deviation) - COMPLIANT + long double x5; // COMPLIANT - codeql::autosar_deviation(a-0-4-2-deviation) + + // codeql::autosar_deviation_next_line(a-0-4-2-deviation) + long double x6; // COMPLIANT + + // codeql::autosar_deviation_begin(a-0-4-2-deviation) + long double x7; // COMPLIANT + // codeql::autosar_deviation_end(a-0-4-2-deviation) +``` + +`codeql::_deviation_end` markers will pair with the closest unmatched `codeql::_deviation_begin` for the same `code-identifier`. Consider this example: +```cpp +1 | // codeql::autosar_deviation_begin(a-0-4-2-deviation) +2 | +3 | // codeql::autosar_deviation_begin(a-0-4-2-deviation) +4 | +5 | // codeql::autosar_deviation_end(a-0-4-2-deviation) +6 | +7 | // codeql::autosar_deviation_end(a-0-4-2-deviation) +``` +Here, Line 1 will pair with Line 7, and Line 3 will pair with Line 5. + +A `codeql::_deviation_end` without a matching `codeql::_deviation_begin`, or `codeql::_deviation_begin` without a matching `codeql::_deviation_end` is invalid and will be ignored. + +`codeql::_deviation_begin` and `ccodeql::_deviation_end` markers only apply within a single file. Markers cannot be paired across files, and deviations do not apply to included files. +Note: deviation comment markers cannot be applied to the body of a macro. Please apply the deviation to macro expansion, or use the attribute deviation format. -##### Deviation permit +##### Deviation permits -The current implementation supports _deviation permits_ as described in the [MISRA Compliance:2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) section *4.3 Deviation permits*. +The current implementation supports _deviation permits_ as described in the [MISRA Compliance:2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) section _4.3 Deviation permits_. Deviation permits are a mechanism to simplify the documentation of many deviations by allowing _deviation records_ to inherit properties from a _deviation permit_. A _deviation record_ can inherit the following properties that are documented in the section on _Deviation records_: @@ -336,13 +576,73 @@ Unlike _deviation records_ their location in the source directory does not impac This means that _deviation permits_ can be made available at build time by any means available. An example of importing _deviation permits_ is through a [Git Submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) that contains a repository of allowed _deviation permits_. -### GitHub Advanced Security and LGTM + +##### Guideline re-categorization plan + +The current implementation supports a _guideline re-categorization plan_ as described in the [MISRA Compliance:2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) section _5 The guideline re-categorization plan_. + +A re-categorization plan provides a mechanism to adjust the policy associated with a guideline that determines whether it may be violated or not and if it may be violated whether a deviation is required. + +The implementation follows the constraints on re-categorization as described in [MISRA Compliance:2020](https://www.misra.org.uk/app/uploads/2021/06/MISRA-Compliance-2020.pdf) section _5.1 Re-categorization_. + +The following tables described the re-categorizations are permitted. + +| Current Category | Revised Category | Revised Category | Revised Category | Revised Category | +| --- | --- | --- | --- | --- | +| | Mandatory | Required | Advisory | Disapplied | +| Mandatory | Permitted | | | | +| Required | Permitted | Permitted | | | +| Advisory | Permitted | Permitted | Permitted | Permitted | + +Each guideline re-categorization **must** be specified in the `guideline-recategorizations` section of a `coding-standards.yml` file that **must** be anywhere in the source repository. + +A guideline re-categorization specification **must** specify a `rule-id`, an identifier for the coding standards rule the re-categorization applies to, and a `category`, a category that can be any of `disapplied`, `advisory`, `required`, or `mandatory`. + +An example guideline re-categorization section is: + +```yaml +guideline-recategorizations: + - rule-id: "A0-1-1" + category: "mandatory" + - rule-id: "A0-1-6" + category: "disapplied" + - rule-id: "A11-0-1" + category: "mandatory" +``` + +Application of the guideline re-categorization plan to the analysis results requires an additional post-processing step. +The post-processing step is implemented by the Python script `path/to/codeql-coding-standards/scripts/guideline_recategorization/recategorize.py`. +The script will update the `external//obligation/` tag for each query implementing a recategorized guideline such that `` is equal to the new category and +add the tag `external//original-obligation/` reflects the orignal category. + +The script should be invoked as follows: + +```bash +python3.9 path/to/codeql-coding-standards/scripts/guideline_recategorization/recategorize.py coding_standards_config_file +``` + +The `recategorize.py` scripts has a dependencies on the following Python packages that can be installed with the command `pip install -r path/to/codeql-coding-standards/scripts/guideline_recategorization/requirements.txt`: + +- Jsonpath-ng==1.5.3 +- Jsonschema +- Jsonpatch +- Jsonpointer +- PyYAML +- Pytest + +and the schema files: + +- `path/to/codeql-coding-standards/schemas/coding-standards-schema-1.0.0.json` +- `path/to/codeql-coding-standards/schemas/sarif-schema-2.1.0.json` + +The schema files **must** be available in the same directory as the `recategorize.py` file or in any ancestor directory. + +### GitHub Advanced Security The only use cases that will be certified under ISO 26262 are those listed above. CodeQL Coding Standards is also compatible with, but not certified for, the following use cases: - - Creating databases and running the CodeQL Coding Standards queries with the [CodeQL Action](https://github.com/github/codeql-action) (for GitHub Actions CI/CD system). - - Uploading the SARIF results files for a CodeQL Coding Standards analysis to the GitHub [Code Scanning](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning) feature. - - Deployment of the default CodeQL Coding Standards queries as a custom query pack for [LGTM](https://semmle.com/lgtm). +- Creating databases and running the CodeQL Coding Standards queries with the [CodeQL Action](https://github.com/github/codeql-action) (for GitHub Actions CI/CD system). +- Uploading the SARIF results files for a CodeQL Coding Standards analysis to the GitHub [Code Scanning](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning) feature. ### Hazard and risk analysis @@ -367,15 +667,16 @@ This section describes known failure modes for "CodeQL Coding Standards" and des | | Use of incorrect build command | Less output. Some files may be only be partially analyzed, or not analyzed at all. | Analysis integrity report lists all analyzed files, and must be crossed referenced with the list of files that are expected to be analyzed. | Ensure the build command corresponds to the build command that is used to build the release artifacts. | | | Incorrect build environment (e.g., concurrent builds writing to same file, overwriting translation unit/object file with different content) | Less or more output. Results are reported that are not violations of the guidelines or guideline violations are not reported | All reported results must be reviewed. | Ensure the build environment is configured to not use shared resources such as caches or artifact providers that can introduce race conditions. Report inconsistent results via the CodeQL Coding Standards [bug tracker](https://github.com/github/codeql-coding-standards/issues). | | | Source root misspecification | Less output. The results cannot be correctly correlated to source files when viewing the resulting Sarif file in a Sarif viewer. | Verify that the reported results are display on the correct files in the Sarif viewer | Ensure the CodeQL CLI configured to use the correct source root that correspond to the root of the repository under consideration. | -| | Ouf of space | Less output. Some files may be only be partially analyzed, or not analyzed at all. | Error reported on the command line. | Increase space. If it remains an issue report space consumption issues via the CodeQL Coding Standards [bug tracker](https://github.com/github/codeql-coding-standards/issues). | +| | Out of space | Less output. Some files may be only be partially analyzed, or not analyzed at all. | Error reported on the command line. | Increase space. If it remains an issue report space consumption issues via the CodeQL Coding Standards [bug tracker](https://github.com/github/codeql-coding-standards/issues). | | | False positives | More output. Results are reported which are not violations of the guidelines. | All reported results must be reviewed. | Report false positive issues via the CodeQL Coding Standards [bug tracker](https://github.com/github/codeql-coding-standards/issues). | | | False negatives | Less output. Violations of the guidelines are not reported. | Other validation and verification processes during software development should be used to complement the analysis performed by CodeQL Coding Standards. | Report false negative issues via the CodeQL Coding Standards [bug tracker](https://github.com/github/codeql-coding-standards/issues). | -| | Modifying coding standard suite | More or less output. If queries are added to the query set more result can be reported. If queries are removed less results might be reported. | All queries supported by the CodeQL Coding Standards are listed in the release artifacts `supported_rules_list_VERSION.csv` where VERSION is replaced with the used release. The rules in the resulting Sarif file must be cross-referenced with the expected rules in this list to determine the validity of the used CodeQL suite. | Ensure that the CodeQL Coding Standards are not modified in ways that are not documented as supported modifications. | +| | Modifying coding standard suite | More or less output. If queries are added to the query set more result can be reported. If queries are removed less results might be reported. | All queries supported by the CodeQL Coding Standards are listed in the release artifacts `supported_rules_list_2.49.0-dev.csv` where VERSION is replaced with the used release. The rules in the resulting Sarif file must be cross-referenced with the expected rules in this list to determine the validity of the used CodeQL suite. | Ensure that the CodeQL Coding Standards are not modified in ways that are not documented as supported modifications. | | | Incorrect deviation record specification | More output. Results are reported for guidelines for which a deviation is assigned. | Analysis integrity report lists all deviations and incorrectly specified deviation records with a reason. Ensure that all deviation records are correctly specified. | Ensure that the deviation record is specified according to the specification in the user manual. | | | Incorrect deviation permit specification | More output. Results are reported for guidelines for which a deviation is assigned. | Analysis integrity report lists all deviations and incorrectly specified deviation permits with a reason. Ensure that all deviation permits are correctly specified. | Ensure that the deviation record is specified according to the specification in the user manual. | | | Unapproved use of a deviation record | Less output. Results for guideline violations are not reported. | Validate that the deviation record use is approved by verifying the approved-by attribute of the deviation record specification. | Ensure that each raised deviation record is approved by an independent approver through an auditable process. | | | Incorrect database. The information extracted by the CodeQL extractor deviates from what the compiler extracts resulting in an incorrect model of the source-code. | More or less output. Incorrect extraction can result in false positives or false negatives. | Combinations of supported compilers and CodeQL CLIs are tested against a [provided](https://github.com/github/codeql/tree/main/cpp/ql/test/library-tests) suite of test cases and a coding standards specific test suite to determine if the extracted information deviates from the expected information. | Report incorrect database issues via the CodeQL Coding Standards [bug tracker](https://github.com/github/codeql-coding-standards/issues). | +| | Use of assembly language instructions, which are not inspected by CodeQL. | More or less output. Can result in false positives or false negatives. | Avoid the use of assembly language instructions where possible. Where unavoidable, encapasulate and isolate the use of assembly language in separate functions to limit impact. Careful manual review of all functions that use assembly language. | Ensure that all functions which use assembly language instructions are manually reviewed for compliance. | + ## Reporting bugs A bug tracker is provided on the [`github/codeql-coding-standards`](https://github.com/github/codeql-coding-standards) repository [issues page](https://github.com/github/codeql-coding-standards/issues). New issues can be filed on the [New Issues](https://github.com/github/codeql-coding-standards/issues/new/choose) page. - diff --git a/integration-tests/deviations/build.sh b/integration-tests/deviations/build.sh index 1ca9f8daca..3196e5de54 100755 --- a/integration-tests/deviations/build.sh +++ b/integration-tests/deviations/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -~/codeql-home/codeqls/codeql-2.6.3/codeql database create --overwrite --language cpp --command "clang++ main.cpp" --command "python3 ../../scripts/deviations/process_coding_standards_config.py" ~/codeql-home/databases/deviations-test \ No newline at end of file +~/codeql-home/codeqls/codeql-2.6.3/codeql database create --overwrite --language cpp --command "clang++ main.cpp" --command "python3 ../../scripts/configuration/process_coding_standards_config.py" ~/codeql-home/databases/deviations-test \ No newline at end of file diff --git a/rule_packages/c/Alignment.json b/rule_packages/c/Alignment.json new file mode 100644 index 0000000000..edf06a09ca --- /dev/null +++ b/rule_packages/c/Alignment.json @@ -0,0 +1,79 @@ +{ + "MISRA-C-2012": { + "RULE-8-15": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An object declared with an explicit alignment shall be explicitly aligned in all declarations.", + "kind": "problem", + "name": "Alignment should match between all declarations of an object", + "precision": "very-high", + "severity": "error", + "short_name": "RedeclarationOfObjectWithoutAlignment", + "tags": [ + "external/misra/c/2012/amendment3", + "readability", + "maintainability" + ] + }, + { + "description": "All declarations of an object with an explicit alignment specification shall specify the same alignment.", + "kind": "problem", + "name": "Alignment should match between all declarations of an object", + "precision": "very-high", + "severity": "error", + "short_name": "RedeclarationOfObjectWithUnmatchedAlignment", + "tags": [ + "external/misra/c/2012/amendment3", + "readability", + "maintainability" + ] + } + ], + "title": "All declarations of an object with an explicit alignment specification shall specify the same alignment" + }, + "RULE-8-16": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A declaration shall not have an alignment of size zero.", + "kind": "problem", + "name": "The alignment specification of zero should not appear in an object declaration", + "precision": "very-high", + "severity": "error", + "short_name": "AlignmentWithSizeZero", + "tags": [ + "external/misra/c/2012/amendment3", + "readability", + "maintainability" + ] + } + ], + "title": "The alignment specification of zero should not appear in an object declaration" + }, + "RULE-8-17": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "While C permits the usage of multiple alignment specifiers, doing so reduces readability and may obscure the intent of the declaration.", + "kind": "problem", + "name": "At most one explicit alignment specifier should appear in an object declaration", + "precision": "very-high", + "severity": "error", + "short_name": "MoreThanOneAlignmentSpecifierOnDeclaration", + "tags": [ + "external/misra/c/2012/amendment3", + "readability" + ] + } + ], + "title": "At most one explicit alignment specifier should appear in an object declaration" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Banned.json b/rule_packages/c/Banned.json index 46032e3a68..265a41de51 100644 --- a/rule_packages/c/Banned.json +++ b/rule_packages/c/Banned.json @@ -13,7 +13,12 @@ "severity": "error", "short_name": "DoNotCallSystem", "tags": [ - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ] } ], @@ -35,7 +40,8 @@ "short_name": "CommaOperatorShouldNotBeUsed", "shared_implementation_short_name": "CommaOperatorUsed", "tags": [ - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -54,7 +60,8 @@ "severity": "error", "short_name": "FeaturesOfStdarghUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -73,7 +80,8 @@ "severity": "warning", "short_name": "UnionKeywordShouldNotBeUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -92,7 +100,8 @@ "severity": "error", "short_name": "StandardLibraryTimeAndDateFunctionsUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -100,7 +109,7 @@ }, "RULE-21-11": { "properties": { - "obligation": "required" + "obligation": "advisory" }, "queries": [ { @@ -111,7 +120,8 @@ "severity": "error", "short_name": "StandardHeaderFileTgmathhUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -130,7 +140,8 @@ "severity": "warning", "short_name": "ExceptionHandlingFeaturesOfFenvhUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/amendment2" ] } ], @@ -149,7 +160,8 @@ "severity": "error", "short_name": "SystemOfStdlibhUsed", "tags": [ - "security" + "security", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -169,7 +181,8 @@ "short_name": "MemoryAllocDeallocFunctionsOfStdlibhUsed", "tags": [ "correctness", - "security" + "security", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -188,7 +201,8 @@ "severity": "error", "short_name": "StandardHeaderFileUsedSetjmph", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -207,7 +221,8 @@ "severity": "error", "short_name": "StandardHeaderFileUsedSignalh", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -227,7 +242,8 @@ "short_name": "StandardLibraryInputoutputFunctionsUsed", "tags": [ "security", - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -245,8 +261,10 @@ "precision": "very-high", "severity": "error", "short_name": "AtofAtoiAtolAndAtollOfStdlibhUsed", + "shared_implementation_short_name": "AtofAtoiAtolAndAtollUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -266,7 +284,8 @@ "short_name": "TerminationFunctionsOfStdlibhUsed", "tags": [ "correctness", - "security" + "security", + "external/misra/c/2012/third-edition-first-revision" ] }, { @@ -278,7 +297,8 @@ "short_name": "TerminationMacrosOfStdlibhUsed", "tags": [ "correctness", - "security" + "security", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -298,13 +318,14 @@ "short_name": "BsearchAndQsortOfStdlibhUsed", "tags": [ "security", - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], "title": "The Standard Library functions 'bsearch' and 'qsort' of 'stdlib.h' shall not be used" }, - "RULE-4-12": { + "DIR-4-12": { "properties": { "obligation": "required" }, @@ -319,7 +340,8 @@ "tags": [ "security", "correctness", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -337,10 +359,12 @@ "precision": "very-high", "severity": "error", "short_name": "OctalConstantsUsed", + "shared_implementation_short_name": "UseOfNonZeroOctalLiteral", "tags": [ "readability", "correctness", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -360,7 +384,8 @@ "short_name": "RestrictTypeQualifierUsed", "tags": [ "correctness", - "security" + "security", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/Banned2.json b/rule_packages/c/Banned2.json new file mode 100644 index 0000000000..3898125d73 --- /dev/null +++ b/rule_packages/c/Banned2.json @@ -0,0 +1,24 @@ +{ + "MISRA-C-2012": { + "RULE-21-24": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The standard functions rand() and srand() will not give high quality random results in all implementations and are therefore banned.", + "kind": "problem", + "name": "The random number generator functions of shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "CallToBannedRandomFunction", + "tags": [ + "security", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "The random number generator functions of shall not be used" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/BitfieldTypes.json b/rule_packages/c/BitfieldTypes.json new file mode 100644 index 0000000000..43ed42f174 --- /dev/null +++ b/rule_packages/c/BitfieldTypes.json @@ -0,0 +1,44 @@ +{ + "MISRA-C-2012": { + "RULE-6-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Declaring bit-fields on types other than appropriate ones causes implementation-specific or undefined behavior.", + "kind": "problem", + "name": "Bit-fields shall only be declared with an appropriate type", + "precision": "very-high", + "severity": "error", + "short_name": "BitFieldsShallOnlyBeDeclaredWithAnAppropriateType", + "shared_implementation_short_name": "BitFieldShallHaveAnAppropriateType", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Bit-fields shall only be declared with an appropriate type" + }, + "RULE-6-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Single-bit named bit fields carry no useful information and therefore should not be declared or used.", + "kind": "problem", + "name": "Single-bit named bit fields shall not be of a signed type", + "precision": "very-high", + "severity": "error", + "short_name": "SingleBitNamedBitFieldsOfASignedType", + "shared_implementation_short_name": "NamedBitFieldsWithSignedIntegerType", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Single-bit named bit fields shall not be of a signed type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/BitfieldTypes2.json b/rule_packages/c/BitfieldTypes2.json new file mode 100644 index 0000000000..957e9bb729 --- /dev/null +++ b/rule_packages/c/BitfieldTypes2.json @@ -0,0 +1,24 @@ +{ + "MISRA-C-2012": { + "RULE-6-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Type punning on a union with bit fields relies on implementation-specific alignment behavior.", + "kind": "problem", + "name": "A bit field shall not be declared as a member of a union", + "precision": "very-high", + "severity": "warning", + "short_name": "BitFieldDeclaredAsMemberOfAUnion", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A bit field shall not be declared as a member of a union" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Concurrency1.json b/rule_packages/c/Concurrency1.json index 15e38e941d..9daa2a83be 100644 --- a/rule_packages/c/Concurrency1.json +++ b/rule_packages/c/Concurrency1.json @@ -15,7 +15,12 @@ "shared_implementation_short_name": "GuardAccessToBitFields", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -35,7 +40,12 @@ "short_name": "RaceConditionsWhenUsingLibraryFunctions", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -55,7 +65,12 @@ "short_name": "DoNotCallSignalInMultithreadedProgram", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" ], "implementation_scope": { "description": "This implementation does not consider threads created function pointers." diff --git a/rule_packages/c/Concurrency2.json b/rule_packages/c/Concurrency2.json index d9102a07df..d9e364d046 100644 --- a/rule_packages/c/Concurrency2.json +++ b/rule_packages/c/Concurrency2.json @@ -15,7 +15,12 @@ "shared_implementation_short_name": "PreventDeadlockByLockingInPredefinedOrder", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -36,7 +41,12 @@ "shared_implementation_short_name": "WrapSpuriousFunctionInLoop", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/c/Concurrency3.json b/rule_packages/c/Concurrency3.json index a57b73f034..6328f6b43c 100644 --- a/rule_packages/c/Concurrency3.json +++ b/rule_packages/c/Concurrency3.json @@ -15,7 +15,12 @@ "shared_implementation_short_name": "DoNotAllowAMutexToGoOutOfScopeWhileLocked", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "This implementation does not allow for thread synchronization to be performed in subroutines. All synchronization must be performed within the context of the other thread management functions." @@ -31,7 +36,12 @@ "shared_implementation_short_name": "DoNotDestroyAMutexWhileItIsLocked", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -52,7 +62,12 @@ "shared_implementation_short_name": "PreserveSafetyWhenUsingConditionVariables", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ], "implementation_scope": { "description": "This implementation does not attempt to identify unique condition variables and instead advocates for the usage of `cnd_broadcast`." @@ -75,7 +90,12 @@ "short_name": "WrapFunctionsThatCanFailSpuriouslyInLoop", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ], "implementation_scope": { "description": "This implementation does not attempt to identify a relationship between the condition variable and the atomic operation." diff --git a/rule_packages/c/Concurrency4.json b/rule_packages/c/Concurrency4.json index 65a17ed2d7..b981ebaa8b 100644 --- a/rule_packages/c/Concurrency4.json +++ b/rule_packages/c/Concurrency4.json @@ -14,19 +14,23 @@ "short_name": "CleanUpThreadSpecificStorage", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "This query does not attempt to ensure that the deallocation function in fact deallocates memory and instead assumes the contract is valid. Additionally, this query requires that all `tss_create` calls are bookended by calls to `tss_delete`, even if a thread is not created." } - } ], "title": "Clean up thread-specific storage" }, "CON34-C": { "properties": { - "obligation": "rule" + "obligation": "rule" }, "queries": [ { @@ -38,7 +42,12 @@ "short_name": "AppropriateThreadObjectStorageDurations", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "This query does not consider Windows implementations or OpenMP implementations. This query is primarily about excluding cases wherein the storage duration of a variable is appropriate. As such, this query is not concerned if the appropriate synchronization mechanisms are used, such as sequencing calls to `thrd_join` and `free`. An audit query is supplied to handle some of those cases." @@ -54,7 +63,12 @@ "tags": [ "external/cert/audit", "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/c/Concurrency5.json b/rule_packages/c/Concurrency5.json index 67707201fd..d1a685dd34 100644 --- a/rule_packages/c/Concurrency5.json +++ b/rule_packages/c/Concurrency5.json @@ -12,9 +12,15 @@ "precision": "high", "severity": "error", "short_name": "ThreadWasPreviouslyJoinedOrDetached", + "shared_implementation_short_name": "JoinOrDetachThreadOnlyOnce", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ], "implementation_scope": { "description": "This query considers problematic usages of join and detach irrespective of the execution of the program and other synchronization and interprocess communication mechanisms that may be used." @@ -37,7 +43,12 @@ "short_name": "AtomicVariableTwiceInExpression", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/c/Concurrency6.json b/rule_packages/c/Concurrency6.json new file mode 100644 index 0000000000..cfb793877e --- /dev/null +++ b/rule_packages/c/Concurrency6.json @@ -0,0 +1,132 @@ +{ + "MISRA-C-2012": { + "DIR-5-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Circular waits leading to thread deadlocks may be avoided by locking in a predefined order.", + "kind": "problem", + "name": "There shall be no deadlocks between threads", + "precision": "very-high", + "severity": "error", + "short_name": "NotNoDeadlocksBetweenThreads", + "shared_implementation_short_name": "PreventDeadlockByLockingInPredefinedOrder", + "tags": [ + "external/misra/c/2012/amendment4", + "correctness", + "concurrency" + ] + } + ], + "title": "There shall be no deadlocks between threads" + }, + "DIR-5-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Creating threads within threads creates uncertainty in program behavior and concurrency overhead costs.", + "kind": "problem", + "name": "Threads shall not be created by other threads", + "precision": "high", + "severity": "error", + "short_name": "ThreadCreatedByThread", + "tags": [ + "external/misra/c/2012/amendment4", + "correctness", + "maintainability", + "concurrency", + "performance" + ] + }, + { + "description": "Creating threads outside of a well-defined program start-up phase creates uncertainty in program behavior and concurrency overhead costs.", + "kind": "problem", + "name": "There shall be no dynamic thread creation", + "precision": "low", + "severity": "error", + "short_name": "BannedDynamicThreadCreation", + "tags": [ + "external/misra/c/2012/amendment4", + "external/misra/c/audit", + "correctness", + "maintainability", + "concurrency", + "performance" + ] + } + ], + "title": "There shall be no dynamic thread creation" + }, + "RULE-12-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Accessing a member of an atomic structure or union results in undefined behavior.", + "kind": "problem", + "name": "Structure and union members of atomic objects shall not be directly accessed", + "precision": "very-high", + "severity": "error", + "short_name": "AtomicAggregateObjectDirectlyAccessed", + "tags": [ + "external/misra/c/2012/amendment4", + "correctness", + "concurrency" + ] + } + ], + "title": "Structure and union members of atomic objects shall not be directly accessed" + }, + "RULE-21-25": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Only the memory ordering of 'memory_order_seq_cst' is fully portable and consistent.", + "kind": "path-problem", + "name": "All memory synchronization operations shall be executed in sequentially consistent order", + "precision": "very-high", + "severity": "error", + "short_name": "InvalidMemoryOrderArgument", + "tags": [ + "external/misra/c/2012/amendment4", + "correctness", + "concurrency" + ] + } + ], + "title": "All memory synchronization operations shall be executed in sequentially consistent order" + }, + "RULE-22-11": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Joining or detaching a previously joined or detached thread can lead to undefined program behavior.", + "kind": "problem", + "name": "A thread that was previously either joined or detached shall not be subsequently joined nor detached", + "precision": "high", + "severity": "error", + "short_name": "ThreadPreviouslyJoinedOrDetached", + "shared_implementation_short_name": "JoinOrDetachThreadOnlyOnce", + "tags": [ + "external/misra/c/2012/amendment4", + "correctness", + "concurrency" + ], + "implementation_scope": { + "description": "This query considers problematic usages of join and detach irrespective of the execution of the program and other synchronization and interprocess communication mechanisms that may be used." + } + } + ], + "title": "A thread that was previously either joined or detached shall not be subsequently joined nor detached" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Concurrency7.json b/rule_packages/c/Concurrency7.json new file mode 100644 index 0000000000..bda8881934 --- /dev/null +++ b/rule_packages/c/Concurrency7.json @@ -0,0 +1,49 @@ +{ + "MISRA-C-2012": { + "RULE-9-7": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Atomic objects that do not have static storage duration shall be initialized with a value or by using 'atomic_init()'.", + "kind": "problem", + "name": "Atomic objects shall be appropriately initialized before being accessed", + "precision": "high", + "severity": "warning", + "short_name": "UninitializedAtomicObject", + "tags": [ + "concurrency", + "external/misra/c/2012/amendment4" + ], + "implementation_scope": { + "description": "This query tracks which functions may start threads, either indirectly or directly (\"thread spawning functions\"), and checks for local atomic variables that are not passed by address into `atomic_init` or other function calls, before such a thread spawning function is called.", + "items": [] + } + } + ], + "title": "Atomic objects shall be appropriately initialized before being accessed" + }, + "RULE-21-26": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The Standard Library function mtx_timedlock() shall only be invoked on mutex objects of appropriate mutex type.", + "kind": "path-problem", + "name": "The Standard Library function mtx_timedlock() shall only be invoked on mutexes of type mtx_timed", + "precision": "high", + "severity": "error", + "short_name": "TimedlockOnInappropriateMutexType", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "The Standard Library function mtx_timedlock() shall only be invoked on mutex objects of appropriate mutex type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Concurrency8.json b/rule_packages/c/Concurrency8.json new file mode 100644 index 0000000000..2dc5d48042 --- /dev/null +++ b/rule_packages/c/Concurrency8.json @@ -0,0 +1,115 @@ +{ + "MISRA-C-2012": { + "RULE-22-12": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Thread objects, thread synchronization objects, and thread-specific storage pointers shall only be accessed by the appropriate Standard Library functions.", + "kind": "problem", + "name": "Standard library threading objects (mutexes, threads, etc.) shall only be accessed by the appropriate Standard Library functions", + "precision": "very-high", + "severity": "error", + "short_name": "NonstandardUseOfThreadingObject", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "Thread objects, thread synchronization objects, and thread-specific storage pointers shall only be accessed by the appropriate Standard Library functions" + }, + "RULE-22-13": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Thread objects, thread synchronization objects, and thread specific storage pointers shall have appropriate storage duration.", + "kind": "problem", + "name": "Threading objects (mutexes, threads, etc). shall have not have automatic or thread storage duration", + "precision": "very-high", + "severity": "error", + "short_name": "ThreadingObjectWithInvalidStorageDuration", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "Thread objects, thread synchronization objects, and thread specific storage pointers shall have appropriate storage duration" + }, + "RULE-22-14": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Mutex and condition objects shall be initialized with the standard library functions before using them.", + "kind": "problem", + "name": "Thread synchronization objects shall be initialized before being accessed", + "precision": "high", + "severity": "error", + "short_name": "MutexNotInitializedBeforeUse", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + }, + { + "description": "Mutex and condition objects initialized inside of threads may result in indeterministic state.", + "kind": "problem", + "name": "Thread synchronization objects shall be initialized deterministically", + "precision": "high", + "severity": "recommendation", + "short_name": "MutexInitializedInsideThread", + "tags": [ + "readability", + "maintainability", + "concurrency", + "external/misra/c/2012/amendment4" + ] + }, + { + "description": "Mutexes shall be initialized with a valid mutex type.", + "kind": "problem", + "name": "Mutexes shall be initialized with a valid mutex type", + "precision": "high", + "severity": "error", + "short_name": "MutexInitWithInvalidMutexType", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "Thread synchronization objects shall be initialized before being accessed" + }, + "RULE-22-16": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Mutex not unlocked by thread on all execution paths in current thread after being locked.", + "kind": "problem", + "name": "All mutex objects locked by a thread shall be explicitly unlocked by the same thread", + "precision": "high", + "severity": "error", + "short_name": "MutexObjectsNotAlwaysUnlocked", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "All mutex objects locked by a thread shall be explicitly unlocked by the same thread" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Concurrency9.json b/rule_packages/c/Concurrency9.json new file mode 100644 index 0000000000..6ae1df8173 --- /dev/null +++ b/rule_packages/c/Concurrency9.json @@ -0,0 +1,158 @@ +{ + "MISRA-C-2012": { + "DIR-5-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Threads shall not access the same memory location concurrently without utilization of thread synchronization objects.", + "kind": "problem", + "name": "There shall be no data races between threads", + "precision": "medium", + "severity": "error", + "short_name": "PossibleDataRaceBetweenThreads", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "There shall be no data races between threads" + }, + "RULE-22-15": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Thread synchronization objects and thread-specific storage pointers shall not be destroyed until after all threads accessing them have terminated.", + "kind": "problem", + "name": "Thread synchronization objects and thread-specific storage pointers shall not be disposed unsafely", + "precision": "medium", + "severity": "error", + "short_name": "ThreadResourceDisposedBeforeThreadsJoined", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "Thread synchronization objects and thread-specific storage pointers shall not be destroyed until after all threads accessing them have terminated" + }, + "RULE-22-17": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked before.", + "kind": "problem", + "name": "No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked", + "precision": "high", + "severity": "error", + "short_name": "InvalidOperationOnUnlockedMutex", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked before" + }, + "RULE-22-18": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Mutexes initialized with mtx_init() without mtx_recursive shall not be locked by a thread that has previously locked it.", + "kind": "problem", + "name": "Non-recursive mutexes shall not be recursively locked", + "precision": "very-high", + "severity": "error", + "short_name": "NonRecursiveMutexRecursivelyLocked", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + }, + { + "description": "Mutexes that may be initialized without mtx_recursive shall not be locked by a thread that may have previously locked it.", + "kind": "problem", + "name": "(Audit) Non-recursive mutexes shall not be recursively locked", + "precision": "high", + "severity": "error", + "short_name": "NonRecursiveMutexRecursivelyLockedAudit", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4", + "external/misra/audit" + ] + } + ], + "title": "Non-recursive mutexes shall not be recursively locked" + }, + "RULE-22-19": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Standard library functions cnd_wait() and cnd_timedwait() shall specify the same mutex object for each condition object in all calls.", + "kind": "problem", + "name": "A condition variable shall be associated with at most one mutex object", + "precision": "very-high", + "severity": "error", + "short_name": "ConditionVariableUsedWithMultipleMutexes", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "A condition variable shall be associated with at most one mutex object" + }, + "RULE-22-20": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Thread specific storage pointers shall be initialized with the standard library functions before using them.", + "kind": "problem", + "name": "Thread-specific storage pointers shall be created before being accessed", + "precision": "high", + "severity": "error", + "short_name": "ThreadStorageNotInitializedBeforeUse", + "tags": [ + "correctness", + "concurrency", + "external/misra/c/2012/amendment4" + ] + }, + { + "description": "Thread specific storage pointers initialized inside of threads may result in indeterministic state.", + "kind": "problem", + "name": "Thread specific storage pointers shall be initialized deterministically", + "precision": "very-high", + "severity": "recommendation", + "short_name": "ThreadStoragePointerInitializedInsideThread", + "tags": [ + "readability", + "maintainability", + "concurrency", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "Thread-specific storage pointers shall be created before being accessed" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Contracts.json b/rule_packages/c/Contracts.json new file mode 100644 index 0000000000..0d2e0a97bd --- /dev/null +++ b/rule_packages/c/Contracts.json @@ -0,0 +1,94 @@ +{ + "CERT-C": { + "MSC40-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Inlined external functions are prohibited by the language standard from defining modifiable static or thread storage objects, or referencing identifiers with internal linkage.", + "kind": "problem", + "name": "Do not violate inline linkage constraints", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotViolateInLineLinkageConstraints", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "This query only considers the constraints related to inline extern functions." + } + } + ], + "title": "Do not violate constraints" + } + }, + "MISRA-C-2012": { + "DIR-4-11": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Range, domain or pole errors in math functions may return unexpected values, trigger floating-point exceptions or set unexpected error modes.", + "kind": "problem", + "name": "The validity of values passed to `math.h` library functions shall be checked", + "precision": "high", + "severity": "error", + "short_name": "CheckMathLibraryFunctionParameters", + "shared_implementation_short_name": "UncheckedRangeDomainPoleErrors", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query identifies possible domain, pole and range errors on a selection of C standard library fuctions from math.h." + } + }, + { + "description": "Trigonometric periodic functions have significantly less precision when called with large floating-point values.", + "kind": "problem", + "name": "The validity of values passed to trigonometric functions shall be checked", + "precision": "high", + "severity": "warning", + "short_name": "LowPrecisionPeriodicTrigonometricFunctionCall", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "The validity of values passed to library functions shall be checked" + }, + "DIR-4-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A function (whether it is part of the standard library, a third party library or a user defined function) may provide some means of indicating the occurrence of an error. This may be via a global error flag, a parametric error flag, a special return value or some other means. Whenever such a mechanism is provided by a function the calling program shall check for the indication of an error as soon as the function returns.", + "kind": "problem", + "name": "If a function generates error information, then that error information shall be tested", + "precision": "very-high", + "severity": "recommendation", + "short_name": "FunctionErrorInformationUntested", + "shared_implementation_short_name": "FunctionErroneousReturnValueNotTested", + "tags": [ + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query enforces checking on some C standard library functions that may return error codes." + } + } + ], + "title": "If a function returns error information, then that error information shall be tested" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Contracts1.json b/rule_packages/c/Contracts1.json index 2882bb617f..65ffdc5e71 100644 --- a/rule_packages/c/Contracts1.json +++ b/rule_packages/c/Contracts1.json @@ -4,38 +4,52 @@ "properties": { "obligation": "rule" }, - "queries": [{ - "description": "Modification of return values of getenv and similar functions results in undefined behaviour.", - "kind": "path-problem", - "name": "Do not modify the return value of certain functions", - "precision": "very-high", - "severity": "warning", - "short_name": "DoNotModifyTheReturnValueOfCertainFunctions", - "shared_implementation_short_name": "ConstLikeReturnValue", - "tags": [ - "correctness" - ] - }], + "queries": [ + { + "description": "Modification of return values of getenv and similar functions results in undefined behaviour.", + "kind": "path-problem", + "name": "Do not modify the return value of certain functions", + "precision": "very-high", + "severity": "warning", + "short_name": "DoNotModifyTheReturnValueOfCertainFunctions", + "shared_implementation_short_name": "ConstLikeReturnValue", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" + ] + } + ], "title": "Do not modify the object referenced by the return value of certain functions" }, "ENV31-C": { "properties": { "obligation": "rule" }, - "queries": [{ - "description": "Using the envp pointer after environment modifications can result in undefined behavior.", - "kind": "problem", - "name": "Do not rely on an env pointer following an operation that may invalidate it", - "precision": "high", - "severity": "error", - "short_name": "EnvPointerIsInvalidAfterCertainOperations", - "tags": [ - "correctness" - ], - "implementation_scope": { - "description": "The rule is enforced in the context of a single function." + "queries": [ + { + "description": "Using the envp pointer after environment modifications can result in undefined behavior.", + "kind": "problem", + "name": "Do not rely on an env pointer following an operation that may invalidate it", + "precision": "high", + "severity": "error", + "short_name": "EnvPointerIsInvalidAfterCertainOperations", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } } - }], + ], "title": "Do not rely on an environment pointer following an operation that may invalidate it" } } diff --git a/rule_packages/c/Contracts2.json b/rule_packages/c/Contracts2.json index f3a1c10d34..6c1bf77de2 100644 --- a/rule_packages/c/Contracts2.json +++ b/rule_packages/c/Contracts2.json @@ -13,7 +13,12 @@ "severity": "error", "short_name": "ExitHandlersMustReturnNormally", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ] } ], @@ -33,7 +38,12 @@ "short_name": "DoNotStorePointersReturnedByEnvFunctions", "shared_implementation_short_name": "InvalidatedEnvStringPointers", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -45,7 +55,12 @@ "short_name": "DoNotStorePointersReturnedByEnvironmentFunWarn", "shared_implementation_short_name": "InvalidatedEnvStringPointersWarn", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -60,14 +75,15 @@ "queries": [ { "description": "The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type.", - "kind": "problem", + "kind": "path-problem", "name": "The pointers returned by environment functions should be treated as const", "precision": "very-high", "severity": "error", "short_name": "ValuesReturnedByLocaleSettingUsedAsPtrToConst", "shared_implementation_short_name": "ConstLikeReturnValue", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -87,7 +103,8 @@ "short_name": "CallToSetlocaleInvalidatesOldPointers", "shared_implementation_short_name": "InvalidatedEnvStringPointers", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] }, { @@ -99,7 +116,8 @@ "short_name": "CallToSetlocaleInvalidatesOldPointersWarn", "shared_implementation_short_name": "InvalidatedEnvStringPointersWarn", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/Contracts3.json b/rule_packages/c/Contracts3.json index 8cb997c3b2..0122b858b5 100644 --- a/rule_packages/c/Contracts3.json +++ b/rule_packages/c/Contracts3.json @@ -4,46 +4,61 @@ "properties": { "obligation": "required" }, - "queries": [{ - "description": "The value of errno shall only be tested when the last function to be called was an errno-setting-function. Testing the value in these conditions does not guarantee the absence of an errors.", - "kind": "problem", - "name": "The value of errno shall only be tested when the last called function is errno-setting", - "precision": "high", - "severity": "warning", - "short_name": "OnlyTestErrnoRightAfterErrnoSettingFunction", - "tags": ["correctness"] - }], + "queries": [ + { + "description": "The value of errno shall only be tested when the last function to be called was an errno-setting-function. Testing the value in these conditions does not guarantee the absence of an errors.", + "kind": "problem", + "name": "The value of errno shall only be tested when the last called function is errno-setting", + "precision": "high", + "severity": "warning", + "short_name": "OnlyTestErrnoRightAfterErrnoSettingFunction", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], "title": "The value of errno shall only be tested when the last function to be called was an errno-setting-function" }, "RULE-22-8": { "properties": { "obligation": "required" }, - "queries": [{ - "description": "The value of errno shall be set to zero prior to a call to an errno-setting-function. Not setting the value leads to incorrectly identifying errors.", - "kind": "problem", - "name": "The value of errno shall be set to zero prior to a call to an errno-setting-function", - "precision": "very-high", - "severity": "error", - "short_name": "ErrnoSetToZeroPriorToCall", - "tags": ["correctness"] - }], + "queries": [ + { + "description": "The value of errno shall be set to zero prior to a call to an errno-setting-function. Not setting the value leads to incorrectly identifying errors.", + "kind": "problem", + "name": "The value of errno shall be set to zero prior to a call to an errno-setting-function", + "precision": "very-high", + "severity": "error", + "short_name": "ErrnoSetToZeroPriorToCall", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], "title": "The value of errno shall be set to zero prior to a call to an errno-setting-function" }, "RULE-22-9": { "properties": { "obligation": "required" }, - "queries": [{ - "description": "The value of errno shall be tested against zero after calling an errno-setting-function. Not testing the value leads to unidentified errors.", - "kind": "problem", - "name": "The value of errno shall be tested against zero after calling an errno-setting-function", - "precision": "very-high", - "severity": "error", - "short_name": "ErrnoSetToZeroAfterCall", - "tags": ["correctness"] - }], + "queries": [ + { + "description": "The value of errno shall be tested against zero after calling an errno-setting-function. Not testing the value leads to unidentified errors.", + "kind": "problem", + "name": "The value of errno shall be tested against zero after calling an errno-setting-function", + "precision": "very-high", + "severity": "error", + "short_name": "ErrnoSetToZeroAfterCall", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], "title": "The value of errno shall be tested against zero after calling an errno-setting-function" } } -} +} \ No newline at end of file diff --git a/rule_packages/c/Contracts4.json b/rule_packages/c/Contracts4.json new file mode 100644 index 0000000000..a62e9d1762 --- /dev/null +++ b/rule_packages/c/Contracts4.json @@ -0,0 +1,76 @@ +{ + "CERT-C": { + "ERR30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not rely solely on errno to determine if en error occurred in setlocale.", + "kind": "problem", + "name": "Do not rely solely on errno to determine if en error occurred in setlocale", + "precision": "high", + "severity": "error", + "short_name": "SetlocaleMightSetErrno", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + }, + { + "description": "Do not check errno before the function return value. Failing to do so might invalidate the error detection.", + "kind": "problem", + "name": "Do not check errno before the function return value", + "precision": "high", + "severity": "error", + "short_name": "ErrnoReadBeforeReturn", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + }, + { + "description": "After calling an errno-setting function, check errno before calling any other function. Failing to do so might end in errno being overwritten.", + "kind": "problem", + "name": "Do not call a function before checking errno", + "precision": "high", + "severity": "error", + "short_name": "FunctionCallBeforeErrnoCheck", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + }, + { + "description": "Set errno to zero prior to each call to an errno-setting function. Failing to do so might end in spurious errno values.", + "kind": "problem", + "name": "Errno is not set to zero prior to an errno-setting call", + "precision": "high", + "severity": "error", + "short_name": "ErrnoNotSetToZero", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + } + ], + "title": "Take care when reading errno" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Contracts5.json b/rule_packages/c/Contracts5.json new file mode 100644 index 0000000000..d4b38b5756 --- /dev/null +++ b/rule_packages/c/Contracts5.json @@ -0,0 +1,58 @@ +{ + "CERT-C": { + "ERR32-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not rely on indeterminate values of errno. This may result in undefined behavior.", + "kind": "problem", + "name": "Do not rely on indeterminate values of errno", + "precision": "high", + "severity": "error", + "short_name": "DoNotRelyOnIndeterminateValuesOfErrno", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } + } + ], + "title": "Do not rely on indeterminate values of errno" + }, + "ERR33-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Detect and handle standard library errors. Undetected failures can lead to unexpected or undefined behavior.", + "kind": "problem", + "name": "Detect and handle standard library errors", + "precision": "high", + "severity": "error", + "short_name": "DetectAndHandleStandardLibraryErrors", + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } + } + ], + "title": "Detect and handle standard library errors" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Contracts6.json b/rule_packages/c/Contracts6.json new file mode 100644 index 0000000000..d89617d6dc --- /dev/null +++ b/rule_packages/c/Contracts6.json @@ -0,0 +1,73 @@ +{ + "CERT-C": { + "EXP40-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not modify constant objects. This may result in undefined behavior.", + "kind": "path-problem", + "name": "Do not modify constant objects", + "precision": "high", + "severity": "error", + "short_name": "DoNotModifyConstantObjects", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "The implementation does not consider pointer aliasing via multiple indirection." + } + } + ], + "title": "Do not modify constant objects" + } + }, + "MISRA-C-2012": { + "RULE-17-5": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The function argument corresponding to an array parameter shall have an appropriate number of elements.", + "kind": "problem", + "name": "An array founction argument shall have an appropriate number of elements", + "precision": "high", + "severity": "error", + "short_name": "ArrayFunctionArgumentNumberOfElements", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The function argument corresponding to an array parameter shall have an appropriate number of elements" + }, + "RULE-17-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The value returned by a function having non-void return type shall be used or cast to void.", + "kind": "problem", + "name": "Return values should be used or cast to void", + "precision": "very-high", + "severity": "error", + "short_name": "ValueReturnedByAFunctionNotUsed", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The value returned by a function having non-void return type shall be used or cast to void" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Contracts7.json b/rule_packages/c/Contracts7.json new file mode 100644 index 0000000000..95df01ca32 --- /dev/null +++ b/rule_packages/c/Contracts7.json @@ -0,0 +1,107 @@ +{ + "CERT-C": { + "MSC33-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "The data passed to the asctime() function is invalid. This can lead to buffer overflow.", + "kind": "problem", + "name": "Do not pass invalid data to the asctime() function", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotPassInvalidDataToTheAsctimeFunction", + "tags": [ + "security", + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p27", + "external/cert/level/l1" + ] + } + ], + "title": "Do not pass invalid data to the asctime() function" + }, + "MSC39-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not call va_arg() on a va_list that has an indeterminate value.", + "kind": "problem", + "name": "Do not call va_arg() on a va_list that has an indeterminate value", + "precision": "high", + "severity": "error", + "short_name": "DoNotCallVaArgOnAVaListThatHasAnIndeterminateValue", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ] + } + ], + "title": "Do not call va_arg() on a va_list that has an indeterminate value" + } + }, + "MISRA-C-2012": { + "RULE-12-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand.", + "kind": "problem", + "name": "The right operand of a shift shall be smaller then the width in bits of the left operand", + "precision": "very-high", + "severity": "error", + "short_name": "RightHandOperandOfAShiftRange", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand" + }, + "RULE-19-1": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "An object shall not be copied or assigned to an overlapping object.", + "kind": "problem", + "name": "An object shall not be assigned to an overlapping object", + "precision": "high", + "severity": "error", + "short_name": "ObjectAssignedToAnOverlappingObject", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "An object shall not be copied to an overlapping object.", + "kind": "problem", + "name": "An object shall not be copied to an overlapping object", + "precision": "high", + "severity": "error", + "short_name": "ObjectCopiedToAnOverlappingObject", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An object shall not be assigned or copied to an overlapping object" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/DeadCode.json b/rule_packages/c/DeadCode.json new file mode 100644 index 0000000000..d8e80d14d1 --- /dev/null +++ b/rule_packages/c/DeadCode.json @@ -0,0 +1,157 @@ +{ + "MISRA-C-2012": { + "RULE-2-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Unreachable code complicates the program and can indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "A project shall not contain unreachable code", + "precision": "very-high", + "severity": "warning", + "short_name": "UnreachableCode", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query reports basic blocks in the program which are unreachable. For basic blocks within templates, the block is only consider unreachable if it is unreachable in all templates. Code generated by macros is ignored for this query, because it may be the case that basic blocks are reachable only in some expansions." + }, + "shared_implementation_short_name": "UnreachableCode" + } + ], + "title": "A project shall not contain unreachable code" + }, + "RULE-2-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Dead code complicates the program and can indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "There shall be no dead code", + "precision": "very-high", + "severity": "warning", + "short_name": "DeadCode", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "There shall be no dead code" + }, + "RULE-2-3": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Unused type declarations are either redundant or indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "A project should not contain unused type declarations", + "precision": "very-high", + "severity": "warning", + "short_name": "UnusedTypeDeclarations", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ], + "shared_implementation_short_name": "UnusedTypeDeclarations" + } + ], + "title": "A project should not contain unused type declarations" + }, + "RULE-2-4": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Unused tag declarations are either redundant or indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "A project should not contain unused tag declarations", + "precision": "very-high", + "severity": "warning", + "short_name": "UnusedTagDeclaration", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A project should not contain unused tag declarations" + }, + "RULE-2-5": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Unused macro declarations are either redundant or indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "A project should not contain unused macro declarations", + "precision": "very-high", + "severity": "warning", + "short_name": "UnusedMacroDeclaration", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A project should not contain unused macro declarations" + }, + "RULE-2-6": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Unused label declarations are either redundant or indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "A function should not contain unused label declarations", + "precision": "very-high", + "severity": "warning", + "short_name": "UnusedLabelDeclaration", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A function should not contain unused label declarations" + }, + "RULE-2-7": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Unused parameters can indicate a mistake when implementing the function.", + "kind": "problem", + "name": "There should be no unused parameters in functions", + "precision": "very-high", + "severity": "warning", + "short_name": "UnusedParameter", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ], + "shared_implementation_short_name": "UnusedParameter" + } + ], + "title": "There should be no unused parameters in functions" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/DeadCode2.json b/rule_packages/c/DeadCode2.json new file mode 100644 index 0000000000..8b373c31b6 --- /dev/null +++ b/rule_packages/c/DeadCode2.json @@ -0,0 +1,42 @@ +{ + "MISRA-C-2012": { + "RULE-2-8": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Object definitions which are unused should be removed.", + "kind": "problem", + "name": "A project should not contain unused object definitions", + "precision": "very-high", + "severity": "recommendation", + "short_name": "UnusedObjectDefinition", + "tags": [ + "maintainability", + "performance", + "external/misra/c/2012/amendment4" + ] + }, + { + "description": "A strict query which reports all unused object definitions with '__attribute__((unused))'.", + "kind": "problem", + "name": "A project should not contain '__attribute__((unused))' object definitions", + "precision": "very-high", + "severity": "recommendation", + "short_name": "UnusedObjectDefinitionStrict", + "tags": [ + "maintainability", + "performance", + "external/misra/c/2012/amendment4", + "external/misra/c/strict" + ] + } + ], + "title": "A project should not contain unused object definitions", + "implementation_scope": { + "description": "Unused object definitions marked with `__attribute__((unused))` (and `used`, `maybe_used`, `cleanup`) are separately reported under the 'strict' query suite. This is because these attributes strongly indicate the contravention is intentional, and a deviation report alone will not suppress compiler warnings." + } + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Declarations1.json b/rule_packages/c/Declarations1.json index 217e1e077c..dba6a07eeb 100644 --- a/rule_packages/c/Declarations1.json +++ b/rule_packages/c/Declarations1.json @@ -1,56 +1,66 @@ { "CERT-C": { "DCL31-C": { - "properties": { - "obligation": "rule" - }, - "queries": [ - { - "description": "Omission of type specifiers may not be supported by some compilers.", - "kind": "problem", - "name": "Declare identifiers before using them", - "precision": "very-high", - "severity": "error", - "short_name": "DeclareIdentifiersBeforeUsingThem", - "shared_implementation_short_name": "TypeOmitted", - "tags": [ - "correctness", - "readability" - ], - "implementation_scope": { - "description": "This query does not check for implicitly typed parameters, typedefs or member declarations as this is partially compiler checked.", - "items": [] - } + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Omission of type specifiers may not be supported by some compilers.", + "kind": "problem", + "name": "Declare identifiers before using them", + "precision": "very-high", + "severity": "error", + "short_name": "DeclareIdentifiersBeforeUsingThem", + "shared_implementation_short_name": "TypeOmitted", + "tags": [ + "correctness", + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "This query does not check for implicitly typed parameters, typedefs or member declarations as this is partially compiler checked.", + "items": [] } - ], - "title": "Declare identifiers before using them" + } + ], + "title": "Declare identifiers before using them" + }, + "DCL37-C": { + "properties": { + "obligation": "rule" }, - "DCL37-C": { - "properties": { - "obligation": "rule" - }, - "queries": [ - { - "description": "Declaring a reserved identifier can lead to undefined behaviour.", - "kind": "problem", - "name": "Do not declare or define a reserved identifier", - "precision": "very-high", - "severity": "warning", - "short_name": "DoNotDeclareOrDefineAReservedIdentifier", - "shared_implementation_short_name": "DeclaredAReservedIdentifier", - "tags": [ - "correctness", - "maintainability", - "readability" - ], - "implementation_scope": { - "description": "This query does not consider identifiers described in the future library directions section of the standard. This query also checks for any reserved identifier as declared regardless of whether its header file is included or not.", - "items": [] - } + "queries": [ + { + "description": "Declaring a reserved identifier can lead to undefined behaviour.", + "kind": "problem", + "name": "Do not declare or define a reserved identifier", + "precision": "very-high", + "severity": "warning", + "short_name": "DoNotDeclareOrDefineAReservedIdentifier", + "shared_implementation_short_name": "DeclaredAReservedIdentifier", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "This query does not consider identifiers described in the future library directions section of the standard. This query also checks for any reserved identifier as declared regardless of whether its header file is included or not.", + "items": [] } - ], - "title": "Do not declare or define a reserved identifier" - } + } + ], + "title": "Do not declare or define a reserved identifier" + } }, "MISRA-C-2012": { "RULE-21-2": { @@ -69,7 +79,8 @@ "tags": [ "correctness", "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -91,7 +102,8 @@ "tags": [ "correctness", "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query considers the first 31 characters of identifiers as significant, as per C99 and reports the case when names are longer than 31 characters and differ in those characters past the 31 first only. This query does not consider universal or extended source characters.", @@ -102,41 +114,43 @@ "title": "External identifiers shall be distinct" }, "RULE-5-4": { - "properties": { - "obligation": "required" + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Declaring multiple macros with the same name leads to undefined behaviour.", + "kind": "problem", + "name": "Macro identifiers shall be distinct", + "precision": "very-high", + "severity": "warning", + "short_name": "MacroIdentifiersNotDistinct", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query checks the first 63 characters of macro identifiers as significant, as per C99. Distinctness of parameters within the same function like macro are checked by compiler and therefore not checked by this rule.", + "items": [] + } }, - "queries": [ - { - "description": "Declaring multiple macros with the same name leads to undefined behaviour.", - "kind": "problem", - "name": "Macro identifiers shall be distinct", - "precision": "very-high", - "severity": "warning", - "short_name": "MacroIdentifiersNotDistinct", - "tags": [ - "correctness", - "maintainability", - "readability" - ], - "implementation_scope": { - "description": "This query checks the first 63 characters of macro identifiers as significant, as per C99. Distinctness of parameters within the same function like macro are checked by compiler and therefore not checked by this rule.", - "items": [] - } - }, - { - "description": "Macros with the same name as their parameters are less readable.", - "kind": "problem", - "name": "Macro identifiers shall be distinct from paramters", - "precision": "very-high", - "severity": "warning", - "short_name": "MacroIdentifierNotDistinctFromParameter", - "tags": [ - "maintainability", - "readability" - ] - } - ], - "title": "Macro identifiers shall be distinct" - } + { + "description": "Macros with the same name as their parameters are less readable.", + "kind": "problem", + "name": "Macro identifiers shall be distinct from paramters", + "precision": "very-high", + "severity": "warning", + "short_name": "MacroIdentifierNotDistinctFromParameter", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Macro identifiers shall be distinct" + } } } \ No newline at end of file diff --git a/rule_packages/c/Declarations2.json b/rule_packages/c/Declarations2.json index 303965b6b6..c5b827e682 100644 --- a/rule_packages/c/Declarations2.json +++ b/rule_packages/c/Declarations2.json @@ -1,98 +1,123 @@ { "CERT-C": { "DCL38-C": { - "properties": { - "obligation": "rule" - }, - "queries": [ - { - "description": "Structures with flexible array members can be declared in ways that will lead to undefined behaviour.", - "kind": "problem", - "name": "Use the correct syntax when declaring a flexible array member", - "precision": "very-high", - "severity": "error", - "short_name": "DeclaringAFlexibleArrayMember", - "tags": [ - "correctness", - "maintainability", - "readability" - ] - } - ], - "title": "Use the correct syntax when declaring a flexible array member" + "properties": { + "obligation": "rule" }, + "queries": [ + { + "description": "Structures with flexible array members can be declared in ways that will lead to undefined behaviour.", + "kind": "problem", + "name": "Use the correct syntax when declaring a flexible array member", + "precision": "very-high", + "severity": "error", + "short_name": "DeclaringAFlexibleArrayMember", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ] + } + ], + "title": "Use the correct syntax when declaring a flexible array member" + }, "DCL40-C": { "properties": { "obligation": "rule" }, "queries": [ { - "description": "Using nondistinct external identifiers results in undefined behaviour.", - "kind": "problem", - "name": "External identifiers shall be distinct", - "precision": "very-high", - "severity": "warning", - "short_name": "ExcessLengthNamesIdentifiersNotDistinct", - "shared_implementation_short_name": "NotDistinctIdentifier", - "tags": [ - "correctness", - "maintainability", - "readability" - ], - "implementation_scope": { - "description": "This query considers the first 31 characters of identifiers as significant, as per C99 and reports the case when names are longer than 31 characters and differ in those characters past the 31 first only. This query does not consider universal or extended source characters.", - "items": [] - } - }, - { - "description": "Declaring incompatible objects, in other words same named objects of different types, then accessing those objects can lead to undefined behaviour.", - "kind": "problem", - "name": "Do not create incompatible declarations of the same function or object", - "precision": "high", - "severity": "error", - "short_name": "IncompatibleObjectDeclarations", - "tags": [ - "correctness", - "maintainability", - "readability" - ] - }, - { - "description": "Declaring incompatible functions, in other words same named function of different return types or with different numbers of parameters or parameter types, then accessing those functions can lead to undefined behaviour.", - "kind": "problem", - "name": "Do not create incompatible declarations of the same function or object", - "precision": "high", - "severity": "error", - "short_name": "IncompatibleFunctionDeclarations", - "tags": [ - "correctness", - "maintainability", - "readability" - ] + "description": "Using nondistinct external identifiers results in undefined behaviour.", + "kind": "problem", + "name": "External identifiers shall be distinct", + "precision": "very-high", + "severity": "warning", + "short_name": "ExcessLengthNamesIdentifiersNotDistinct", + "shared_implementation_short_name": "NotDistinctIdentifier", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "This query considers the first 31 characters of identifiers as significant, as per C99 and reports the case when names are longer than 31 characters and differ in those characters past the 31 first only. This query does not consider universal or extended source characters.", + "items": [] } + }, + { + "description": "Declaring incompatible objects, in other words same named objects of different types, then accessing those objects can lead to undefined behaviour.", + "kind": "problem", + "name": "Do not create incompatible declarations of the same function or object", + "precision": "high", + "severity": "error", + "short_name": "IncompatibleObjectDeclarations", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + }, + { + "description": "Declaring incompatible functions, in other words same named function of different return types or with different numbers of parameters or parameter types, then accessing those functions can lead to undefined behaviour.", + "kind": "problem", + "name": "Do not create incompatible declarations of the same function or object", + "precision": "high", + "severity": "error", + "short_name": "IncompatibleFunctionDeclarations", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } ], "title": "Do not create incompatible declarations of the same function or object" }, "DCL41-C": { - "properties": { - "obligation": "rule" - }, - "queries": [ - { - "description": "Declaring a variable in a switch statement before the first case label can result in reading uninitialized memory which is undefined behaviour.", - "kind": "problem", - "name": "Do not declare variables inside a switch statement before the first case label", - "precision": "very-high", - "severity": "error", - "short_name": "VariablesInsideSwitchStatement", - "tags": [ - "correctness", - "maintainability", - "readability" - ] - } - ], - "title": "Do not declare variables inside a switch statement before the first case label" - } + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Declaring a variable in a switch statement before the first case label can result in reading uninitialized memory which is undefined behaviour.", + "kind": "problem", + "name": "Do not declare variables inside a switch statement before the first case label", + "precision": "very-high", + "severity": "error", + "short_name": "VariablesInsideSwitchStatement", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" + ] + } + ], + "title": "Do not declare variables inside a switch statement before the first case label" + } } } \ No newline at end of file diff --git a/rule_packages/c/Declarations3.json b/rule_packages/c/Declarations3.json index a22567b237..8c2e0879ff 100644 --- a/rule_packages/c/Declarations3.json +++ b/rule_packages/c/Declarations3.json @@ -14,7 +14,8 @@ "shared_implementation_short_name": "IdentifierHidden", "tags": [ "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query does not consider C90 or C99 definitions of significant name and instead uses full name matches only.", @@ -38,7 +39,8 @@ "short_name": "IdentifiersNotDistinctFromMacroNames", "tags": [ "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -58,7 +60,8 @@ "short_name": "TypedefNameNotUnique", "tags": [ "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -78,7 +81,8 @@ "short_name": "TagNameNotUnique", "tags": [ "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -99,7 +103,8 @@ "shared_implementation_short_name": "TypeOmitted", "tags": [ "correctness", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query does not check for implicitly typed parameters, typedefs or member declarations as this is partially compiler checked.", diff --git a/rule_packages/c/Declarations4.json b/rule_packages/c/Declarations4.json new file mode 100644 index 0000000000..dedc6a73d4 --- /dev/null +++ b/rule_packages/c/Declarations4.json @@ -0,0 +1,126 @@ +{ + "MISRA-C-2012": { + "RULE-8-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Omission of parameter types or names prevents the compiler from doing type checking when those functions are used and therefore may result in undefined behaviour.", + "kind": "problem", + "name": "Function types shall be in prototype form with named parameters", + "precision": "medium", + "severity": "error", + "short_name": "FunctionTypesNotInPrototypeForm", + "shared_implementation_short_name": "FunctionTypesNotInPrototypeFormShared", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query does not check for implicitly typed parameters and checks function declarations and definitions but not function pointer types. This query cannot determine when the keyword void is used in place of no parameter.", + "items": [] + } + } + ], + "title": "Function types shall be in prototype form with named parameters" + }, + "RULE-8-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using different types across the same declarations disallows strong type checking and can lead to undefined behaviour.", + "kind": "problem", + "name": "All declarations of an object shall use the same names and type qualifiers", + "precision": "very-high", + "severity": "error", + "short_name": "DeclarationsOfAnObjectSameNameAndType", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "Using different types across the same declarations disallows strong type checking and can lead to undefined behaviour.", + "kind": "problem", + "name": "All declarations of a function shall use the same names and type qualifiers", + "precision": "very-high", + "severity": "error", + "short_name": "DeclarationsOfAFunctionSameNameAndType", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "All declarations of an object or function shall use the same names and type qualifiers" + }, + "RULE-8-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A compatible declaration shall be visible when an object with external linkage is defined, otherwise program behaviour may be undefined.", + "kind": "problem", + "name": "A compatible declaration shall be visible when an object with external linkage is defined", + "precision": "very-high", + "severity": "error", + "short_name": "CompatibleDeclarationObjectDefined", + "tags": [ + "readability", + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query does not check for the recommendation of declarations in headers.", + "items": [] + } + }, + { + "description": "A compatible declaration shall be visible when a function with external linkage is defined, otherwise program behaviour may be undefined.", + "kind": "problem", + "name": "A compatible declaration shall be visible when a function with external linkage is defined", + "precision": "very-high", + "severity": "error", + "short_name": "CompatibleDeclarationFunctionDefined", + "tags": [ + "readability", + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query does not check for the recommendation of declarations in headers.", + "items": [] + } + } + ], + "title": "A compatible declaration shall be visible when an object or function with external linkage is defined" + }, + "RULE-8-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An identifier with multiple definitions in different translation units leads to undefined behavior.", + "kind": "problem", + "name": "An identifier with external linkage shall have exactly one definition", + "precision": "high", + "severity": "error", + "short_name": "IdentifierWithExternalLinkageOneDefinition", + "shared_implementation_short_name": "IdentifierWithExternalLinkageOneDefinitionShared", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An identifier with external linkage shall have exactly one external definition" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Declarations5.json b/rule_packages/c/Declarations5.json new file mode 100644 index 0000000000..36591e575b --- /dev/null +++ b/rule_packages/c/Declarations5.json @@ -0,0 +1,105 @@ +{ + "MISRA-C-2012": { + "RULE-5-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using nondistinct identifiers results in undefined behaviour.", + "kind": "problem", + "name": "Identifiers declared in the same scope and name space shall be distinct", + "precision": "very-high", + "severity": "warning", + "short_name": "IdentifiersDeclaredInTheSameScopeNotDistinct", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This query considers the first 63 characters of identifiers as significant, as per C99 for nonexternal identifiers and reports the case when names are longer than 63 characters and differ in those characters past the 63 first only. This query does not consider universal or extended source characters.", + "items": [] + } + } + ], + "title": "Identifiers declared in the same scope and name space shall be distinct" + }, + "RULE-8-5": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Declarations in multiple files can lead to unexpected program behaviour.", + "kind": "problem", + "name": "An external object or function shall be declared once in one and only one file", + "precision": "very-high", + "severity": "warning", + "short_name": "ExternalObjectOrFunctionNotDeclaredInOneFile", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An external object or function shall be declared once in one and only one file" + }, + "RULE-8-8": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "If a function has internal linkage then all re-declarations shall include the static storage class specifier to make the internal linkage explicit.", + "kind": "problem", + "name": "If a function has internal linkage then all re-declarations shall include the static storage class", + "precision": "very-high", + "severity": "warning", + "short_name": "MissingStaticSpecifierFunctionRedeclarationC", + "shared_implementation_short_name": "MissingStaticSpecifierFunctionRedeclarationShared", + "tags": [ + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "If an object has internal linkage then all re-declarations shall include the static storage class specifier to make the internal linkage explicit.", + "kind": "problem", + "name": "If an object has internal linkage then all re-declarations shall include the static storage class", + "precision": "very-high", + "severity": "warning", + "short_name": "MissingStaticSpecifierObjectRedeclarationC", + "shared_implementation_short_name": "MissingStaticSpecifierObjectRedeclarationShared", + "tags": [ + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The static storage class specifier shall be used in all declarations of objects and functions that have internal linkage" + }, + "RULE-8-9": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "An identifier declared to be an object or type shall be defined in a block that minimizes its visibility to prevent any accidental use of the identifier.", + "kind": "problem", + "name": "An object should be defined at block scope if its identifier only appears in a single function", + "precision": "high", + "severity": "warning", + "short_name": "UnnecessaryExposedIdentifierDeclarationC", + "shared_implementation_short_name": "UnnecessaryExposedIdentifierDeclarationShared", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An object should be defined at block scope if its identifier only appears in a single function" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Declarations6.json b/rule_packages/c/Declarations6.json new file mode 100644 index 0000000000..198b4e8351 --- /dev/null +++ b/rule_packages/c/Declarations6.json @@ -0,0 +1,153 @@ +{ + "MISRA-C-2012": { + "RULE-17-3": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Omission of type specifiers may not be supported by some compilers. Additionally implicit typing can lead to undefined behaviour.", + "kind": "problem", + "name": "A function shall not be declared implicitly", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionDeclaredImplicitly", + "tags": [ + "correctness", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A function shall not be declared implicitly" + }, + "RULE-18-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The use of flexible array members can lead to unexpected program behaviour.", + "kind": "problem", + "name": "Flexible array members shall not be declared", + "precision": "very-high", + "severity": "error", + "short_name": "FlexibleArrayMembersDeclared", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Flexible array members shall not be declared" + }, + "RULE-5-8": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using non-unique identifiers can lead to developer confusion.", + "kind": "problem", + "name": "Identifiers that define objects or functions with external linkage shall be unique", + "precision": "very-high", + "severity": "error", + "short_name": "IdentifiersWithExternalLinkageNotUnique", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Identifiers that define objects or functions with external linkage shall be unique" + }, + "RULE-5-9": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Using non-unique identifiers can lead to developer confusion.", + "kind": "problem", + "name": "Identifiers that define objects or functions with internal linkage should be unique", + "precision": "very-high", + "severity": "error", + "short_name": "IdentifiersWithInternalLinkageNotUnique", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This rule does not explicitly check for the exception of inline functions in header files across multiple translation units as the CodeQL database already represents these as the same entity." + } + } + ], + "title": "Identifiers that define objects or functions with internal linkage should be unique" + }, + "RULE-8-10": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Declaring an inline function with external linkage can lead to undefined or incorrect program behaviour.", + "kind": "problem", + "name": "An inline function shall be declared with the static storage class", + "precision": "very-high", + "severity": "error", + "short_name": "InlineFunctionNotDeclaredStaticStorage", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An inline function shall be declared with the static storage class" + }, + "RULE-8-11": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Declaring an array without an explicit size disallows the compiler and static checkers from doing array bounds analysis and can lead to less readable, unsafe code.", + "kind": "problem", + "name": "When an array with external linkage is declared, its size should be explicitly specified", + "precision": "very-high", + "severity": "error", + "short_name": "ArrayExternalLinkageSizeExplicitlySpecified", + "tags": [ + "correctness", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "When an array with external linkage is declared, its size should be explicitly specified" + }, + "RULE-8-7": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Declarations with external linkage that are referenced in only one translation unit can indicate an intention to only have those identifiers accessible in that translation unit and accidental future accesses in other translation units can lead to confusing program behaviour.", + "kind": "problem", + "name": "Functions and objects should not be defined with external linkage if they are referenced in only one", + "precision": "very-high", + "severity": "error", + "short_name": "ShouldNotBeDefinedWithExternalLinkage", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Functions and objects should not be defined with external linkage if they are referenced in only one translation unit" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Declarations7.json b/rule_packages/c/Declarations7.json new file mode 100644 index 0000000000..86818cdcb5 --- /dev/null +++ b/rule_packages/c/Declarations7.json @@ -0,0 +1,77 @@ +{ + "CERT-C": { + "DCL39-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Passing a structure with uninitialized fields or padding bytes can cause information to be unintentionally leaked.", + "kind": "problem", + "name": "Avoid information leakage when passing a structure across a trust boundary", + "precision": "medium", + "severity": "error", + "short_name": "InformationLeakageAcrossTrustBoundariesC", + "shared_implementation_short_name": "InformationLeakageAcrossBoundaries", + "tags": [ + "security", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p1", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "The rule does not detect cases where fields may have uninitialized padding but are initialized via an initializer." + } + } + ], + "title": "Avoid information leakage when passing a structure across a trust boundary" + } + }, + "MISRA-C-2012": { + "RULE-18-8": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using a variable length array can lead to unexpected or undefined program behaviour.", + "kind": "problem", + "name": "Variable-length array types shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "VariableLengthArrayTypesUsed", + "tags": [ + "correctness", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Variable-length array types shall not be used" + }, + "RULE-8-12": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using an implicitly specified enumeration constant that is not unique (with respect to an explicitly specified constant) can lead to unexpected program behaviour.", + "kind": "problem", + "name": "Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique", + "precision": "very-high", + "severity": "error", + "short_name": "ValueImplicitEnumerationConstantNotUnique", + "shared_implementation_short_name": "NonUniqueEnumerationConstant", + "tags": [ + "correctness", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Declarations8.json b/rule_packages/c/Declarations8.json new file mode 100644 index 0000000000..6275e32595 --- /dev/null +++ b/rule_packages/c/Declarations8.json @@ -0,0 +1,51 @@ +{ + "CERT-C": { + "DCL30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Declare objects with appropriate storage durations", + "precision": "very-high", + "severity": "error", + "short_name": "AppropriateStorageDurationsStackAdressEscape", + "shared_implementation_short_name": "DoNotCopyAddressOfAutoStorageObjectToOtherObject", + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" + ], + "implementation_scope": { + "description": "The rule checks specifically for pointers to objects with automatic storage duration that are assigned to static storage duration variables." + } + }, + { + "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", + "name": "Declare objects with appropriate storage durations", + "precision": "high", + "severity": "error", + "short_name": "AppropriateStorageDurationsFunctionReturn", + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" + ], + "implementation_scope": { + "description": "The rule checks specifically for pointers to objects with automatic storage duration that are returned by functions or assigned to function output parameters." + } + } + ], + "title": "Declare objects with appropriate storage durations" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Declarations9.json b/rule_packages/c/Declarations9.json new file mode 100644 index 0000000000..ebfcf7c41f --- /dev/null +++ b/rule_packages/c/Declarations9.json @@ -0,0 +1,25 @@ +{ + "MISRA-C-2012": { + "RULE-11-10": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Conversions between types by using an _Atomic void type may result in undefined behavior.", + "kind": "problem", + "name": "The _Atomic qualifier shall not be applied to the incomplete type void", + "precision": "very-high", + "severity": "error", + "short_name": "AtomicQualifierAppliedToVoid", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision", + "external/misra/c/2012/amendment4" + ] + } + ], + "title": "The _Atomic qualifier shall not be applied to the incomplete type void" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/EssentialTypes.json b/rule_packages/c/EssentialTypes.json new file mode 100644 index 0000000000..a8ae26e8c6 --- /dev/null +++ b/rule_packages/c/EssentialTypes.json @@ -0,0 +1,246 @@ +{ + "MISRA-C-2012": { + "RULE-10-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using an inappropriate essential type operand may lead to confusing or unexpected behavior when the operand is converted.", + "kind": "problem", + "name": "Operands shall not be of an inappropriate essential type", + "precision": "very-high", + "severity": "warning", + "short_name": "OperandsOfAnInappropriateEssentialType", + "tags": [ + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "Using pointer types with logical operators should be avoid because it can cause confusing behavior.", + "kind": "problem", + "name": "Logical operators should not be used with pointer types", + "precision": "very-high", + "severity": "warning", + "short_name": "PointerTypeOnLogicalOperator", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Operands shall not be of an inappropriate essential type" + }, + "RULE-10-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations.", + "kind": "problem", + "name": "Inappropriate use of essentially character type operands in addition and subtraction operations", + "precision": "very-high", + "severity": "error", + "short_name": "AdditionSubtractionOnEssentiallyCharType", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations" + }, + "RULE-10-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category.", + "kind": "problem", + "name": "Do not assign to an object with a different essential type category or narrower essential type", + "precision": "very-high", + "severity": "warning", + "short_name": "AssignmentOfIncompatibleEssentialType", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category" + }, + "RULE-10-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category.", + "kind": "problem", + "name": "Operator with usual arithmetic conversions shall have operands with the same essential type category", + "precision": "very-high", + "severity": "warning", + "short_name": "OperandsWithMismatchedEssentialTypeCategory", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category" + }, + "RULE-10-5": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Casting the value of an expression to an inappropriate essential type may lead to confusing or unexpected behavior in the way the value is converted.", + "kind": "problem", + "name": "The value of an expression should not be cast to an inappropriate essential type", + "precision": "very-high", + "severity": "warning", + "short_name": "InappropriateEssentialTypeCast", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The value of an expression should not be cast to an inappropriate essential type" + }, + "RULE-10-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Assigning a composite expression to an object with wider essential type can cause some unexpected conversions.", + "kind": "problem", + "name": "The value of a composite expression shall not be assigned to an object with wider essential type", + "precision": "very-high", + "severity": "warning", + "short_name": "AssignmentToWiderEssentialType", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The value of a composite expression shall not be assigned to an object with wider essential type" + }, + "RULE-10-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "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.", + "kind": "problem", + "name": "Implicit conversion of composite expression operand to wider essential type", + "precision": "very-high", + "severity": "warning", + "short_name": "ImplicitConversionOfCompositeExpression", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "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" + }, + "RULE-10-8": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The value of a composite expression shall not be cast to a different essential type category or a wider essential type.", + "kind": "problem", + "name": "Composite expression explicitly casted to wider or different essential type", + "precision": "very-high", + "severity": "warning", + "short_name": "InappropriateCastOfCompositeExpression", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The value of a composite expression shall not be cast to a different essential type category or a wider essential type" + }, + "RULE-14-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A floating point loop counter can cause confusing behavior when incremented.", + "kind": "problem", + "name": "A loop counter shall not have essentially floating type", + "precision": "very-high", + "severity": "warning", + "short_name": "LoopOverEssentiallyFloatType", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A loop counter shall not have essentially floating type" + }, + "RULE-21-14": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using memcmp to compare null terminated strings may give unexpected results because memcmp compares by size with no consideration for the null terminator.", + "kind": "path-problem", + "name": "The Standard Library function memcmp shall not be used to compare null terminated strings", + "precision": "very-high", + "severity": "error", + "short_name": "MemcmpUsedToCompareNullTerminatedStrings", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The Standard Library function memcmp shall not be used to compare null terminated strings" + }, + "RULE-21-16": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type.", + "kind": "problem", + "name": "Do not use memcmp on pointers to characters or composite types such as structs and unions", + "precision": "very-high", + "severity": "error", + "short_name": "MemcmpOnInappropriateEssentialTypeArgs", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/EssentialTypes2.json b/rule_packages/c/EssentialTypes2.json new file mode 100644 index 0000000000..5292eccdb8 --- /dev/null +++ b/rule_packages/c/EssentialTypes2.json @@ -0,0 +1,47 @@ +{ + "MISRA-C-2012": { + "RULE-21-22": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "All operand arguments to any type-generic macros in shall have an appropriate essential type.", + "kind": "problem", + "name": "All operand arguments to type-generic macros in shall have an appropriate essential type", + "precision": "high", + "severity": "error", + "short_name": "TgMathArgumentWithInvalidEssentialType", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "description": "The CodeQL database may not contain the necessary information to determine the essential type of literal macro arguments such as character literals." + }, + "title": "All operand arguments to any type-generic macros in shall have an appropriate essential type" + }, + "RULE-21-23": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "All operand arguments to any multi-argument type-generic macros in shall have the same standard type.", + "kind": "problem", + "name": "Operand arguments for an invocation of a type-generic macro shall have the same standard type", + "precision": "high", + "severity": "error", + "short_name": "TgMathArgumentsWithDifferingStandardType", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "All operand arguments to any multi-argument type-generic macros in shall have the same standard type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Expressions.json b/rule_packages/c/Expressions.json index 9d1f8b16a7..9be722b761 100644 --- a/rule_packages/c/Expressions.json +++ b/rule_packages/c/Expressions.json @@ -13,7 +13,12 @@ "severity": "error", "short_name": "DoNotCallFunctionPointerWithIncompatibleType", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "This query raises a result for a function assigned to a function pointer of an incompatible type even if the function pointer is never eventually called." @@ -27,7 +32,12 @@ "severity": "error", "short_name": "DoNotCallFunctionsWithIncompatibleArguments", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -39,7 +49,12 @@ "short_name": "CallPOSIXOpenWithCorrectArgumentCount", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "The analysis of invalid parameter count passed to POSIX open calls only applies when the value of the flags argument is computed locally." @@ -62,7 +77,12 @@ "short_name": "DoNotUseABitwiseOperatorWithABooleanLikeOperand", "tags": [ "maintainability", - "readability" + "readability", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/c/Expressions2.json b/rule_packages/c/Expressions2.json new file mode 100644 index 0000000000..d639ae2c34 --- /dev/null +++ b/rule_packages/c/Expressions2.json @@ -0,0 +1,28 @@ +{ + "CERT-C": { + "EXP16-C": { + "properties": { + "obligation": "recommendation" + }, + "queries": [ + { + "description": "Comparing function pointers to a constant value is not reliable and likely indicates a programmer error.", + "kind": "problem", + "name": "Do not compare function pointers to constant values", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotCompareFunctionPointersToConstantValues", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" + ] + } + ], + "title": "Do not compare function pointers to constant values" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/FloatingTypes.json b/rule_packages/c/FloatingTypes.json new file mode 100644 index 0000000000..17690574e5 --- /dev/null +++ b/rule_packages/c/FloatingTypes.json @@ -0,0 +1,104 @@ +{ + "CERT-C": { + "FLP32-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Range, domain or pole errors in math functions may return unexpected values, trigger floating-point exceptions or set unexpected error modes.", + "kind": "problem", + "name": "Prevent or detect domain and range errors in math functions", + "precision": "high", + "severity": "error", + "short_name": "UncheckedRangeDomainPoleErrors", + "shared_implementation_short_name": "UncheckedRangeDomainPoleErrors", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ], + "implementation_scope": { + "description": "This query identifies possible domain, pole and range errors on a selection of C standard library fuctions from math.h." + } + } + ], + "title": "Prevent or detect domain and range errors in math functions" + }, + "FLP34-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Conversions of out-of-range floating-point values to integral types can lead to undefined behavior.", + "kind": "problem", + "name": "Ensure that floating-point conversions are within range of the new type", + "precision": "high", + "severity": "error", + "short_name": "UncheckedFloatingPointConversion", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ] + } + ], + "title": "Ensure that floating-point conversions are within range of the new type" + }, + "FLP36-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Integer to floating-point conversions may lose precision if the floating-point type is unable to fully represent the integer value.", + "kind": "problem", + "name": "Preserve precision when converting integral values to floating-point type", + "precision": "high", + "severity": "error", + "short_name": "IntToFloatPreservePrecision", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "Preserve precision when converting integral values to floating-point type" + }, + "FLP37-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Do not use object representations to compare floating-point values", + "precision": "very-high", + "severity": "error", + "short_name": "MemcmpUsedToCompareFloats", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "Do not use object representations to compare floating-point values" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/FloatingTypes2.json b/rule_packages/c/FloatingTypes2.json new file mode 100644 index 0000000000..a1c02daaf5 --- /dev/null +++ b/rule_packages/c/FloatingTypes2.json @@ -0,0 +1,38 @@ +{ + "MISRA-C-2012": { + "DIR-4-15": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Evaluation of floating-point expressions shall not lead to the undetected generation of infinities.", + "kind": "path-problem", + "name": "Evaluation of floating-point expressions shall not lead to the undetected generation of infinities", + "precision": "medium", + "severity": "warning", + "short_name": "PossibleMisuseOfUndetectedInfinity", + "shared_implementation_short_name": "MisuseOfInfiniteFloatingPointValue", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Evaluation of floating-point expressions shall not lead to the undetected generation of NaNs.", + "kind": "path-problem", + "name": "Evaluation of floating-point expressions shall not lead to the undetected generation of NaNs", + "precision": "low", + "severity": "warning", + "short_name": "PossibleMisuseOfUndetectedNaN", + "shared_implementation_short_name": "MisuseOfNaNFloatingPointValue", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "Evaluation of floating-point expressions shall not lead to the undetected generation of infinities and NaNs" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/FunctionTypes.json b/rule_packages/c/FunctionTypes.json new file mode 100644 index 0000000000..d9d8b6496d --- /dev/null +++ b/rule_packages/c/FunctionTypes.json @@ -0,0 +1,24 @@ +{ + "MISRA-C-2012": { + "RULE-17-12": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A function identifier should only be called with a parenthesized parameter list or used with a & (address-of).", + "kind": "problem", + "name": "A function identifier should only be called with a parenthesized parameter list or used with a &", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionAddressesShouldAddressOperator", + "tags": [ + "readability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A function identifier should only be called with a parenthesized parameter list or used with a & (address-of)" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Generics.json b/rule_packages/c/Generics.json new file mode 100644 index 0000000000..02c7cb2364 --- /dev/null +++ b/rule_packages/c/Generics.json @@ -0,0 +1,191 @@ +{ + "MISRA-C-2012": { + "RULE-23-1": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A generic selection should only be expanded from a macro.", + "kind": "problem", + "name": "A generic selection should only be expanded from a macro", + "precision": "very-high", + "severity": "warning", + "short_name": "GenericSelectionNotExpandedFromAMacro", + "tags": [ + "maintainability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "A generic selection should depend on the type of a macro argument.", + "kind": "problem", + "name": "A generic selection should depend on the type of a macro argument", + "precision": "high", + "severity": "warning", + "short_name": "GenericSelectionDoesntDependOnMacroArgument", + "tags": [ + "correctness", + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic selection should only be expanded from a macro" + }, + "RULE-23-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A generic selection that is not expanded from a macro shall not contain potential side effects in the controlling expression.", + "kind": "problem", + "name": "A generic selection shall not contain side-effects if it is not expanded from a macro", + "precision": "high", + "severity": "warning", + "short_name": "GenericSelectionNotFromMacroWithSideEffects", + "tags": [ + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "description": + "Due to limited information in the CodeQL database for macro argument expansions, this implementation reports generics not of the form `_Generic((X)` where all invocations of that generic contain a side effect in the controlling expression." + }, + "title": "A generic selection that is not expanded from a macro shall not contain potential side effects in the controlling expression" + }, + "RULE-23-3": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A generic selection should contain at least one non-default association.", + "kind": "problem", + "name": "A generic selection should contain at least one non-default association", + "precision": "very-high", + "severity": "warning", + "short_name": "GenericWithoutNonDefaultAssociation", + "tags": [ + "correctness", + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic selection should contain at least one non-default association" + }, + "RULE-23-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Generic selections undergo lvalue conversion before type comparison, leading to certain types being impossible to select.", + "kind": "problem", + "name": "A generic association shall list an appropriate type", + "precision": "very-high", + "severity": "error", + "short_name": "GenericAssociationWithUnselectableType", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic association shall list an appropriate type" + }, + "RULE-23-5": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Pointer types in a generic selection do not undergo pointer conversions and should not counterintuitively fall through to the default association.", + "kind": "problem", + "name": "A generic selection should not depend on implicit pointer type conversion", + "precision": "very-high", + "severity": "warning", + "short_name": "DangerousDefaultSelectionForPointerInGeneric", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic selection should not depend on implicit pointer type conversion" + }, + "RULE-23-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The controlling expression of a generic selection shall have an essential type that matches its standard type.", + "kind": "problem", + "name": "The controlling expression of a generic selection shall have an essential type that matches its standard type", + "precision": "high", + "severity": "error", + "short_name": "GenericExpressionWithIncorrectEssentialType", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "description": + "The CodeQL extractor will expand character literals passed into macros into integer literals, and therefore the essential type system for character literals will not necessarily be analyzed correctly." + }, + "title": "The controlling expression of a generic selection shall have an essential type that matches its standard type" + }, + "RULE-23-7": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A generic selection that is expanded from a macro should evaluate its argument only once.", + "kind": "problem", + "name": "A generic selection that is expanded from a macro should evaluate its argument only once", + "precision": "medium", + "severity": "warning", + "short_name": "InvalidGenericMacroArgumentEvaluation", + "tags": [ + "correctness", + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "description": + "Due to limited information in the CodeQL database for macro argument expansions, this implementation performs string matching on the macro parameters against the macro body to determine where parameters are expanded. If text indicating a nonevaluated context such as sizeof() or _Alignof() appear, there will be no positive result." + }, + "title": "A generic selection that is expanded from a macro should evaluate its argument only once" + }, + "RULE-23-8": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A default association shall appear as either the first or the last association of a generic selection.", + "kind": "problem", + "name": "A default association shall appear as either the first or the last association of a generic", + "precision": "very-high", + "severity": "warning", + "short_name": "DefaultGenericSelectionNotFirstOrLast", + "tags": [ + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A default association shall appear as either the first or the last association of a generic selection" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/IO1.json b/rule_packages/c/IO1.json index 7d7ae66645..8a42c4e52a 100644 --- a/rule_packages/c/IO1.json +++ b/rule_packages/c/IO1.json @@ -15,7 +15,12 @@ "shared_implementation_short_name": "NonConstantFormat", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -35,7 +40,12 @@ "short_name": "DistinguishBetweenCharReadFromAFileAndEofOrWeof", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function. The query does not validate if the FILE status is handled correctly after being read." @@ -50,7 +60,12 @@ "short_name": "EndOfFileCheckPortability", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function. The query does not validate if the FILE status is handled correctly after being read." @@ -73,7 +88,12 @@ "short_name": "DoNotAlternatelyIOFromAStreamWithoutPositioning", "shared_implementation_short_name": "IOFstreamMissingPositioning", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function." @@ -94,9 +114,15 @@ "precision": "very-high", "severity": "error", "short_name": "CloseFilesWhenTheyAreNoLongerNeeded", + "shared_implementation_short_name": "CloseFileHandleWhenNoLongerNeededShared", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function." @@ -119,7 +145,12 @@ "short_name": "UndefinedBehaviorAccessingAClosedFile", "shared_implementation_short_name": "DoNotAccessAClosedFile", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function." @@ -144,7 +175,8 @@ "short_name": "FileUsedAfterClosed", "shared_implementation_short_name": "DoNotAccessAClosedFile", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function." diff --git a/rule_packages/c/IO2.json b/rule_packages/c/IO2.json index 41c14a0d0e..69c12d7723 100644 --- a/rule_packages/c/IO2.json +++ b/rule_packages/c/IO2.json @@ -14,7 +14,12 @@ "short_name": "DoNotCopyAFileObject", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -34,7 +39,12 @@ "short_name": "ResetStringsOnFgetsOrFgetwsFailure", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -53,7 +63,12 @@ "severity": "error", "short_name": "DoNotCallGetcAndPutcWithSideEffects", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -72,7 +87,12 @@ "severity": "error", "short_name": "OnlyUseValuesForFsetposThatAreReturnedFromFgetpos", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/c/IO3.json b/rule_packages/c/IO3.json index 8d1c250eda..af6e9da732 100644 --- a/rule_packages/c/IO3.json +++ b/rule_packages/c/IO3.json @@ -14,7 +14,12 @@ "short_name": "DoNotPerformFileOperationsOnDevices", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ], "implementation_scope": { "description": "The rule checks that filenames are not tainted. It does not verify that appropriate OS-specific checks are in place to exclude that the opened file is a device." @@ -36,7 +41,12 @@ "severity": "error", "short_name": "SuccessfulFgetsOrFgetwsMayReturnAnEmptyString", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ], "implementation_scope": { "description": "The rule checks that access to a string returned by fgets() or fgetws() if protected by a guard condition. The rule is enforced in the context of a single function." @@ -60,7 +70,8 @@ "severity": "error", "short_name": "FileOpenForReadAndWriteOnDifferentStreams", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "The rule is enforced in the context of a single function." @@ -82,7 +93,8 @@ "severity": "error", "short_name": "AttemptToWriteToAReadOnlyStream", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -101,7 +113,8 @@ "severity": "error", "short_name": "PointerToAFileObjectDereferenced", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -120,7 +133,8 @@ "severity": "error", "short_name": "EofShallBeComparedWithUnmodifiedReturnValues", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 0873d2707b..8d9c150335 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -14,7 +14,12 @@ "short_name": "ToctouRaceConditionsWhileAccessingFiles", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ], "implementation_scope": { "description": "The query is limited to the specific class of TOCTOU race conditions that derives from the incorrectuse of `fopen` to check the existence of a file." @@ -37,7 +42,12 @@ "short_name": "UseValidSpecifiers", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -49,7 +59,12 @@ "short_name": "WrongNumberOfFormatArguments", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -61,11 +76,16 @@ "short_name": "WrongTypeFormatArguments", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], "title": "Use valid format strings" } } -} +} \ No newline at end of file diff --git a/rule_packages/c/IntegerOverflow.json b/rule_packages/c/IntegerOverflow.json new file mode 100644 index 0000000000..f528d3d542 --- /dev/null +++ b/rule_packages/c/IntegerOverflow.json @@ -0,0 +1,151 @@ +{ + "CERT-C": { + "INT30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Ensure that unsigned integer operations do not wrap", + "precision": "medium", + "severity": "error", + "short_name": "UnsignedIntegerOperationsWrapAround", + "shared_implementation_short_name": "UnsignedOperationWithConstantOperandsWraps", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" + ] + } + ], + "title": "Ensure that unsigned integer operations do not wrap" + }, + "INT31-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Ensure that integer conversions do not result in lost or misinterpreted data", + "precision": "medium", + "severity": "error", + "short_name": "IntegerConversionCausesDataLoss", + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" + ] + } + ], + "title": "Ensure that integer conversions do not result in lost or misinterpreted data" + }, + "INT32-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "The multiplication of two signed integers can lead to underflow or overflow and therefore undefined behavior.", + "kind": "problem", + "name": "Ensure that operations on signed integers do not result in overflow", + "precision": "medium", + "severity": "error", + "short_name": "SignedIntegerOverflow", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" + ] + } + ], + "title": "Ensure that operations on signed integers do not result in overflow" + }, + "INT33-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Dividing or taking the remainder by zero is undefined behavior.", + "kind": "problem", + "name": "Ensure that division and remainder operations do not result in divide-by-zero errors", + "precision": "medium", + "severity": "error", + "short_name": "DivOrRemByZero", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" + ] + } + ], + "title": "Ensure that division and remainder operations do not result in divide-by-zero errors" + }, + "INT35-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Use correct integer precisions", + "precision": "high", + "severity": "error", + "short_name": "UseCorrectIntegerPrecisions", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "Use correct integer precisions" + } + }, + "MISRA-C-2012": { + "RULE-12-4": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Unsigned integer expressions do not strictly overflow, but instead wrap around in a modular way. Any constant unsigned integer expressions that in effect \"overflow\" will not be detected by the compiler. Although there may be good reasons at run-time to rely on the modular arithmetic provided by unsigned integer types, the reasons for using it at compile-time to evaluate a constant expression are less obvious. Any instance of an unsigned integer constant expression wrapping around is therefore likely to indicate a programming error.", + "kind": "problem", + "name": "Evaluation of constant expressions should not lead to unsigned integer wrap-around", + "precision": "very-high", + "severity": "error", + "short_name": "ConstantUnsignedIntegerExpressionsWrapAround", + "shared_implementation_short_name": "ConstantUnsignedIntegerExpressionsWrapAround", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Evaluation of constant expressions should not lead to unsigned integer wrap-around" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/InvalidMemory1.json b/rule_packages/c/InvalidMemory1.json new file mode 100644 index 0000000000..3b0a6bb40c --- /dev/null +++ b/rule_packages/c/InvalidMemory1.json @@ -0,0 +1,104 @@ +{ + "CERT-C": { + "EXP33-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Using the value of an object with automatic storage duration while it is indeterminate is undefined behavior.", + "kind": "problem", + "name": "Do not read uninitialized memory", + "precision": "medium", + "severity": "error", + "shared_implementation_short_name": "ReadOfUninitializedMemory", + "short_name": "DoNotReadUninitializedMemory", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" + ] + } + ], + "title": "Do not read uninitialized memory" + }, + "EXP34-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Dereferencing a null pointer leads to undefined behavior.", + "kind": "problem", + "name": "Do not dereference null pointers", + "precision": "medium", + "severity": "error", + "shared_implementation_short_name": "DereferenceOfNullPointer", + "short_name": "DoNotDereferenceNullPointers", + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] + } + ], + "title": "Do not dereference null pointers" + }, + "MEM30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Accessing memory that has been deallocated is undefined behavior.", + "kind": "problem", + "name": "Do not access freed memory", + "precision": "high", + "severity": "error", + "short_name": "DoNotAccessFreedMemory", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] + } + ], + "title": "Do not access freed memory" + } + }, + "MISRA-C-2012": { + "RULE-9-1": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Accessing an object before it has been initialized can lead to undefined behavior.", + "kind": "problem", + "name": "The value of an object with automatic storage duration shall not be read before it has been set", + "precision": "medium", + "severity": "error", + "shared_implementation_short_name": "ReadOfUninitializedMemory", + "short_name": "ObjectWithAutoStorageDurationReadBeforeInit", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The value of an object with automatic storage duration shall not be read before it has been set" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/InvalidMemory2.json b/rule_packages/c/InvalidMemory2.json new file mode 100644 index 0000000000..025a5d246c --- /dev/null +++ b/rule_packages/c/InvalidMemory2.json @@ -0,0 +1,80 @@ +{ + "CERT-C": { + "ARR32-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "A variable-length array size that is zero, negative, overflowed, wrapped around, or excessively large may lead to undefined behaviour.", + "kind": "problem", + "name": "Ensure size arguments for variable length arrays are in a valid range", + "precision": "high", + "severity": "error", + "short_name": "VariableLengthArraySizeNotInValidRange", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" + ] + } + ], + "title": "Ensure size arguments for variable length arrays are in a valid range" + }, + "ARR37-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Do not add or subtract an integer to a pointer to a non-array object", + "precision": "high", + "severity": "error", + "short_name": "DoNotUsePointerArithmeticOnNonArrayObjectPointers", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + } + ], + "title": "Do not add or subtract an integer to a pointer to a non-array object" + }, + "EXP35-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Attempting to modify an object with temporary lifetime results in undefined behavior.", + "kind": "problem", + "name": "Do not modify objects with temporary lifetime", + "precision": "high", + "severity": "error", + "short_name": "DoNotModifyObjectsWithTemporaryLifetime", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "This implementation also always reports non-modifying accesses of objects with temporary lifetime, which are only compliant in C11." + } + } + ], + "title": "Do not modify objects with temporary lifetime" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/InvalidMemory3.json b/rule_packages/c/InvalidMemory3.json new file mode 100644 index 0000000000..feeb8b2b47 --- /dev/null +++ b/rule_packages/c/InvalidMemory3.json @@ -0,0 +1,59 @@ +{ + "MISRA-C-2012": { + "RULE-18-10": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Pointers to variably-modified array types shall not be used, as these pointer types are frequently incompatible with other fixed or variably sized arrays, resulting in undefined behavior.", + "kind": "problem", + "name": "Pointers to variably-modified array types shall not be used", + "precision": "high", + "severity": "error", + "short_name": "PointersToVariablyModifiedArrayTypesUsed", + "tags": [ + "external/misra/c/2012/amendment4", + "correctness", + "security" + ] + } + ], + "title": "Pointers to variably-modified array types shall not be used" + }, + "RULE-18-9": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Modifying or accessing elements of an array with temporary lifetime that has been converted to a pointer will result in undefined behavior.", + "kind": "problem", + "name": "An object with temporary lifetime shall not undergo array to pointer conversion", + "precision": "high", + "severity": "error", + "short_name": "ArrayToPointerConversionOfTemporaryObject", + "tags": [ + "external/misra/c/2012/amendment3", + "correctness", + "security" + ] + }, + { + "description": "Modifying elements of an array with temporary lifetime will result in undefined behavior.", + "kind": "problem", + "name": "Usage of the subscript operator on an object with temporary lifetime shall not return a modifiable value", + "precision": "high", + "severity": "error", + "short_name": "ModifiableLValueSubscriptedWithTemporaryLifetime", + "tags": [ + "external/misra/c/2012/amendment3", + "correctness", + "security" + ] + } + ], + "title": "An object with temporary lifetime shall not undergo array to pointer conversion" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Language1.json b/rule_packages/c/Language1.json index 50aed45c55..6b20822196 100644 --- a/rule_packages/c/Language1.json +++ b/rule_packages/c/Language1.json @@ -14,7 +14,8 @@ "short_name": "LanguageNotEncapsulatedAndIsolated", "tags": [ "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/Language2.json b/rule_packages/c/Language2.json new file mode 100644 index 0000000000..43dbb4ecef --- /dev/null +++ b/rule_packages/c/Language2.json @@ -0,0 +1,47 @@ +{ + "MISRA-C-2012": { + "DIR-4-2": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Assembly language is not portable and should be documented.", + "kind": "problem", + "name": "All usage of assembly language should be documented", + "precision": "very-high", + "severity": "warning", + "short_name": "UsageOfAssemblyLanguageShouldBeDocumented", + "shared_implementation_short_name": "UsageOfAssemblerNotDocumented", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "All usage of assembly language should be documented" + }, + "RULE-1-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Emergent language features may have unpredictable behavior and should not be used.", + "kind": "problem", + "name": "Emergent language features shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "EmergentLanguageFeaturesUsed", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/amendment2" + ] + } + ], + "title": "Emergent language features shall not be used" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Language3.json b/rule_packages/c/Language3.json new file mode 100644 index 0000000000..c19881e05c --- /dev/null +++ b/rule_packages/c/Language3.json @@ -0,0 +1,52 @@ +{ + "MISRA-C-2012": { + "RULE-1-2": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Language extensions are not portable to other compilers and should not be used.", + "kind": "problem", + "name": "Language extensions should not be used", + "precision": "high", + "severity": "error", + "short_name": "LanguageExtensionsShouldNotBeUsed", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This implementation attempts to cover a broad section of the compiler specific extensions documented in: https://clang.llvm.org/docs/LanguageExtensions.html and https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html but is not comprehensive. The following topics are addressed in this query: Builtin macros, Variable Attributes, Function Attributes, Statement Expressions, Non-Local Gotos, Conditionals, Extended Integer / Numeric Types, Zero Length Structures, Zero Length Arrays, Variable Length Arrays, Case Attributes, Alignment, __sync and __fetch builtins. Other topics listed in the extension references are not covered by this query." + } + } + ], + "title": "Language extensions should not be used" + }, + "RULE-1-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Relying on undefined or unspecified behavior can result in unreliable programs.", + "kind": "problem", + "name": "There shall be no occurrence of undefined or critical unspecified behavior", + "precision": "high", + "severity": "error", + "short_name": "OccurrenceOfUndefinedBehavior", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This implementation only considers alternate forms of `main` and the undefined behavior that results. Note that the current version of CodeQL is not able to detect this issue if a function is named `main` since it will assume the return type and formal parameters. Additional cases from Appendix J of the C99 standard are not currently considered." + } + } + ], + "title": "There shall be no occurrence of undefined or critical unspecified behavior" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Language4.json b/rule_packages/c/Language4.json new file mode 100644 index 0000000000..fdc11924f4 --- /dev/null +++ b/rule_packages/c/Language4.json @@ -0,0 +1,144 @@ +{ + "MISRA-C-2012": { + "RULE-1-5": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Declaring a function with internal linkage without the static storage class specifier is an obselescent feature.", + "kind": "problem", + "name": "If a function has internal linkage then all re-declarations shall include the static storage class", + "precision": "very-high", + "severity": "warning", + "short_name": "MissingStaticSpecifierFuncRedeclarationObsolete", + "shared_implementation_short_name": "MissingStaticSpecifierFunctionRedeclarationShared", + "tags": [ + "readability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Declaring an identifier with internal linkage without the static storage class specifier is an obselescent feature.", + "kind": "problem", + "name": "If an object has internal linkage then all re-declarations shall include the static storage class", + "precision": "very-high", + "severity": "warning", + "short_name": "MissingStaticSpecifierObjectRedeclarationObsolete", + "shared_implementation_short_name": "MissingStaticSpecifierObjectRedeclarationShared", + "tags": [ + "readability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "The use of non-prototype format parameter type declarators is an obsolescent language feature.", + "kind": "problem", + "name": "Function types shall be in prototype form with named parameters", + "precision": "medium", + "severity": "error", + "short_name": "FunctionTypesNotInPrototypeFormObsolete", + "shared_implementation_short_name": "FunctionTypesNotInPrototypeFormShared", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ], + "implementation_scope": { + "description": "This query does not check for implicitly typed parameters and checks function declarations and definitions but not function pointer types." + } + }, + { + "description": "The macro ATOMIC_VAR_INIT is has been declared an obsolescent language feature since C18.", + "kind": "problem", + "name": "Disallowed usage of obsolete macro ATOMIC_VAR_INIT compiled as C18", + "precision": "very-high", + "severity": "recommendation", + "short_name": "UseOfObsoleteMacroAtomicVarInit", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Directives that undefine and/or redefine the standard boolean macros has been declared an obsolescent language feature since C99.", + "kind": "problem", + "name": "Programs may not undefine or redefine the macros bool, true, or false", + "precision": "very-high", + "severity": "warning", + "short_name": "InvalidDefineOrUndefOfStdBoolMacro", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "The function 'gets' is an obsolescent language feature which was removed in C11.", + "kind": "problem", + "name": "Disallowed usage of obsolescent function 'gets'", + "precision": "very-high", + "severity": "error", + "short_name": "CallToObsolescentFunctionGets", + "tags": [ + "external/misra/c/2012/amendment3", + "security", + "maintainability" + ] + }, + { + "description": "Calling the function 'ungetc' on a file stream with a position of zero is an obsolescent language feature.", + "kind": "path-problem", + "name": "Disallowed obsolescent usage of 'ungetc' on a file stream at position zero", + "precision": "high", + "severity": "error", + "short_name": "UngetcCallOnStreamPositionZero", + "tags": [ + "external/misra/c/2012/amendment3", + "security", + "maintainability" + ] + }, + { + "description": "Invoking realloc with a size argument set to zero is implementation-defined behavior and declared as an obsolete feature in C18.", + "kind": "problem", + "name": "Size argument value in realloc call may equal zero", + "precision": "medium", + "severity": "error", + "short_name": "SizeInReallocCallMayBeZero", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Invoking realloc with a size argument set to zero is implementation-defined behavior and declared as an obsolete feature in C18.", + "kind": "problem", + "name": "Size argument value in realloc call is equal zero", + "precision": "very-high", + "severity": "error", + "short_name": "SizeInReallocCallIsZero", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "Obsolencent language features shall not be used", + "implementation_scope": { + "description": "Not all items from Appendix F are covered by this rule. Some are not supportable and some are covered already by other rules.", + "items": [ + "Appendix F, item ID 1 is reported by both Rule 8.8 and by this implementation of Rule 1.5.", + "Appendix F, item ID 2 refers to compiler behavior which cannot be statically analyzed.", + "Appendix F, item ID 3, which states that storage-class specifiers may not be used except in the beginning of a declaration, is not supportable without additional changes to the CodeQL CLI.", + "Appendix F, item IDs 4 and 5 are reported by both Rule 8.2 and by this implementation of Rule 1.5.", + "Appendix F, item ID 6 is reported for all C versions, though the macro ATOMIC_VAR_INIT was not officially declared obsolescent until C18.", + "Appendix F, item ID 8 is reported by both Rule 21.6 and by this implementation of Rule 1.5.", + "Appendix F, item ID 9 is reported by this implementation of 1.5, though all uses of ungetc() are also reported by Rule 21.3.", + "Appendix F, item ID 10 is reported by this implementation of 1.5, though all uses of realloc() are also reported by Rule 21.3.", + "Appendix F, item ID 10 is reported for all C versions, as realloc() with a size argument of zero was implementation-defined behavior in C99 and C11." + ] + } + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Memory1.json b/rule_packages/c/Memory1.json new file mode 100644 index 0000000000..8515fe15e1 --- /dev/null +++ b/rule_packages/c/Memory1.json @@ -0,0 +1,69 @@ +{ + "MISRA-C-2012": { + "RULE-9-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Using braces in initializers of objects and subobjects improves code readability and clarifies intent.", + "kind": "problem", + "name": "The initializer for an aggregate or union shall be enclosed in braces", + "precision": "high", + "severity": "recommendation", + "short_name": "InitializerForAggregateOrUnionNotEnclosedInBraces", + "shared_implementation_short_name": "UseInitializerBracesToMatchAggregateTypeStructure", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The initializer for an aggregate or union shall be enclosed in braces" + }, + "RULE-9-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An array object or a subobject of an array shall be explicitly initialized if any other object in that array is explicitly initialized.", + "kind": "problem", + "name": "Arrays shall not be partially initialized", + "precision": "high", + "severity": "warning", + "short_name": "PartiallyInitializedArrayWithExplicitInitializers", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Arrays shall not be partially initialized" + }, + "RULE-9-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Repeated initialization of an element in an object can lead to side-effects or may signal a logic error.", + "kind": "problem", + "name": "An element of an object shall not be initialized more than once", + "precision": "high", + "severity": "error", + "short_name": "RepeatedInitializationOfAggregateObjectElement", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An element of an object shall not be initialized more than once" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Memory2.json b/rule_packages/c/Memory2.json new file mode 100644 index 0000000000..55a7dd2a35 --- /dev/null +++ b/rule_packages/c/Memory2.json @@ -0,0 +1,257 @@ +{ + "CERT-C": { + "ARR36-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Subtraction between pointers referring to differing arrays results in undefined behavior.", + "kind": "path-problem", + "name": "Do not subtract two pointers that do not refer to the same array", + "precision": "high", + "severity": "warning", + "short_name": "DoNotSubtractPointersThatDoNotReferToTheSameArray", + "shared_implementation_short_name": "DoNotSubtractPointersAddressingDifferentArrays", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + }, + { + "description": "Comparison using the >, >=, <, and <= operators between pointers referring to differing arrays results in undefined behavior.", + "kind": "path-problem", + "name": "Do not subtract two pointers that do not refer to the same array", + "precision": "high", + "severity": "warning", + "short_name": "DoNotRelatePointersThatDoNotReferToTheSameArray", + "shared_implementation_short_name": "DoNotUseRelationalOperatorsWithDifferingArrays", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + } + ], + "title": "Do not subtract or compare two pointers that do not refer to the same array" + }, + "EXP42-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Padding data values are unspecified and should not be included in comparisons.", + "kind": "problem", + "name": "Do not compare padding data", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotComparePaddingData", + "shared_implementation_short_name": "MemcmpUsedToComparePaddingData", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ] + } + ], + "title": "Do not compare padding data" + }, + "MEM31-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Failing to free memory that is no longer needed can lead to a memory leak and resource exhaustion.", + "kind": "problem", + "name": "Free dynamically allocated memory when no longer needed", + "precision": "very-high", + "severity": "error", + "short_name": "FreeMemoryWhenNoLongerNeededCert", + "shared_implementation_short_name": "FreeMemoryWhenNoLongerNeededShared", + "tags": [ + "correctness", + "security", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } + } + ], + "title": "Free dynamically allocated memory when no longer needed" + }, + "MEM33-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "A structure containing a flexible array member must be allocated dynamically in order for subsequent accesses to the flexible array to point to valid memory.", + "kind": "problem", + "name": "Allocate structures containing a flexible array member dynamically", + "precision": "very-high", + "severity": "error", + "short_name": "AllocStructsWithAFlexibleArrayMemberDynamically", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ] + }, + { + "description": "Copying a structure containing a flexbile array member by assignment ignores the flexible array member data.", + "kind": "problem", + "name": "Copy structures containing a flexible array member using memcpy or a similar function", + "precision": "very-high", + "severity": "error", + "short_name": "CopyStructsWithAFlexibleArrayMemberDynamically", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ] + } + ], + "title": "Allocate and copy structures containing a flexible array member dynamically" + }, + "MEM34-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Freeing memory that is not allocated dynamically can lead to heap corruption and undefined behavior.", + "kind": "path-problem", + "name": "Only free memory allocated dynamically", + "precision": "high", + "severity": "error", + "short_name": "OnlyFreeMemoryAllocatedDynamicallyCert", + "shared_implementation_short_name": "OnlyFreeMemoryAllocatedDynamicallyShared", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] + } + ], + "title": "Only free memory allocated dynamically" + }, + "MEM36-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Realloc does not preserve the alignment of memory allocated with aligned_alloc and can result in undefined behavior if reallocating more strictly aligned memory.", + "kind": "path-problem", + "name": "Do not modify the alignment of objects by calling realloc", + "precision": "high", + "severity": "error", + "short_name": "DoNotModifyAlignmentOfMemoryWithRealloc", + "tags": [ + "correctness", + "security", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "Do not modify the alignment of objects by calling realloc" + } + }, + "MISRA-C-2012": { + "RULE-22-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Memory allocated dynamically with standard library functions should be freed to avoid memory leaks.", + "kind": "problem", + "name": "Memory allocated dynamically with Standard Library functions shall be explicitly released", + "precision": "very-high", + "severity": "error", + "short_name": "FreeMemoryWhenNoLongerNeededMisra", + "shared_implementation_short_name": "FreeMemoryWhenNoLongerNeededShared", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } + }, + { + "description": "File handles acquired with standard library functions should be released to avoid resource exhaustion.", + "kind": "problem", + "name": "File handles acquired with Standard Library functions shall be explicitly closed", + "precision": "very-high", + "severity": "error", + "short_name": "CloseFileHandleWhenNoLongerNeededMisra", + "shared_implementation_short_name": "CloseFileHandleWhenNoLongerNeededShared", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } + } + ], + "title": "All resources obtained dynamically by means of Standard Library functions shall be explicitly released" + }, + "RULE-22-2": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Freeing memory that is not allocated dynamically can lead to heap corruption and undefined behavior.", + "kind": "path-problem", + "name": "A block of memory shall only be freed if it was allocated by means of a Standard Library function", + "precision": "high", + "severity": "error", + "short_name": "OnlyFreeMemoryAllocatedDynamicallyMisra", + "shared_implementation_short_name": "OnlyFreeMemoryAllocatedDynamicallyShared", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A block of memory shall only be freed if it was allocated by means of a Standard Library function" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Memory3.json b/rule_packages/c/Memory3.json new file mode 100644 index 0000000000..e1ed7382e0 --- /dev/null +++ b/rule_packages/c/Memory3.json @@ -0,0 +1,29 @@ +{ + "CERT-C": { + "MEM35-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "The size of memory allocated dynamically must be adequate to represent the type of object referenced by the allocated memory.", + "kind": "problem", + "name": "Allocate sufficient memory for an object", + "precision": "medium", + "severity": "error", + "short_name": "InsufficientMemoryAllocatedForObject", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" + ] + } + ], + "title": "Allocate sufficient memory for an object" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Misc.json b/rule_packages/c/Misc.json index 323ec17350..183c05988b 100644 --- a/rule_packages/c/Misc.json +++ b/rule_packages/c/Misc.json @@ -12,9 +12,14 @@ "precision": "very-high", "severity": "error", "short_name": "RandUsedForGeneratingPseudorandomNumbers", - "shared_implementation_short_name" : "DoNotUseRandForGeneratingPseudorandomNumbers", + "shared_implementation_short_name": "DoNotUseRandForGeneratingPseudorandomNumbers", "tags": [ - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -33,7 +38,12 @@ "severity": "error", "short_name": "ProperlySeedPseudorandomNumberGenerators", "tags": [ - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p18", + "external/cert/level/l1" ], "implementation_scope": { "description": "This rule will be checked by looking for calls to random that are no preceded by a call to srandom(). We perform a simple check for the argument to srandom() and verify it is not a literal (or a value easily deduced to be a literal)." @@ -56,7 +66,12 @@ "short_name": "ControlFlowReachesTheEndOfANonVoidFunction", "shared_implementation_short_name": "NonVoidFunctionDoesNotReturn", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/c/NoReturn.json b/rule_packages/c/NoReturn.json new file mode 100644 index 0000000000..f485060095 --- /dev/null +++ b/rule_packages/c/NoReturn.json @@ -0,0 +1,65 @@ +{ + "MISRA-C-2012": { + "RULE-17-10": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Function declared with _noreturn will by definition not return a value, and should be declared to return void.", + "kind": "problem", + "name": "A function declared with _noreturn shall have a return type of void", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NonVoidReturnTypeOfNoreturnFunction", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A function declared with _noreturn shall have a return type of void" + }, + "RULE-17-11": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Functions which cannot return should be declared with _Noreturn.", + "kind": "problem", + "name": "A function without a branch that returns shall be declared with _Noreturn", + "precision": "very-high", + "severity": "recommendation", + "short_name": "FunctionWithNoReturningBranchShouldBeNoreturn", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A function without a branch that returns shall be declared with _Noreturn" + }, + "RULE-17-9": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Returning inside a function declared with _Noreturn is undefined behavior.", + "kind": "problem", + "name": "Verify that a function declared with _Noreturn does not return", + "precision": "very-high", + "severity": "error", + "short_name": "ReturnStatementInNoreturnFunction", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ], + "shared_implementation_short_name": "FunctionNoReturnAttributeCondition" + } + ], + "title": "Verify that a function declared with _Noreturn does not return" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/OutOfBounds.json b/rule_packages/c/OutOfBounds.json new file mode 100644 index 0000000000..3354348230 --- /dev/null +++ b/rule_packages/c/OutOfBounds.json @@ -0,0 +1,98 @@ +{ + "CERT-C": { + "ARR30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Forming or using an out-of-bounds pointer is undefined behavior and can result in invalid memory accesses.", + "kind": "problem", + "name": "Do not form or use out-of-bounds pointers or array subscripts", + "precision": "medium", + "severity": "error", + "short_name": "DoNotFormOutOfBoundsPointersOrArraySubscripts", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" + ] + } + ], + "title": "Do not form or use out-of-bounds pointers or array subscripts" + }, + "ARR38-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Guarantee that library functions do not form invalid pointers", + "precision": "high", + "severity": "error", + "short_name": "LibraryFunctionArgumentOutOfBounds", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] + } + ], + "title": "Guarantee that library functions do not form invalid pointers" + } + }, + "MISRA-C-2012": { + "RULE-21-17": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Use of string manipulation functions from with improper buffer sizes can result in out-of-bounds buffer accesses.", + "kind": "problem", + "name": "Use of the string handling functions from shall not result in accesses beyond the bounds", + "precision": "high", + "severity": "error", + "short_name": "StringFunctionPointerArgumentOutOfBounds", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters" + }, + "RULE-21-18": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Passing a size_t argument that is non-positive or greater than the size of the smallest buffer argument to any function in may result in out-of-bounds buffer accesses.", + "kind": "problem", + "name": "The size_t argument passed to any function in shall have an appropriate value", + "precision": "high", + "severity": "error", + "short_name": "StringLibrarySizeArgumentOutOfBounds", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The size_t argument passed to any function in shall have an appropriate value" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Pointers1.json b/rule_packages/c/Pointers1.json index 991838e34f..1b54fc1fb6 100644 --- a/rule_packages/c/Pointers1.json +++ b/rule_packages/c/Pointers1.json @@ -13,7 +13,8 @@ "severity": "error", "short_name": "ConversionBetweenFunctionPointerAndOtherType", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -32,7 +33,8 @@ "severity": "error", "short_name": "ConversionBetweenIncompleteTypePointerAndOtherType", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -51,7 +53,8 @@ "severity": "error", "short_name": "CastBetweenObjectPointerAndDifferentObjectType", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -70,7 +73,8 @@ "severity": "error", "short_name": "ConversionBetweenPointerToObjectAndIntegerType", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -89,7 +93,8 @@ "severity": "error", "short_name": "ConversionFromPointerToVoidIntoPointerToObject", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -108,7 +113,8 @@ "severity": "error", "short_name": "CastBetweenPointerToVoidAndArithmeticType", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -127,7 +133,8 @@ "severity": "error", "short_name": "CastBetweenPointerToObjectAndNonIntArithmeticType", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -146,7 +153,8 @@ "severity": "error", "short_name": "CastRemovesConstOrVolatileQualification", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -165,7 +173,8 @@ "severity": "error", "short_name": "MacroNullNotUsedAsIntegerNullPointerConstant", "tags": [ - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This rule allows two forms of null-pointer constants: a Zero literal created by the NULL macro or a Zero literal cast to a void pointer." @@ -188,7 +197,8 @@ "short_name": "PointerAndDerivedPointerMustAddressSameArray", "shared_implementation_short_name": "DoNotUsePointerArithmeticToAddressDifferentArrays", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -208,7 +218,8 @@ "short_name": "SubtractionBetweenPointersMustAddressSameArray", "shared_implementation_short_name": "DoNotSubtractPointersAddressingDifferentArrays", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -228,11 +239,12 @@ "short_name": "RelationalOperatorComparesPointerToDifferentArray", "shared_implementation_short_name": "DoNotUseRelationalOperatorsWithDifferingArrays", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], - "title": "The relational operators >, >=, < and <= shall not be applied to objects of pointer type except where they point into the same object" + "title": "The relational operators >, >=, < and <= shall not be applied to expressions of pointer type except where they point into the same object" }, "RULE-18-4": { "properties": { @@ -248,7 +260,8 @@ "short_name": "DoNotUseAdditionOrSubtractionOperatorsOnPointers", "shared_implementation_short_name": "UseOnlyArrayIndexingForPointerArithmetic", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -268,7 +281,8 @@ "short_name": "NoMoreThanTwoLevelsOfPointerNestingInDeclarations", "shared_implementation_short_name": "DoNotUseMoreThanTwoLevelsOfPointerIndirection", "tags": [ - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -288,13 +302,26 @@ "short_name": "AutomaticStorageObjectAddressCopiedToOtherObject", "shared_implementation_short_name": "DoNotCopyAddressOfAutoStorageObjectToOtherObject", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "Storing the address of a thread-local object in a global object will result in undefined behavior if the address is accessed after the relevant thread is terminated.", + "kind": "problem", + "name": "The address of an object with thread-local storage shall not be copied to a global object", + "precision": "very-high", + "severity": "error", + "short_name": "ThreadLocalObjectAddressCopiedToGlobalObject", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" ] } ], "title": "The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist" }, - "RULE-4-8": { + "DIR-4-8": { "properties": { "obligation": "advisory" }, @@ -308,7 +335,8 @@ "short_name": "ObjectWithNoPointerDereferenceShouldBeOpaque", "tags": [ "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This rule considers all cases where a structure or union is referenced as a pointer but has no FieldAccess within a translation unit. Further excluded from this rule are translation units in which the structure or union is declared as a non-pointer variable." @@ -332,7 +360,8 @@ "tags": [ "correctness", "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "To exclude compliant exceptions, this rule only excludes direct assignments of pointers to non-const-qualified types in the context of a single function and does not cover memory-copying functions. This rule also excludes pointers passed to other functions without conversion." diff --git a/rule_packages/c/Pointers2.json b/rule_packages/c/Pointers2.json index da275001c4..fcfd9356e6 100644 --- a/rule_packages/c/Pointers2.json +++ b/rule_packages/c/Pointers2.json @@ -12,8 +12,13 @@ "precision": "high", "severity": "error", "short_name": "DoNotAddOrSubtractAScaledIntegerToAPointer", - "tags":[ - "correctness" + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json new file mode 100644 index 0000000000..8a169b71a8 --- /dev/null +++ b/rule_packages/c/Pointers3.json @@ -0,0 +1,120 @@ +{ + "CERT-C": { + "EXP32-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Do not access a volatile object through a nonvolatile reference", + "precision": "high", + "severity": "error", + "short_name": "DoNotAccessVolatileObjectWithNonVolatileReference", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" + ], + "implementation_scope": { + "description": "In limited cases, this query can raise false-positives for assignment of volatile objects and subsequent accesses of those objects via non-volatile pointers." + } + } + ], + "title": "Do not access a volatile object through a nonvolatile reference" + }, + "EXP36-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Do not cast pointers into more strictly aligned pointer types", + "precision": "high", + "severity": "error", + "short_name": "DoNotCastPointerToMoreStrictlyAlignedPointerType", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" + ] + } + ], + "title": "Do not cast pointers into more strictly aligned pointer types" + }, + "EXP39-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Modifying underlying pointer data through a pointer of an incompatible type can lead to unpredictable results.", + "kind": "path-problem", + "name": "Do not access a variable through a pointer of an incompatible type", + "precision": "high", + "severity": "error", + "short_name": "DoNotAccessVariableViaPointerOfIncompatibleType", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "Do not access a variable through a pointer of an incompatible type" + }, + "EXP43-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Passing an aliased pointer to a restrict-qualified parameter is undefined behavior.", + "kind": "problem", + "name": "Do not pass aliased pointers to restrict-qualified parameters", + "precision": "medium", + "severity": "error", + "short_name": "DoNotPassAliasedPointerToRestrictQualifiedParam", + "shared_implementation_short_name": "DoNotPassAliasedPointerToRestrictQualifiedParamShared", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" + ] + }, + { + "description": "Restrict qualified pointers referencing overlapping objects is undefined behavior.", + "kind": "problem", + "name": "Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer", + "precision": "high", + "severity": "error", + "short_name": "RestrictPointerReferencesOverlappingObject", + "tags": [ + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" + ] + } + ], + "title": "Avoid undefined behavior when using restrict-qualified pointers" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Preprocessor1.json b/rule_packages/c/Preprocessor1.json index b93bc72731..cf4f023023 100644 --- a/rule_packages/c/Preprocessor1.json +++ b/rule_packages/c/Preprocessor1.json @@ -14,7 +14,8 @@ "short_name": "IncludeDirectivesPrecededByDirectivesOrComments", "shared_implementation_short_name": "PreprocessorIncludesPreceded", "tags": [ - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -34,10 +35,10 @@ "short_name": "PreprocessorHashOperatorsShouldNotBeUsed", "shared_implementation_short_name": "HashOperatorsUsed", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } - ], "title": "The # and ## preprocessor operators should not be used" }, @@ -55,7 +56,8 @@ "short_name": "ForbiddenCharactersInHeaderFileName", "shared_implementation_short_name": "PreprocessorIncludesForbiddenHeaderNames", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query identifies the use of the ', \\, /*, // characters in header file names. The query is not able to detect the use of the \" character in header file names.", @@ -79,8 +81,9 @@ "short_name": "IdentifiersUsedInPreprocessorExpression", "shared_implementation_short_name": "UndefinedMacroIdentifiers", "tags": [ - "correctness", - "readability" + "correctness", + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query does not detect the case where an undefined character is used but not actually evaluated, for example, as a result of the inclusion of a logical AND operator in the #if expression.", diff --git a/rule_packages/c/Preprocessor2.json b/rule_packages/c/Preprocessor2.json index 66e759c5b9..62bb0b770a 100644 --- a/rule_packages/c/Preprocessor2.json +++ b/rule_packages/c/Preprocessor2.json @@ -12,8 +12,10 @@ "precision": "very-high", "severity": "warning", "short_name": "MoreThanOneHashOperatorInMacroDefinition", + "shared_implementation_short_name": "MacroParameterFollowingHash", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query applies to function like macros and not object like macros. This rule strictly disallows the use of # operator followed by a ## and other combinations are permitted.", @@ -35,9 +37,11 @@ "precision": "high", "severity": "warning", "short_name": "MacroParameterUsedAsHashOperand", + "shared_implementation_short_name": "AMixedUseMacroArgumentSubjectToExpansion", "tags": [ - "maintainability", - "readability" + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -56,14 +60,15 @@ "severity": "warning", "short_name": "UndefShouldNotBeUsed", "tags": [ - "maintainability", - "readability" + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], "title": "#undef should not be used" }, - "RULE-4-10": { + "DIR-4-10": { "properties": { "obligation": "required" }, @@ -79,7 +84,8 @@ "tags": [ "correctness", "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query defines header file as any file that is included regardless of file extension. This query does not consider the use of `#pragma once` as a permitted header guard.", diff --git a/rule_packages/c/Preprocessor3.json b/rule_packages/c/Preprocessor3.json index 0b0c735a04..79e2aec59c 100644 --- a/rule_packages/c/Preprocessor3.json +++ b/rule_packages/c/Preprocessor3.json @@ -1,24 +1,25 @@ { "MISRA-C-2012": { "RULE-20-8": { - "properties": { - "obligation": "required" - }, - "queries": [ - { - "description": "A controlling expression of a #if or #elif preprocessing directive that does not evaluate to 0 or 1 makes code more difficult to understand.", - "kind": "problem", - "name": "The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1", - "precision": "high", - "severity": "warning", - "short_name": "ControllingExpressionIfDirective", - "tags": [ - "maintainability", - "readability" - ] - } - ], - "title": "The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1" - } + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A controlling expression of a #if or #elif preprocessing directive that does not evaluate to 0 or 1 makes code more difficult to understand.", + "kind": "problem", + "name": "The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1", + "precision": "high", + "severity": "warning", + "short_name": "ControllingExpressionIfDirective", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1" + } } } \ No newline at end of file diff --git a/rule_packages/c/Preprocessor4.json b/rule_packages/c/Preprocessor4.json index 404909c479..608a23d974 100644 --- a/rule_packages/c/Preprocessor4.json +++ b/rule_packages/c/Preprocessor4.json @@ -15,7 +15,8 @@ "tags": [ "correctness", "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -36,7 +37,8 @@ "shared_implementation_short_name": "PreprocessingDirectiveWithinMacroArgument", "tags": [ "readability", - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -57,7 +59,8 @@ "tags": [ "correctness", "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/Preprocessor5.json b/rule_packages/c/Preprocessor5.json index 29c0156410..60a1752e73 100644 --- a/rule_packages/c/Preprocessor5.json +++ b/rule_packages/c/Preprocessor5.json @@ -14,7 +14,12 @@ "short_name": "DoNotTreatAPredefinedIdentifierAsObject", "tags": [ "correctness", - "readability" + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ], "implementation_scope": { "description": "This query reports locations corresponding to both redefinitions of those standard library macros as well as locations where the identifiers used for accesses.", @@ -38,7 +43,12 @@ "short_name": "MacroOrFunctionArgsContainHashToken", "tags": [ "correctness", - "readability" + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ], "implementation_scope": { "description": "This query defines end of function call as the next node in the control flow graph.", @@ -65,7 +75,8 @@ "shared_implementation_short_name": "MacroParameterNotEnclosedInParentheses", "tags": [ "correctness", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ], "implementation_scope": { "description": "This query checks for every instance of a parameter to be enclosed in parentheses regardless of whether the expansion of that parameter forms an expression or not.", diff --git a/rule_packages/c/Preprocessor6.json b/rule_packages/c/Preprocessor6.json new file mode 100644 index 0000000000..6d71b8697b --- /dev/null +++ b/rule_packages/c/Preprocessor6.json @@ -0,0 +1,27 @@ +{ + "MISRA-C-2012": { + "DIR-4-9": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Using a function-like macro instead of a function can lead to unexpected program behaviour.", + "kind": "problem", + "name": "A function should be used in preference to a function-like macro where they are interchangeable", + "precision": "medium", + "severity": "recommendation", + "short_name": "FunctionOverFunctionLikeMacro", + "shared_implementation_short_name": "FunctionLikeMacrosDefined", + "tags": [ + "external/misra/audit", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A function should be used in preference to a function-like macro where they are interchangeable" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/SideEffects1.json b/rule_packages/c/SideEffects1.json index e66f4c3136..4dec3d8bbf 100644 --- a/rule_packages/c/SideEffects1.json +++ b/rule_packages/c/SideEffects1.json @@ -13,7 +13,12 @@ "severity": "warning", "short_name": "DependenceOnOrderOfScalarEvaluationForSideEffects", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] }, { @@ -24,7 +29,12 @@ "severity": "warning", "short_name": "DependenceOnOrderOfFunctionArgumentsForSideEffects", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -43,7 +53,12 @@ "severity": "error", "short_name": "UnevaluatedOperandWithSideEffect", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], @@ -62,7 +77,12 @@ "severity": "error", "short_name": "AssignmentsInSelectionStatements", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -83,7 +103,8 @@ "severity": "warning", "short_name": "UnenclosedSizeofOperand", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] }, { @@ -94,7 +115,8 @@ "severity": "warning", "short_name": "ImplicitPrecedenceOfOperatorsInExpression", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -113,7 +135,8 @@ "severity": "error", "short_name": "InitializerListsContainPersistentSideEffects", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -131,9 +154,11 @@ "precision": "very-high", "severity": "error", "short_name": "ResultOfAnAssignmentOperatorShouldNotBeUsed", + "shared_implementation_short_name": "ResultOfAnAssignmentOperatorShouldNotBeUsed", "tags": [ "correctness", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -152,7 +177,8 @@ "severity": "error", "short_name": "PossibleSuppressedSideEffectInLogicOperatorOperand", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -160,7 +186,7 @@ }, "RULE-13-6": { "properties": { - "obligation": "mandatory" + "obligation": "required" }, "queries": [ { @@ -171,7 +197,8 @@ "severity": "error", "short_name": "SizeofOperandWithSideEffect", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/SideEffects2.json b/rule_packages/c/SideEffects2.json index 42467c2852..b7e1baa901 100644 --- a/rule_packages/c/SideEffects2.json +++ b/rule_packages/c/SideEffects2.json @@ -14,7 +14,8 @@ "short_name": "SideEffectAndCrementInFullExpression", "tags": [ "readability", - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -33,7 +34,8 @@ "severity": "warning", "short_name": "ModificationOfFunctionParameter", "tags": [ - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/SideEffects3.json b/rule_packages/c/SideEffects3.json new file mode 100644 index 0000000000..1ff29ec166 --- /dev/null +++ b/rule_packages/c/SideEffects3.json @@ -0,0 +1,36 @@ +{ + "MISRA-C-2012": { + "RULE-13-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The value of an expression and its persistent side effects are depending on the evaluation order resulting in unpredictable behavior.", + "kind": "problem", + "name": "The value of an expression and its persistent side effects depend on its evaluation order", + "precision": "very-high", + "severity": "error", + "short_name": "UnsequencedSideEffects", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "The value of an atomic variable shall not depend on evaluation order and interleaving of threads.", + "kind": "problem", + "name": "The value of an atomic variable shall not depend on the evaluation order of interleaved threads", + "precision": "very-high", + "severity": "error", + "short_name": "UnsequencedAtomicReads", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "The value of an expression and its persistent side effects shall be the same under all permitted evaluation orders" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/SideEffects4.json b/rule_packages/c/SideEffects4.json new file mode 100644 index 0000000000..5b0c6da3f5 --- /dev/null +++ b/rule_packages/c/SideEffects4.json @@ -0,0 +1,31 @@ +{ + "CERT-C": { + "PRE31-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Macro arguments can be expanded multiple times which can cause side-effects to be evaluated multiple times leading to unexpected program behavior.", + "kind": "problem", + "name": "Avoid side effects in arguments to unsafe macros", + "precision": "low", + "severity": "error", + "short_name": "SideEffectsInArgumentsToUnsafeMacros", + "tags": [ + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "This implementation only considers ++ and function call side effects. Due to the textual nature of macro expansion it is not always possible to determine accurately whether a side-effect was produced by a particular argument, and this may cause both false positives and false negatives. The query does not consider the case where a macro argument including a side-effect is never evaluated." + } + } + ], + "title": "Avoid side effects in arguments to unsafe macros" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/SignalHandlers.json b/rule_packages/c/SignalHandlers.json new file mode 100644 index 0000000000..ae9045a64d --- /dev/null +++ b/rule_packages/c/SignalHandlers.json @@ -0,0 +1,107 @@ +{ + "CERT-C": { + "SIG30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Call only asynchronous-safe functions within signal handlers.", + "kind": "problem", + "name": "Call only asynchronous-safe functions within signal handlers", + "precision": "very-high", + "severity": "error", + "short_name": "CallOnlyAsyncSafeFunctionsWithinSignalHandlers", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] + } + ], + "title": "Call only asynchronous-safe functions within signal handlers" + }, + "SIG31-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not access shared objects in signal handlers.", + "kind": "problem", + "name": "Do not access shared objects in signal handlers", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotAccessSharedObjectsInSignalHandlers", + "tags": [ + "correctness", + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" + ], + "implementation_scope": { + "description": "The implementation does not verify the correct usage of `atomic_is_lock_free`." + } + } + ], + "title": "Do not access shared objects in signal handlers" + }, + "SIG34-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not call signal() from within interruptible signal handlers.", + "kind": "problem", + "name": "Do not call signal() from within interruptible signal handlers", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotCallSignalFromInterruptibleSignalHandlers", + "tags": [ + "correctness", + "security", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" + ] + } + ], + "title": "Do not call signal() from within interruptible signal handlers" + }, + "SIG35-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Do not return from a computational exception signal handler.", + "kind": "problem", + "name": "Do not return from a computational exception signal handler", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotReturnFromAComputationalExceptionHandler", + "tags": [ + "correctness", + "security", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p1", + "external/cert/level/l3" + ] + } + ], + "title": "Do not return from a computational exception signal handler" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/StandardLibraryFunctionTypes.json b/rule_packages/c/StandardLibraryFunctionTypes.json new file mode 100644 index 0000000000..ee0d7f5af1 --- /dev/null +++ b/rule_packages/c/StandardLibraryFunctionTypes.json @@ -0,0 +1,42 @@ +{ + "MISRA-C-2012": { + "RULE-21-13": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Passing arguments to functions outside the range of unsigned char or EOF causes undefined behavior.", + "kind": "problem", + "name": " function arguments shall be represented as unsigned char", + "precision": "very-high", + "severity": "error", + "short_name": "CtypeFunctionArgNotUnsignedCharOrEof", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Any value passed to a function in shall be representable as an unsigned char or be the value EOF" + }, + "RULE-21-15": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Passing pointers to incompatible types as arguments to memcpy, memmove and memcmp indicates programmers' confusion.", + "kind": "problem", + "name": "The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers", + "precision": "very-high", + "severity": "error", + "short_name": "MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible types" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements1.json b/rule_packages/c/Statements1.json new file mode 100644 index 0000000000..c932a8642d --- /dev/null +++ b/rule_packages/c/Statements1.json @@ -0,0 +1,87 @@ +{ + "MISRA-C-2012": { + "RULE-16-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Nested switch labels can lead to unstructured code.", + "kind": "problem", + "name": "Nested switch labels shall not be used", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NestSwitchLabelInSwitchStatement", + "shared_implementation_short_name": "NestedLabelInSwitch", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement" + }, + "RULE-16-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An unterminated switch-clause occurring at the end of a switch statement may fall into switch clauses which are added later.", + "kind": "problem", + "name": "An unconditional break statement shall terminate every switch-clause", + "precision": "high", + "severity": "warning", + "short_name": "BreakShallTerminateSwitchClause", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "An unconditional break statement shall terminate every switch-clause" + }, + "RULE-16-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A default label that has no statements or a comment explaining why this is correct indicates a missing implementation that may result in unexpected behavior when the default case is executed.", + "kind": "problem", + "name": "Every switch statement shall have a default label", + "precision": "very-high", + "severity": "warning", + "short_name": "EverySwitchShallHaveDefaultLabel", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Every switch statement shall have a default label" + }, + "RULE-16-5": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Locating the default label is easier when it is the first or last label.", + "kind": "problem", + "name": "A default label shall appear as either the first or the last switch label or a switch statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "DefaultNotFirstOrLastOfSwitch", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A default label shall appear as either the first or the last switch label of a switch statement" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements2.json b/rule_packages/c/Statements2.json new file mode 100644 index 0000000000..9cd71b69c9 --- /dev/null +++ b/rule_packages/c/Statements2.json @@ -0,0 +1,111 @@ +{ + "MISRA-C-2012": { + "RULE-15-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Unconstrained use of goto can lead to unstructured code.", + "kind": "problem", + "name": "The goto statement shall jump to a label declared later in the same function", + "precision": "very-high", + "severity": "recommendation", + "short_name": "GotoLabelLocationCondition", + "shared_implementation_short_name": "GotoStatementCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The goto statement shall jump to a label declared later in the same function" + }, + "RULE-15-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement.", + "kind": "problem", + "name": "The goto statement and any of its label shall be declared or enclosed in the same block", + "precision": "high", + "severity": "recommendation", + "short_name": "GotoLabelBlockCondition", + "shared_implementation_short_name": "GotoReferenceALabelInSurroundingBlock", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement" + }, + "RULE-15-4": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "More than one break or goto statement in iteration conditions may lead to readability and maintainability issues.", + "kind": "problem", + "name": "There should be no more than one break or goto statement used to terminate any iteration statement", + "precision": "very-high", + "severity": "error", + "short_name": "LoopIterationCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "There should be no more than one break or goto statement used to terminate any iteration statement" + }, + "RULE-16-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Switch Statements with a single path are redundant and may cause programming errors.", + "kind": "problem", + "name": "Every switch statement shall have at least two switch-clauses", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SwitchClauseNumberCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Every switch statement shall have at least two switch-clauses" + }, + "RULE-16-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An `if-else` construct is more appropriate for boolean controlled expression.", + "kind": "problem", + "name": "A switch-expression shall not have essentially Boolean type", + "precision": "very-high", + "severity": "error", + "short_name": "SwitchExpressionBoolCondition", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A switch-expression shall not have essentially Boolean type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements3.json b/rule_packages/c/Statements3.json new file mode 100644 index 0000000000..94206d485f --- /dev/null +++ b/rule_packages/c/Statements3.json @@ -0,0 +1,130 @@ +{ + "MISRA-C-2012": { + "RULE-15-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "If the body of a switch is not enclosed in braces, then this can lead to incorrect execution, and is hard for developers to maintain.", + "kind": "problem", + "name": "The statement forming the body of a switch shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SwitchCompoundCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "if the body of a loop is not enclosed in braces, then this can lead to incorrect execution, and is hard for developers to maintain.", + "kind": "problem", + "name": "The statement forming the body of a loop shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "LoopCompoundCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "if the body of a selection statement is not enclosed in braces, then this can lead to incorrect execution, and is hard for developers to maintain.", + "kind": "problem", + "name": "The statement forming the body of a slection statement shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SelectionCompoundCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The body of an iteration-statement or a selection-statement shall be a compund-statement" + }, + "RULE-15-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Terminating an `if...else` construct is a defensive programming technique.", + "kind": "problem", + "name": "All if / else if constructs shall be terminated with an else statement", + "precision": "very-high", + "severity": "recommendation", + "shared_implementation_short_name": "IfElseTerminationConstruct", + "short_name": "IfElseEndCondition", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "All if / else if constructs shall be terminated with an else statement" + }, + "RULE-16-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The switch statement syntax is weak and may lead to unspecified behaviour.", + "kind": "problem", + "name": "A well formed switch statement must start with a case clause", + "precision": "very-high", + "severity": "recommendation", + "shared_implementation_short_name": "SwitchCasePositionCondition", + "short_name": "SwitchCaseStartCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "The switch statement syntax is weak and may lead to unspecified behaviour.", + "kind": "problem", + "name": "A well formed switch statement should only have expression, compound, selection, iteration or try statements within its body", + "precision": "very-high", + "severity": "recommendation", + "shared_implementation_short_name": "SwitchNotWellFormed", + "short_name": "SwitchStmtNotWellFormed", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "All switch statements shall be well-formed" + }, + "RULE-17-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Recursive function may cause memory and system failure issues.", + "kind": "problem", + "name": "Functions shall not call themselves, either directly or indirectly", + "precision": "very-high", + "severity": "error", + "short_name": "RecursiveFunctionCondition", + "tags": [ + "maintainability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "Functions shall not call themselves, either directly or indirectly" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements4.json b/rule_packages/c/Statements4.json new file mode 100644 index 0000000000..e770fe032a --- /dev/null +++ b/rule_packages/c/Statements4.json @@ -0,0 +1,87 @@ +{ + "CERT-C": { + "FLP30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Loop counters should not use floating-point variables to keep code portable.", + "kind": "problem", + "name": "Do not use floating-point variables as loop counters", + "precision": "very-high", + "severity": "recommendation", + "short_name": "FloatingPointLoopCounters", + "tags": [ + "maintainability", + "readability", + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" + ] + } + ], + "title": "Do not use floating-point variables as loop counters" + } + }, + "MISRA-C-2012": { + "RULE-14-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A well-formed for loop makes code easier to review.", + "kind": "problem", + "name": "A for loop shall be well-formed", + "precision": "very-high", + "severity": "recommendation", + "short_name": "ForLoopNotWellFormed", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A for loop shall be well-formed" + }, + "RULE-14-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Non boolean conditions can be confusing for developers.", + "kind": "problem", + "name": "The condition of an if-statement shall have type bool", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NonBooleanIfCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + }, + { + "description": "Non boolean conditions can be confusing for developers.", + "kind": "problem", + "name": "The condition of an iteration statement shall have type bool", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NonBooleanIterationCondition", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements5.json b/rule_packages/c/Statements5.json new file mode 100644 index 0000000000..03380f4897 --- /dev/null +++ b/rule_packages/c/Statements5.json @@ -0,0 +1,74 @@ +{ + "MISRA-C-2012": { + "RULE-14-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "If a controlling expression has an invariant value then it is possible that there is a programming error.", + "kind": "problem", + "name": "Controlling expressions shall not be invariant", + "precision": "very-high", + "severity": "error", + "short_name": "ControllingExprInvariant", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "implementation_scope": { + "description": "Not all invariant logical expressions which contain dynamic values are detected to be invariant, for instance, `x < 3 && x > 5` where x does not have a statically known value." + }, + "title": "Controlling expressions shall not be invariant" + }, + "RULE-15-5": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Not having a single point of exit in a function can lead to unintentional behaviour.", + "kind": "problem", + "name": "A function should have a single point of exit at the end", + "precision": "very-high", + "severity": "recommendation", + "short_name": "FunctionReturnCondition", + "tags": [ + "maintainability", + "readability", + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A function should have a single point of exit at the end" + }, + "RULE-17-4": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Not returning with an expression from a non-void function can lead to undefined behaviour.", + "kind": "problem", + "name": "All exit paths from a function with non-void return type shall have an explicit return statement", + "precision": "very-high", + "severity": "error", + "short_name": "NonVoidFunctionReturnCondition", + "shared_implementation_short_name": "NonVoidFunctionDoesNotReturn", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "All exit paths from a function with non-void return type shall have an explicit return statement with an expression" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements6.json b/rule_packages/c/Statements6.json new file mode 100644 index 0000000000..c8ab3efe38 --- /dev/null +++ b/rule_packages/c/Statements6.json @@ -0,0 +1,26 @@ +{ + "MISRA-C-2012": { + "RULE-15-1": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "The goto statement shall not be used.", + "kind": "problem", + "name": "The goto statement should not be used", + "precision": "very-high", + "severity": "error", + "short_name": "GotoStatementUsed", + "shared_implementation_short_name": "GotoStatementShouldNotBeUsed", + "tags": [ + "correctness", + "security", + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The goto statement should not be used" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Static.json b/rule_packages/c/Static.json new file mode 100644 index 0000000000..2af2af402a --- /dev/null +++ b/rule_packages/c/Static.json @@ -0,0 +1,27 @@ +{ + "MISRA-C-2012": { + "RULE-17-6": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Using the static keyword in an array type is error prone, and relies on the programmer to adhere to the guarantees to avoid undefined behavior.", + "kind": "problem", + "name": "The declaration of an array parameter shall not contain the static keyword between the [ ]", + "precision": "very-high", + "severity": "error", + "short_name": "UseOfArrayStatic", + "tags": [ + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "The static keyword is associated with particular array types in our model. This means we can get false positives when two parameter use the same array type and size, but only one of which uses the `static` keyword." + } + } + ], + "title": "The declaration of an array parameter shall not contain the static keyword between the [ ]" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Strings1.json b/rule_packages/c/Strings1.json index a0347aefc2..c4565fc898 100644 --- a/rule_packages/c/Strings1.json +++ b/rule_packages/c/Strings1.json @@ -14,7 +14,12 @@ "short_name": "DoNotAttemptToModifyStringLiterals", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], @@ -34,7 +39,12 @@ "short_name": "StringsHasSufficientSpaceForTheNullTerminator", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ], "implementation_scope": { "description": "The enforcement of this rule does not try to approximate the effects of loops and as such may not find cases where a loop operation on a string fails to null terminate a string (or causes an overflow)." @@ -57,8 +67,16 @@ "short_name": "NonNullTerminatedToFunctionThatExpectsAString", "tags": [ "correctness", - "security" - ] + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" + ], + "implementation_scope": { + "description": "Wide character types are not handled correctly on the `aarch64le` architecture. This can lead to false negative alerts." + } } ], "title": "Do not pass a non-null-terminated character sequence to a library function that expects a string" diff --git a/rule_packages/c/Strings2.json b/rule_packages/c/Strings2.json index 99f5e240d7..a32b1b4c28 100644 --- a/rule_packages/c/Strings2.json +++ b/rule_packages/c/Strings2.json @@ -14,7 +14,12 @@ "short_name": "ToCharacterHandlingFunctionsRepresentableAsUChar", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/c/Strings3.json b/rule_packages/c/Strings3.json index dff9744cdd..c9003f2ff8 100644 --- a/rule_packages/c/Strings3.json +++ b/rule_packages/c/Strings3.json @@ -12,9 +12,15 @@ "precision": "very-high", "severity": "error", "short_name": "CastCharBeforeConvertingToLargerSizes", + "shared_implementation_short_name": "CastCharBeforeConvertingToLargerSizes", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -34,8 +40,16 @@ "short_name": "DoNotConfuseNarrowAndWideFunctions", "tags": [ "correctness", - "security" - ] + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p27", + "external/cert/level/l1" + ], + "implementation_scope": { + "description": "Wide character types are not handled correctly on the `aarch64le` architecture. This can lead to false negative alerts." + } } ], "title": "Do not confuse narrow and wide character strings and functions" diff --git a/rule_packages/c/Syntax.json b/rule_packages/c/Syntax.json index 476254130b..e588c366c0 100644 --- a/rule_packages/c/Syntax.json +++ b/rule_packages/c/Syntax.json @@ -14,7 +14,8 @@ "short_name": "CharacterSequencesAndUsedWithinAComment", "tags": [ "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -35,7 +36,8 @@ "tags": [ "maintainability", "readability", - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -53,16 +55,18 @@ "precision": "very-high", "severity": "warning", "short_name": "OctalAndHexadecimalEscapeSequencesNotTerminated", + "shared_implementation_short_name": "NonTerminatedEscapeSequences", "tags": [ "maintainability", "readability", - "correctness" + "correctness", + "external/misra/c/2012/third-edition-first-revision" ] } ], "title": "Octal and hexadecimal escape sequences shall be terminated" }, - "RULE-4-4": { + "DIR-4-4": { "properties": { "obligation": "advisory" }, @@ -78,8 +82,9 @@ "tags": [ "maintainability", "readability", - "correctness" - ] + "correctness", + "external/misra/c/2012/third-edition-first-revision" + ] } ], "title": "Sections of code should not be commented out" @@ -96,10 +101,11 @@ "precision": "very-high", "severity": "recommendation", "short_name": "IdentifiersInTheSameNameSpaceUnambiguous", - "shared_implementation_short_name" : "DifferentIdentifiersNotTypographicallyUnambiguous", + "shared_implementation_short_name": "DifferentIdentifiersNotTypographicallyUnambiguous", "tags": [ "readability", - "maintainability" + "maintainability", + "external/misra/c/2012/third-edition-first-revision" ] } ], @@ -119,8 +125,12 @@ "short_name": "UOrUSuffixRepresentedInUnsignedType", "tags": [ "maintainability", - "readability" - ] + "readability", + "external/misra/c/2012/third-edition-first-revision" + ], + "implementation_scope": { + "description": "This implementation does not consider constants defined in macro bodies." + } } ], "title": "A 'U' or 'u' suffix shall be applied to all integer constants that are represented in an unsigned type" @@ -137,9 +147,11 @@ "precision": "very-high", "severity": "recommendation", "short_name": "LowercaseCharacterLUsedInLiteralSuffix", + "shared_implementation_short_name": "LowercaseLStartsInLiteralSuffix", "tags": [ "maintainability", - "readability" + "readability", + "external/misra/c/2012/third-edition-first-revision" ] } ], diff --git a/rule_packages/c/Types1.json b/rule_packages/c/Types1.json new file mode 100644 index 0000000000..bb451eba70 --- /dev/null +++ b/rule_packages/c/Types1.json @@ -0,0 +1,109 @@ +{ + "CERT-C": { + "INT34-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "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", + "name": "Bit shift should not be done by a negative operand or an operand of greater-or-equal precision than that of another", + "precision": "very-high", + "severity": "error", + "short_name": "ExprShiftedbyNegativeOrGreaterPrecisionOperand", + "tags": [ + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "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" + }, + "INT36-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Converting between pointers and integers is not portable and might cause invalid memory access.", + "kind": "problem", + "name": "Do not convert pointers to integers and back", + "precision": "very-high", + "severity": "error", + "short_name": "ConvertingAPointerToIntegerOrIntegerToPointer", + "tags": [ + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" + ] + } + ], + "title": "Converting a pointer to integer or integer to pointer" + } + }, + "MISRA-C-2012": { + "DIR-4-6": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Using plain numerical types over typedefs with explicit sign and bit counts may lead to confusion on how much bits are allocated for a value.", + "kind": "problem", + "name": "Do not use plain numerical types over typedefs named after their explicit bit layout", + "precision": "high", + "severity": "error", + "short_name": "PlainNumericalTypeUsedOverExplicitTypedef", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "typedefs that indicate size and signedness should be used in place of the basic numerical types" + }, + "RULE-12-5": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Using sizeof operator on an array type function parameter leads to unintended results.", + "kind": "problem", + "name": "The sizeof operator should not be used on an array type function parameter", + "precision": "very-high", + "severity": "error", + "short_name": "SizeofOperatorUsedOnArrayTypeParam", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "The sizeof operator shall not have an operand which is a function parameter declared as 'array of type'" + }, + "RULE-7-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Assigning string literal to a variable with type other than a pointer to const char and modifying it causes undefined behavior .", + "kind": "problem", + "name": "A string literal shall only be assigned to a pointer to const char", + "precision": "very-high", + "severity": "error", + "short_name": "StringLiteralAssignedToNonConstChar", + "tags": [ + "external/misra/c/2012/third-edition-first-revision" + ] + } + ], + "title": "A string literal shall not be assigned to an object unless the object's type is 'pointer to const-qualified char'" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Types2.json b/rule_packages/c/Types2.json new file mode 100644 index 0000000000..7e4c0827fe --- /dev/null +++ b/rule_packages/c/Types2.json @@ -0,0 +1,84 @@ +{ + "MISRA-C-2012": { + "RULE-7-5": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Integer constant macros should be given a literal value as an argument.", + "kind": "problem", + "name": "The argument of an integer constant macro shall be a literal", + "precision": "very-high", + "severity": "warning", + "short_name": "InvalidIntegerConstantMacroArgument", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Integer constant macro arguments should be a decimal, hex, or octal literal.", + "kind": "problem", + "name": "The argument of an integer constant macro shall be a decimal, hex, or octal literal", + "precision": "very-high", + "severity": "error", + "short_name": "InvalidLiteralForIntegerConstantMacroArgument", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Integer constant macros should be used integer literal values with no u/l suffix.", + "kind": "problem", + "name": "The argument of an integer constant macro shall not use literal suffixes u, l, or ul", + "precision": "high", + "severity": "warning", + "short_name": "IntegerConstantMacroArgumentUsesSuffix", + "tags": [ + "readability", + "maintainability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "Integer constant macros argument values should be values of a compatible size.", + "kind": "problem", + "name": "The argument of an integer constant macro shall have an appropriate size", + "precision": "very-high", + "severity": "error", + "short_name": "IncorrectlySizedIntegerConstantMacroArgument", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ], + "implementation_scope": { + "description": "This rule can validate integers sized 32 or smaller. When the CodeQL runtime supports big ints, this will be expanded to include 64 bit integer types." + } + } + ], + "title": "The argument of an integer constant macro shall have an appropriate form" + }, + "RULE-7-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Small integer constant macros expression are promoted to type int, which can lead to unexpected results.", + "kind": "problem", + "name": "The small integer variants of the minimum-width integer constant macros shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "UseOfBannedSmallIntegerConstantMacro", + "tags": [ + "readability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "The small integer variants of the minimum-width integer constant macros shall not be used" + } + } +} \ No newline at end of file diff --git a/rule_packages/cpp/Allocations.json b/rule_packages/cpp/Allocations.json index 6b40523e16..416cd3b567 100644 --- a/rule_packages/cpp/Allocations.json +++ b/rule_packages/cpp/Allocations.json @@ -197,7 +197,12 @@ "short_name": "ProperlyDeallocateDynamicallyAllocatedResources", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -216,7 +221,12 @@ "severity": "error", "short_name": "DetectAndHandleMemoryAllocationErrors", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -235,7 +245,12 @@ "severity": "error", "short_name": "MissingConstructorCallForManuallyManagedObject", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] }, { @@ -246,7 +261,12 @@ "severity": "error", "short_name": "MissingDestructorCallForManuallyManagedObject", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -267,7 +287,12 @@ "shared_implementation_short_name": "PlacementNewNotProperlyAligned", "tags": [ "security", - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] }, { @@ -280,7 +305,12 @@ "shared_implementation_short_name": "PlacementNewInsufficientStorage", "tags": [ "security", - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -300,7 +330,12 @@ "short_name": "ThrowingOperatorNewReturnsNullCert", "shared_implementation_short_name": "ThrowingOperatorNewReturnsNull", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] }, { @@ -312,7 +347,12 @@ "short_name": "ThrowingOperatorNewThrowsInvalidExceptionCert", "shared_implementation_short_name": "ThrowingOperatorNewThrowsInvalidException", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] }, { @@ -324,7 +364,12 @@ "short_name": "ThrowingNoThrowOperatorNewDeleteCert", "shared_implementation_short_name": "ThrowingNoThrowOperatorNewDelete", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] }, { @@ -336,7 +381,12 @@ "short_name": "OperatorDeleteMissingPartnerCert", "shared_implementation_short_name": "OperatorDeleteMissingPartner", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -356,7 +406,12 @@ "short_name": "UsingDefaultOperatorNewForOverAlignedTypes", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/BannedFunctions.json b/rule_packages/cpp/BannedFunctions.json index 1ee1dc31e2..6cdb019ace 100644 --- a/rule_packages/cpp/BannedFunctions.json +++ b/rule_packages/cpp/BannedFunctions.json @@ -89,6 +89,7 @@ "precision": "very-high", "severity": "error", "short_name": "SetjmpMacroAndTheLongjmpFunctionUsed", + "shared_implementation_short_name": "DoNotUseSetjmpOrLongjmpShared", "tags": [ "correctness", "scope/single-translation-unit" @@ -188,6 +189,7 @@ "precision": "very-high", "severity": "error", "short_name": "MacroOffsetofUsed", + "shared_implementation_short_name": "MacroOffsetofUsed", "tags": [ "security", "scope/single-translation-unit" @@ -210,9 +212,15 @@ "precision": "very-high", "severity": "error", "short_name": "DoNotUseSetjmpOrLongjmp", + "shared_implementation_short_name": "DoNotUseSetjmpOrLongjmpShared", "tags": [ "correctness", - "scope/single-translation-unit" + "scope/single-translation-unit", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -233,7 +241,12 @@ "shared_implementation_short_name": "DoNotUseRandForGeneratingPseudorandomNumbers", "tags": [ "security", - "scope/single-translation-unit" + "scope/single-translation-unit", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -253,7 +266,12 @@ "short_name": "PreferSpecialMemberFunctionsAndOverloadedOperatorsToCStandardLibraryFunctions", "tags": [ "correctness", - "scope/single-translation-unit" + "scope/single-translation-unit", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/BannedLibraries.json b/rule_packages/cpp/BannedLibraries.json index 4eb5896cfd..fce11b9eca 100644 --- a/rule_packages/cpp/BannedLibraries.json +++ b/rule_packages/cpp/BannedLibraries.json @@ -114,6 +114,7 @@ "precision": "very-high", "severity": "warning", "short_name": "CsignalFunctionsUsed", + "shared_implementation_short_name": "CsignalFunctionsUsed", "tags": [ "maintainability", "correctness", @@ -127,6 +128,7 @@ "precision": "very-high", "severity": "warning", "short_name": "CsignalTypesUsed", + "shared_implementation_short_name": "CsignalTypesUsed", "tags": [ "maintainability", "correctness", @@ -177,6 +179,7 @@ "precision": "very-high", "severity": "warning", "short_name": "CstdioFunctionsUsed", + "shared_implementation_short_name": "CstdioFunctionsUsed", "tags": [ "maintainability", "correctness", @@ -190,6 +193,7 @@ "precision": "very-high", "severity": "warning", "short_name": "CstdioMacrosUsed", + "shared_implementation_short_name": "CstdioMacrosUsed", "tags": [ "maintainability", "correctness", @@ -203,6 +207,7 @@ "precision": "very-high", "severity": "warning", "short_name": "CstdioTypesUsed", + "shared_implementation_short_name": "CstdioTypesUsed", "tags": [ "maintainability", "correctness", @@ -229,6 +234,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "UsageOfAssemblerNotDocumented", + "shared_implementation_short_name": "UsageOfAssemblerNotDocumented", "tags": [ "readability", "maintainability", diff --git a/rule_packages/cpp/BannedSyntax.json b/rule_packages/cpp/BannedSyntax.json index e2f3ce6ef7..8f739145f7 100644 --- a/rule_packages/cpp/BannedSyntax.json +++ b/rule_packages/cpp/BannedSyntax.json @@ -141,7 +141,14 @@ "tags": [ "correctness", "scope/single-translation-unit" - ] + ], + "implementation_scope": { + "description": "This query has the following limitations:", + "items": [ + "It erroneously reports functional notation casts on primitive types (e.g. int(x)) as traditional C-style casts.", + "It will not report C-Style casts that result in a direct initialization via a constructor call with the given argument." + ] + } } ], "title": "Traditional C-style casts shall not be used." @@ -162,6 +169,7 @@ "precision": "very-high", "severity": "error", "short_name": "ReinterpretCastUsed", + "shared_implementation_short_name": "ReinterpretCastUsed", "tags": [ "correctness", "security", @@ -187,6 +195,7 @@ "precision": "very-high", "severity": "error", "short_name": "GotoStatementUsed", + "shared_implementation_short_name": "GotoStatementShouldNotBeUsed", "tags": [ "correctness", "security", @@ -259,6 +268,7 @@ "name": "The asm declaration shall not be used", "precision": "very-high", "severity": "error", + "shared_implementation_short_name": "AsmDeclarationUsed", "short_name": "AsmDeclarationUsed", "tags": [ "correctness", @@ -407,7 +417,12 @@ "tags": [ "correctness", "security", - "scope/single-translation-unit" + "scope/single-translation-unit", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ] } ], diff --git a/rule_packages/cpp/BannedTypes.json b/rule_packages/cpp/BannedTypes.json index 4a45433746..e84399b928 100644 --- a/rule_packages/cpp/BannedTypes.json +++ b/rule_packages/cpp/BannedTypes.json @@ -41,6 +41,7 @@ "precision": "very-high", "severity": "warning", "short_name": "VectorboolSpecializationUsed", + "shared_implementation_short_name": "VectorShouldNotBeSpecializedWithBool", "tags": [ "correctness", "scope/single-translation-unit" diff --git a/rule_packages/cpp/Classes.json b/rule_packages/cpp/Classes.json index 61eab45081..e7c8a10d92 100644 --- a/rule_packages/cpp/Classes.json +++ b/rule_packages/cpp/Classes.json @@ -178,23 +178,16 @@ "obligation": "required" }, "queries": [ - { - "description": "A function that is either trivial, a template function, or a member of a template class may not be defined outside of a class body.", - "kind": "problem", - "name": "A function shall be defined with a class body if and only if it is intended to be inlined", - "precision": "very-high", - "severity": "recommendation", - "short_name": "TrivialOrTemplateFunctionDefinedOutsideClassDefinition", - "tags": [] - }, { "description": "A function that is not either trivial, a template function, or a member of a template class may not be defined within a class body.", "kind": "problem", "name": "A function shall be defined with a class body if and only if it is intended to be inlined", - "precision": "very-high", + "precision": "low", "severity": "recommendation", "short_name": "NonTrivialNonTemplateFunctionDefinedInsideClassDefinition", - "tags": [] + "tags": [ + "external/autosar/audit" + ] } ], "title": "A function definition shall only be placed in a class definition if (1) the function is intended to be inlined (2) it is a member function template (3) it is a member function of a class template." @@ -322,7 +315,12 @@ "severity": "recommendation", "short_name": "OffsetUsedOnInvalidTypeOrMember", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Comments.json b/rule_packages/cpp/Comments.json index d6364f01d6..2421bec52f 100644 --- a/rule_packages/cpp/Comments.json +++ b/rule_packages/cpp/Comments.json @@ -16,6 +16,7 @@ "precision": "very-high", "severity": "warning", "short_name": "SingleLineCommentEndsWithSlash", + "shared_implementation_short_name": "LineSplicingUsedInComments", "tags": [ "correctness", "readability", @@ -70,7 +71,10 @@ "tags": [ "maintainability", "readability" - ] + ], + "implementation_scope": { + "description": "Function scope declarations are excluded from this rule as they are restricted in scope to only a single function." + } } ], "title": "All declarations of 'user-defined' types, static and non-static data members, functions and methods shall be preceded by documentation." @@ -91,6 +95,7 @@ "precision": "very-high", "severity": "warning", "short_name": "SlashStarUsedWithinACStyleComment", + "shared_implementation_short_name": "CharacterSequenceUsedWithinACStyleComment", "tags": [ "maintainability", "readability", diff --git a/rule_packages/cpp/Concurrency.json b/rule_packages/cpp/Concurrency.json index 6e5898ecd8..3bba2f409f 100644 --- a/rule_packages/cpp/Concurrency.json +++ b/rule_packages/cpp/Concurrency.json @@ -15,7 +15,12 @@ "shared_implementation_short_name": "DoNotAllowAMutexToGoOutOfScopeWhileLocked", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -28,7 +33,12 @@ "shared_implementation_short_name": "DoNotDestroyAMutexWhileItIsLocked", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -48,7 +58,12 @@ "short_name": "EnsureActivelyHeldLocksAreReleasedOnExceptionalConditions", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -69,7 +84,12 @@ "shared_implementation_short_name": "GuardAccessToBitFields", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -90,7 +110,12 @@ "shared_implementation_short_name": "PreventDeadlockByLockingInPredefinedOrder", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -111,7 +136,12 @@ "shared_implementation_short_name": "WrapSpuriousFunctionInLoop", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -132,7 +162,12 @@ "shared_implementation_short_name": "PreserveSafetyWhenUsingConditionVariables", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -152,7 +187,12 @@ "short_name": "DoNotSpeculativelyLockALockedNonRecursiveMutex", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p1", + "external/cert/level/l3" ] }, { @@ -164,7 +204,12 @@ "short_name": "LockedALockedNonRecursiveMutexAudit", "tags": [ "correctness", - "concurrency" + "concurrency", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p1", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Conditionals.json b/rule_packages/cpp/Conditionals.json index 4c382a06e9..584df19420 100644 --- a/rule_packages/cpp/Conditionals.json +++ b/rule_packages/cpp/Conditionals.json @@ -16,6 +16,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "NonBooleanIfCondition", + "shared_implementation_short_name": "NonBooleanIfStmt", "tags": [ "maintainability", "readability" @@ -28,6 +29,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "NonBooleanIterationCondition", + "shared_implementation_short_name": "NonBooleanIterationStmt", "tags": [ "maintainability", "readability" @@ -76,6 +78,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "SwitchCompoundCondition", + "shared_implementation_short_name": "SwitchCompoundCondition", "tags": [ "maintainability", "readability" @@ -88,6 +91,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "LoopCompoundCondition", + "shared_implementation_short_name": "LoopCompoundCondition", "tags": [ "maintainability", "readability" @@ -136,6 +140,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "IfElseTerminationCondition", + "shared_implementation_short_name": "IfElseTerminationConstruct", "tags": [ "maintainability", "readability" @@ -160,6 +165,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "SwitchDoesNotStartWithCase", + "shared_implementation_short_name": "SwitchCasePositionCondition", "tags": [ "maintainability", "readability" @@ -172,6 +178,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "SwitchStatementNotWellFormed", + "shared_implementation_short_name": "SwitchNotWellFormed", "tags": [ "maintainability", "readability" @@ -190,12 +197,13 @@ }, "queries": [ { - "description": "By default in C++, the switch structure is weak, which may lead to switch labels being placed anywhere in the switch block. This can cause unspecified behaviour.", + "description": "Nested switch labels cause undefined behaviour.", "kind": "problem", "name": "A switch-label shall only be used when the most closely-enclosing compound statement is the body of a switch statement", "precision": "very-high", "severity": "recommendation", "short_name": "NestedCaseInSwitch", + "shared_implementation_short_name": "NestedLabelInSwitch", "tags": [ "maintainability", "readability" @@ -328,6 +336,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "GotoStatementJumpCondition", + "shared_implementation_short_name": "GotoStatementCondition", "tags": [ "maintainability", "readability" diff --git a/rule_packages/cpp/Const.json b/rule_packages/cpp/Const.json index ca848c2fdc..6f76b7f5b8 100644 --- a/rule_packages/cpp/Const.json +++ b/rule_packages/cpp/Const.json @@ -33,19 +33,6 @@ "obligation": "required" }, "queries": [ - { - "description": "`Constexpr`/`const` specifiers prevent unintentional data modification for parameters intended as immutable.", - "kind": "problem", - "name": "Constexpr or const specifiers shall be used for immutable parameter usage", - "precision": "high", - "severity": "warning", - "short_name": "DeclarationUnmodifiedParamMissingConstSpecifier", - "tags": [ - "correctness", - "maintainability", - "readability" - ] - }, { "description": "`Constexpr`/`const` specifiers prevent unintentional data modification for data intended as immutable.", "kind": "problem", @@ -57,7 +44,10 @@ "correctness", "maintainability", "readability" - ] + ], + "implementation_scope": { + "description": "We exclude function parameters from this rule in line with the rule intention as described in the C++ Core Guidelines Con.1 which excludes function parameters." + } } ], "title": "Constexpr or const specifiers shall be used for immutable data declaration." @@ -81,17 +71,6 @@ "tags": [ "maintainability" ] - }, - { - "description": "Using 'constexpr' makes it clear that a function is intended to return a compile time constant.", - "kind": "problem", - "name": "The constexpr specifier shall be used for functions whose return value can be determined at compile time", - "precision": "high", - "severity": "recommendation", - "short_name": "FunctionMissingConstexpr", - "tags": [ - "maintainability" - ] } ], "title": "The constexpr specifier shall be used for values that can be determined at compile time." @@ -272,7 +251,12 @@ "shared_implementation_short_name": "RemoveConstOrVolatileQualification", "short_name": "RemoveConstOrVolatileQualificationCert", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/DeadCode.json b/rule_packages/cpp/DeadCode.json index ec17e4b84f..4746f86dee 100644 --- a/rule_packages/cpp/DeadCode.json +++ b/rule_packages/cpp/DeadCode.json @@ -67,7 +67,10 @@ "tags": [ "readability", "maintainability" - ] + ], + "implementation_scope": { + "description": "Use of any overload of a function in an overload set constitute a use of all members of the set. An overload set is a set of functions with the same name that differ in the number, type and/or qualifiers of their parameters, and, for the purpose of this query, are limited to functions which are declared in the same scope (namespace or class). Functions defined in anonymous (unnamed) namespaces and global namespaces are therefore not currently considered to be part of the same overload set." + } } ], "title": "Every function defined in an anonymous namespace, or static function with internal linkage, or private member function shall be used." @@ -91,7 +94,8 @@ "tags": [ "readability", "maintainability" - ] + ], + "shared_implementation_short_name": "UnusedParameter" } ], "title": "There shall be no unused named parameters in non-virtual functions." @@ -139,7 +143,8 @@ "tags": [ "readability", "maintainability" - ] + ], + "shared_implementation_short_name": "UnusedTypeDeclarations" } ], "title": "There should be no unused type declarations." @@ -163,7 +168,8 @@ "tags": [ "readability", "maintainability" - ] + ], + "shared_implementation_short_name": "UnreachableCode" } ], "title": "A project shall not contain unreachable code." @@ -188,6 +194,21 @@ "readability", "maintainability" ] + }, + { + "description": "Uncalled functions complicate the program and can indicate a possible mistake on the part of the programmer.", + "kind": "problem", + "name": "Every defined function should be called at least once", + "precision": "medium", + "severity": "warning", + "short_name": "UnusedSplMemberFunction", + "tags": [ + "readability", + "maintainability" + ], + "implementation_scope": { + "description": "In limited cases, this query can raise false-positives for special member function calls invoked from the C++ Metaprogramming library." + } } ], "title": "Every defined function should be called at least once." @@ -235,7 +256,10 @@ "tags": [ "readability", "maintainability" - ] + ], + "implementation_scope": { + "description": "In limited cases, this query can raise false-positives for variables that are defined as constexpr and used in an expression to instantiate a template." + } }, { "description": "Unused variables complicate the program and can indicate a possible mistake on the part of the programmer.", @@ -334,10 +358,11 @@ "tags": [ "readability", "maintainability" - ] + ], + "shared_implementation_short_name": "DeadCode" } ], "title": "There shall be no dead code." } } -} \ No newline at end of file +} diff --git a/rule_packages/cpp/Declarations.json b/rule_packages/cpp/Declarations.json index 2b22de6de1..a5b8ebeec3 100644 --- a/rule_packages/cpp/Declarations.json +++ b/rule_packages/cpp/Declarations.json @@ -50,6 +50,7 @@ "precision": "very-high", "severity": "error", "short_name": "GlobalSizedOperatorDeleteNotDefined", + "shared_implementation_short_name": "GlobalSizedOperatorDeleteNotDefined", "tags": [ "maintainability" ] @@ -61,6 +62,7 @@ "precision": "very-high", "severity": "error", "short_name": "GlobalUnsizedOperatorDeleteNotDefined", + "shared_implementation_short_name": "GlobalUnsizedOperatorDeleteNotDefined", "tags": [ "maintainability" ] @@ -78,12 +80,28 @@ }, "queries": [ { - "description": "The basic numerical types of char, int, short, long are not supposed to be used. The specific-length types from header need be used instead.", + "description": "The basic numerical types of signed/unsigned char, int, short, long are not supposed to be used. The specific-length types from header need be used instead.", "kind": "problem", "name": "Use fixed-width integer types instead of basic, variable-width, integer types", "precision": "very-high", "severity": "error", "short_name": "VariableWidthIntegerTypesUsed", + "tags": [ + "correctness", + "security", + "maintainability" + ], + "implementation_scope": { + "description": "This implementation excludes the plain char type from consideration." + } + }, + { + "description": "The basic numerical type char is not supposed to be used. The specific-length types from header need be used instead.", + "kind": "problem", + "name": "Use a fixed-width integer type instead of a char type", + "precision": "very-high", + "severity": "error", + "short_name": "VariableWidthPlainCharTypeUsed", "tags": [ "correctness", "security", @@ -200,6 +218,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "EnumerationUnderlyingBaseTypeNotExplicitlyDefined", + "shared_implementation_short_name": "EnumerationNotDefinedWithAnExplicitUnderlyingType", "tags": [ "readability", "maintainability" diff --git a/rule_packages/cpp/ExceptionSafety.json b/rule_packages/cpp/ExceptionSafety.json index 07e97ae328..73b84edde4 100644 --- a/rule_packages/cpp/ExceptionSafety.json +++ b/rule_packages/cpp/ExceptionSafety.json @@ -90,7 +90,12 @@ "short_name": "GuaranteeExceptionSafety", "shared_implementation_short_name": "ExceptionSafetyGuarantees", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], @@ -111,7 +116,12 @@ "shared_implementation_short_name": "ExceptionSafetyValidState", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Exceptions1.json b/rule_packages/cpp/Exceptions1.json index d42c949b48..7c3a2a708a 100644 --- a/rule_packages/cpp/Exceptions1.json +++ b/rule_packages/cpp/Exceptions1.json @@ -90,6 +90,7 @@ "precision": "very-high", "severity": "error", "short_name": "PointerExceptionObject", + "shared_implementation_short_name": "ExceptionObjectHavePointerType", "tags": [ "correctness" ] @@ -224,6 +225,7 @@ "severity": "error", "kind": "path-problem", "short_name": "NoExceptFunctionThrows", + "shared_implementation_short_name": "NoexceptFunctionShouldNotPropagateToTheCaller", "tags": [ "correctness" ] @@ -428,6 +430,7 @@ "precision": "very-high", "severity": "error", "short_name": "EmptyThrowOutsideCatch", + "shared_implementation_short_name": "EmptyThrowOnlyWithinACatchHandler", "tags": [ "correctness" ] @@ -499,7 +502,12 @@ "shared_implementation_short_name": "ConditionVariablePostConditionFailed", "tags": [ "correctness", - "external/cert/audit" + "external/cert/audit", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -510,7 +518,12 @@ "short_name": "JoinableThreadCopiedOrDestroyedCert", "shared_implementation_short_name": "JoinableThreadCopiedOrDestroyed", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -522,7 +535,12 @@ "short_name": "RethrowNestedWithoutCaptureCert", "shared_implementation_short_name": "RethrowNestedWithoutCapture", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -534,7 +552,12 @@ "short_name": "ExplicitAbruptTerminationCert", "shared_implementation_short_name": "ExplicitAbruptTermination", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -546,7 +569,12 @@ "short_name": "ExitHandlerThrowsExceptionCert", "shared_implementation_short_name": "ExitHandlerThrowsException", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -565,7 +593,12 @@ "kind": "path-problem", "short_name": "HandleAllExceptions", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -584,7 +617,12 @@ "shared_implementation_short_name": "DestroyedValueReferencedInDestructorCatchBlock", "short_name": "DestroyedValueReferencedInConstructorDestructorCatchBlock", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -603,7 +641,12 @@ "kind": "path-problem", "short_name": "HonorExceptionSpecifications", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], @@ -623,7 +666,12 @@ "shared_implementation_short_name": "HandleAllExceptionsDuringStartup", "short_name": "HandleAllExceptionsThrownBeforeMainBeginsExecuting", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], @@ -641,7 +689,12 @@ "severity": "error", "short_name": "ExceptionObjectsMustBeNothrowCopyConstructible", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -660,7 +713,12 @@ "shared_implementation_short_name": "CatchExceptionsByLvalueReference", "short_name": "CatchExceptionsByLvalueReference", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Exceptions2.json b/rule_packages/cpp/Exceptions2.json index ece305a04a..2e2f2dfba6 100644 --- a/rule_packages/cpp/Exceptions2.json +++ b/rule_packages/cpp/Exceptions2.json @@ -295,7 +295,12 @@ "severity": "error", "short_name": "DoNotLetExceptionsEscapeFromDestructorsOrDeallocationFunctions", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -315,7 +320,12 @@ "shared_implementation_short_name": "CatchBlockShadowing", "short_name": "CatchBlockShadowingCert", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], diff --git a/rule_packages/cpp/Expressions.json b/rule_packages/cpp/Expressions.json index 8bf26eb14c..10f85237de 100644 --- a/rule_packages/cpp/Expressions.json +++ b/rule_packages/cpp/Expressions.json @@ -86,9 +86,13 @@ "precision": "very-high", "severity": "recommendation", "short_name": "FunctionErroneousReturnValueNotTested", + "shared_implementation_short_name": "FunctionErroneousReturnValueNotTested", "tags": [ "maintainability" - ] + ], + "implementation_scope": { + "description": "The query enforces checking on some C standard library functions that may return error codes." + } } ], "title": "If a function generates error information, then that error information shall be tested." @@ -126,7 +130,7 @@ }, "queries": [ { - "description": "Expressions with type (plain) char and wchar_t shall not be used as operands to built-in operators other than the assignment operator =, the equality operators == and ! =, and the unary & operator. Manipulation of character data may generate results that are contrary to developer expectations. For example, ISO/IEC 14882:2003 [1] §2.2(3) only requires that the digits \"0\" to \"9\" have consecutive numerical values.", + "description": "Expressions with type (plain) char and wchar_t shall not be used as operands to built-in operators other than the assignment operator =, the equality operators == and ! =, and the unary & operator. Manipulation of character data may generate results that are contrary to developer expectations. For example, ISO/IEC 14882:2003 [1] \u00a72.2(3) only requires that the digits \"0\" to \"9\" have consecutive numerical values.", "kind": "problem", "name": "Expressions with type (plain) char and wchar_t shall only be used as operands to =, ==, !=, &", "precision": "very-high", @@ -249,6 +253,7 @@ "precision": "very-high", "severity": "error", "short_name": "ConstantUnsignedIntegerExpressionsWrapAround", + "shared_implementation_short_name": "ConstantUnsignedIntegerExpressionsWrapAround", "tags": [ "correctness", "security" @@ -318,7 +323,12 @@ "severity": "error", "short_name": "PassPromotablePrimitiveTypeToVaStart", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -329,7 +339,12 @@ "severity": "error", "short_name": "PassReferenceTypeToVaStart", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -340,7 +355,12 @@ "severity": "warning", "short_name": "PassNonTrivialObjectToVaStart", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/FloatingPoint.json b/rule_packages/cpp/FloatingPoint.json new file mode 100644 index 0000000000..672e57acff --- /dev/null +++ b/rule_packages/cpp/FloatingPoint.json @@ -0,0 +1,36 @@ +{ + "MISRA-C++-2023": { + "DIR-0-3-1": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Possible misuse of a generate infinite floating point value.", + "kind": "path-problem", + "name": "Possible misuse of a generate infinite floating point value", + "precision": "medium", + "severity": "warning", + "short_name": "PossibleMisuseOfInfiniteFloatingPointValue", + "shared_implementation_short_name": "MisuseOfInfiniteFloatingPointValue", + "tags": [ + "correctness" + ] + }, + { + "description": "Possible mishandling of an undetected NaN value produced by a floating point operation.", + "kind": "path-problem", + "name": "Possible mishandling of an undetected NaN value produced by a floating point operation", + "precision": "low", + "severity": "warning", + "short_name": "PossibleMisuseOfNaNFloatingPointValue", + "shared_implementation_short_name": "MisuseOfNaNFloatingPointValue", + "tags": [ + "correctness" + ] + } + ], + "title": "Floating-point arithmetic should be used appropriately" + } + } +} \ No newline at end of file diff --git a/rule_packages/cpp/Freed.json b/rule_packages/cpp/Freed.json index 36b9b31c3d..30ab6982b2 100644 --- a/rule_packages/cpp/Freed.json +++ b/rule_packages/cpp/Freed.json @@ -111,7 +111,12 @@ "severity": "error", "short_name": "DoNotDeleteAnArrayThroughAPointerOfTheIncorrectType", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -132,7 +137,12 @@ "short_name": "ObjectAccessedBeforeLifetimeCert", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -145,7 +155,12 @@ "short_name": "ObjectAccessedAfterLifetimeCert", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -166,7 +181,12 @@ "short_name": "UseAfterFree", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], diff --git a/rule_packages/cpp/Functions.json b/rule_packages/cpp/Functions.json index 7f21cf0873..b650b0937c 100644 --- a/rule_packages/cpp/Functions.json +++ b/rule_packages/cpp/Functions.json @@ -87,6 +87,7 @@ "precision": "very-high", "severity": "error", "short_name": "RecursiveFunctions", + "shared_implementation_short_name": "FunctionsCallThemselvesEitherDirectlyOrIndirectly", "tags": [ "correctness", "maintainability" @@ -232,6 +233,7 @@ "precision": "very-high", "severity": "error", "short_name": "FunctionReturnAutomaticVarCondition", + "shared_implementation_short_name": "ReturnReferenceOrPointerToAutomaticLocalVariable", "tags": [ "correctness", "security" @@ -279,7 +281,12 @@ "severity": "error", "short_name": "FunctionWithMismatchedLanguageLinkage", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -299,7 +306,12 @@ "short_name": "NonVoidFunctionDoesNotReturnCert", "shared_implementation_short_name": "NonVoidFunctionDoesNotReturn", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -319,11 +331,16 @@ "short_name": "FunctionNoReturnAttributeConditionCert", "shared_implementation_short_name": "FunctionNoReturnAttributeCondition", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], "title": "Do not return from a function declared [[noreturn]]" } } -} +} \ No newline at end of file diff --git a/rule_packages/cpp/IO.json b/rule_packages/cpp/IO.json index 9ad0650e62..3d1012232c 100644 --- a/rule_packages/cpp/IO.json +++ b/rule_packages/cpp/IO.json @@ -43,7 +43,12 @@ "short_name": "InterleavedInputOutputWithoutPosition", "shared_implementation_short_name": "IOFstreamMissingPositioning", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -63,7 +68,12 @@ "short_name": "CloseFilesWhenTheyAreNoLongerNeeded", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/ImportMisra23.json b/rule_packages/cpp/ImportMisra23.json new file mode 100644 index 0000000000..243fc7cc20 --- /dev/null +++ b/rule_packages/cpp/ImportMisra23.json @@ -0,0 +1,1733 @@ +{ + "MISRA-C++-2023": { + "DIR-5-7-2": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Commented out code may become out of date leading to developer confusion.", + "kind": "problem", + "name": "Sections of code should not be \u201ccommented out\u201d", + "precision": "very-high", + "severity": "error", + "short_name": "SectionsOfCodeShouldNotBeCommentedOut", + "shared_implementation_short_name": "SectionsOfCodeShallNotBeCommentedOut", + "tags": [ + "maintainability", + "readability", + "correctness" + ] + } + ], + "title": "Sections of code should not be \u201ccommented out\u201d" + }, + "RULE-6-2-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The one-definition rule specifies when there should be a single definition of an element and a violation of that rule leads to undefined behavior.", + "kind": "problem", + "name": "The one-definition rule shall not be violated", + "precision": "very-high", + "severity": "error", + "short_name": "OneDefinitionRuleViolated", + "shared_implementation_short_name": "OneDefinitionRuleViolation", + "tags": [ + "correctness", + "scope/system" + ] + } + ], + "title": "The one-definition rule shall not be violated" + }, + "RULE-6-4-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Use of an identifier declared in an inner scope with an identical name to an identifier in an outer scope can lead to inadvertent errors if the incorrect identifier is modified.", + "kind": "problem", + "name": "A variable declared in an inner scope shall not hide a variable declared in an outer scope", + "precision": "very-high", + "severity": "error", + "short_name": "VariableDeclaredInInnerScopeHidesOuterScope", + "shared_implementation_short_name": "IdentifierHidden", + "tags": [ + "readability", + "maintainability", + "scope/single-translation-unit" + ] + } + ], + "title": "A variable declared in an inner scope shall not hide a variable declared in an outer scope" + }, + "RULE-6-8-1": { + "properties": { + "enforcement": "undecidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Accessing an object before its lifetime can result in undefined behavior.", + "kind": "problem", + "name": "Access of uninitialized object", + "precision": "high", + "severity": "error", + "shared_implementation_short_name": "ObjectAccessedBeforeLifetime", + "short_name": "ObjectAccessedBeforeLifetimeMisra", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Accessing an object after its lifetime results in undefined behavior.", + "kind": "problem", + "name": "Access of object after lifetime (use-after-free)", + "precision": "high", + "severity": "error", + "shared_implementation_short_name": "ObjectAccessedAfterLifetime", + "short_name": "ObjectAccessedAfterLifetimeMisra", + "tags": [ + "correctness", + "security" + ] + } + ], + "title": "An object shall not be accessed outside of its lifetime" + }, + "RULE-8-2-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A cast shall not remove any const or volatile qualification from the type accessed via a pointer or by reference.", + "kind": "problem", + "name": "A cast shall not remove any const or volatile qualification from the type accessed via a pointer or", + "precision": "very-high", + "severity": "error", + "short_name": "CastRemovesConstOrVolatileFromPointerOrReference", + "shared_implementation_short_name": "RemoveConstOrVolatileQualification", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "A cast shall not remove any const or volatile qualification from the type accessed via a pointer or by reference" + }, + "RULE-9-4-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "All if ... else if constructs shall be terminated with an else statement.", + "kind": "problem", + "name": "All if ... else if constructs shall be terminated with an else statement", + "precision": "very-high", + "severity": "error", + "short_name": "IfElseIfEndCondition", + "shared_implementation_short_name": "IfElseTerminationConstruct", + "tags": [ + "readability", + "maintainability", + "scope/single-translation-unit" + ] + } + ], + "title": "All if ... else if constructs shall be terminated with an else statement" + }, + "RULE-9-6-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Jumping back to an earlier section in the code can lead to accidental iterations.", + "kind": "problem", + "name": "The goto statement shall jump to a label declared later in the function body", + "precision": "very-high", + "severity": "error", + "short_name": "GotoShallJumpToLabelDeclaredLaterInTheFunction", + "shared_implementation_short_name": "GotoStatementCondition", + "tags": [ + "maintainability", + "readability", + "scope/single-translation-unit" + ] + } + ], + "title": "The goto statement shall jump to a label declared later in the function body" + }, + "RULE-9-6-4": { + "properties": { + "enforcement": "undecidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A function with the [[noreturn]] attribute that returns leads to undefined behaviour.", + "kind": "problem", + "name": "A function declared with the [[noreturn]] attribute shall not return", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionDeclaredWithTheNoreturnAttributeReturn", + "shared_implementation_short_name": "FunctionNoReturnAttributeCondition", + "tags": [ + "correctness", + "scope/system" + ] + } + ], + "title": "A function declared with the [[noreturn]] attribute shall not return" + }, + "RULE-9-6-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A function with non-void return type that does not exit via a return statement can result in undefined behaviour. An exception to this rule is exiting via exception handling.", + "kind": "problem", + "name": "A function with non-void return type shall return a value on all paths", + "precision": "very-high", + "severity": "error", + "short_name": "NonVoidFunctionShallReturnAValueOnAllPaths", + "shared_implementation_short_name": "NonVoidFunctionDoesNotReturn", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "A function with non-void return type shall return a value on all paths" + }, + "RULE-11-3-2": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Declarations with more than two levels of pointer nesting can result in code that is difficult to read and understand.", + "kind": "problem", + "name": "The declaration of an object should contain no more than two levels of pointer indirection", + "precision": "very-high", + "severity": "error", + "short_name": "DeclarationOfAnObjectIndirectionsLevel", + "shared_implementation_short_name": "DoNotUseMoreThanTwoLevelsOfPointerIndirection", + "tags": [ + "readability", + "maintainability", + "scope/single-translation-unit" + ] + } + ], + "title": "The declaration of an object should contain no more than two levels of pointer indirection" + }, + "RULE-18-3-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Handlers for a function-try-block of a constructor or destructor shall not refer to non-static members from their class or its bases.", + "kind": "problem", + "name": "Handlers for a function-try-block of a constructor or destructor shall not refer to non-static", + "precision": "very-high", + "severity": "error", + "short_name": "HandlersReferToNonStaticMembersFromTheirClass", + "shared_implementation_short_name": "DestroyedValueReferencedInDestructorCatchBlock", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "Handlers for a function-try-block of a constructor or destructor shall not refer to non-static members from their class or its bases" + }, + "RULE-19-0-3": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Using anything other than other pre-processor directives or comments before an '#include' directive makes the code more difficult to read.", + "kind": "problem", + "name": "#include directives should only be preceded by preprocessor directives or comments", + "precision": "very-high", + "severity": "error", + "short_name": "IncludeDirectivesPrecededByPreprocessorDirectives", + "shared_implementation_short_name": "PreprocessorIncludesPreceded", + "tags": [ + "readability", + "scope/single-translation-unit" + ] + } + ], + "title": "#include directives should only be preceded by preprocessor directives or comments" + }, + "RULE-19-1-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be defined prior to evaluation.", + "kind": "problem", + "name": "All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be", + "precision": "very-high", + "severity": "error", + "short_name": "IdentifiersUsedInTheControllingExpressionOf", + "shared_implementation_short_name": "UndefinedMacroIdentifiers", + "tags": [ + "correctness", + "readability", + "scope/single-translation-unit" + ] + } + ], + "title": "All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be defined prior to evaluation" + }, + "RULE-19-2-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The ' or \" or \\ characters and the /* or // character sequences shall not occur in a header file name.", + "kind": "problem", + "name": "The ' or \" or \\ characters and the /* or // character sequences shall not occur in a header file", + "precision": "very-high", + "severity": "error", + "short_name": "CharsThatShouldNotOccurInHeaderFileName", + "shared_implementation_short_name": "PreprocessorIncludesForbiddenHeaderNames", + "tags": [ + "scope/single-translation-unit", + "correctness" + ], + "implementation_scope": { + "description": "This query identifies the use of the ', \\, /*, // characters in header file names. The query is not able to detect the use of the \" character in header file names.", + "items": [] + } + } + ], + "title": "The ' or \" or \\ characters and the /* or // character sequences shall not occur in a header file name" + }, + "RULE-19-3-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "The order of evaluation for the '#' and '##' operators may differ between compilers, which can cause unexpected behaviour.", + "kind": "problem", + "name": "The # and ## preprocessor operators should not be used", + "precision": "very-high", + "severity": "error", + "short_name": "AndPreprocessorOperatorsShouldNotBeUsed", + "shared_implementation_short_name": "HashOperatorsUsed", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "The # and ## preprocessor operators should not be used" + }, + "RULE-19-3-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Arguments to a function-like macro shall not contain tokens that look like pre-processing directives or else behaviour after macro expansion is unpredictable.", + "kind": "problem", + "name": "Tokens that look like a preprocessing directive shall not occur within a macro argument", + "precision": "very-high", + "severity": "error", + "short_name": "TokensThatLookLikeDirectivesInAMacroArgument", + "shared_implementation_short_name": "PreprocessingDirectiveWithinMacroArgument", + "tags": [ + "readability", + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "Tokens that look like a preprocessing directive shall not occur within a macro argument" + }, + "RULE-21-6-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Do not delete pointers to incomplete classes to prevent undefined behavior.", + "kind": "problem", + "name": "A pointer to an incomplete class type shall not be deleted", + "precision": "very-high", + "severity": "error", + "short_name": "PointerToAnIncompleteClassTypeDeleted", + "shared_implementation_short_name": "DeleteOfPointerToIncompleteClass", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "A pointer to an incomplete class type shall not be deleted" + }, + "RULE-25-5-2": { + "properties": { + "enforcement": "decidable", + "obligation": "mandatory" + }, + "queries": [ + { + "description": "The pointers returned by the C++ Standard Library functions localeconv, getenv, setlocale or strerror must only be used as if they have pointer to const-qualified type.", + "kind": "path-problem", + "name": "The pointers returned by environment functions should be treated as const", + "precision": "very-high", + "severity": "error", + "short_name": "PointersReturnedByLocaleFunctionsMustBeUsedAsConst", + "shared_implementation_short_name": "ConstLikeReturnValue", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "The pointers returned by the C++ Standard Library functions localeconv, getenv, setlocale or strerror must only be used as if they have pointer to const-qualified type" + }, + "RULE-25-5-3": { + "properties": { + "enforcement": "undecidable", + "obligation": "mandatory" + }, + "queries": [ + { + "description": "The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror may be invalid following a subsequent call to the same function.", + "kind": "problem", + "name": "The pointer returned by the Standard Library env functions is invalid", + "precision": "very-high", + "severity": "error", + "short_name": "CallToSetlocaleInvalidatesOldPointersMisra", + "shared_implementation_short_name": "InvalidatedEnvStringPointers", + "tags": [ + "correctness", + "scope/system" + ] + }, + { + "description": "The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror may be invalid following a subsequent call to the same function.", + "kind": "problem", + "name": "The pointer returned by the Standard Library env functions is invalid warning", + "precision": "very-high", + "severity": "warning", + "short_name": "CallToSetlocaleInvalidatesOldPointersWarnMisra", + "shared_implementation_short_name": "InvalidatedEnvStringPointersWarn", + "tags": [ + "correctness", + "scope/system" + ] + } + ], + "title": "The pointer returned by the C++ Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror must not be used following a subsequent call to the same function" + }, + "RULE-28-6-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Moved-from object shall not be read-accessed.", + "kind": "problem", + "name": "An object shall not be used while in a potentially moved-from state", + "precision": "very-high", + "severity": "error", + "short_name": "ObjectUsedWhileInPotentiallyMovedFromState", + "shared_implementation_short_name": "MovedFromObjectsUnspecifiedState", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "An object shall not be used while in a potentially moved-from state" + }, + "RULE-30-0-2": { + "properties": { + "enforcement": "undecidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Alternate input and output operations on a file stream shall not be used without an intervening flush or positioning call.", + "kind": "problem", + "name": "Reads and writes on the same file stream shall be separated by a positioning operation", + "precision": "very-high", + "severity": "error", + "short_name": "ReadsAndWritesOnStreamNotSeparatedByPositioning", + "shared_implementation_short_name": "IOFstreamMissingPositioning", + "tags": [ + "correctness", + "scope/system" + ], + "implementation_scope": { + "description": "The rule is enforced in the context of a single function." + } + } + ], + "title": "Reads and writes on the same file stream shall be separated by a positioning operation" + }, + "RULE-8-19-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "The comma operator should not be used.", + "kind": "problem", + "name": "The comma operator should not be used", + "precision": "very-high", + "severity": "error", + "short_name": "CommaOperatorShouldNotBeUsed", + "shared_implementation_short_name": "CommaOperatorUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The comma operator should not be used" + }, + "DIR-15-8-1": { + "properties": { + "allocated-target": [ + "implementation" + ], + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "User-provided copy assignment operators and move assignment operators shall handle self-assignment.", + "kind": "problem", + "name": "User-provided copy assignment operators and move assignment operators shall handle self-assignment", + "precision": "very-high", + "severity": "error", + "short_name": "CopyAndMoveAssignmentsShallHandleSelfAssignment", + "shared_implementation_short_name": "CopyAndMoveAssignmentsShallHandleSelfAssignment", + "tags": [] + } + ], + "title": "User-provided copy assignment operators and move assignment operators shall handle self-assignment" + }, + "RULE-10-0-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "A declaration should not declare more than one variable or member variable.", + "kind": "problem", + "name": "Multiple declarations in the same local statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "UseSingleLocalDeclarators", + "shared_implementation_short_name": "MultipleLocalDeclarators", + "tags": [ + "readability", + "maintainability", + "scope/single-translation-unit" + ] + }, + { + "description": "A declaration should not declare more than one variable or member variable.", + "kind": "problem", + "name": "Multiple declarations in the same global or member declaration sequence", + "precision": "medium", + "severity": "recommendation", + "short_name": "UseSingleGlobalOrMemberDeclarators", + "shared_implementation_short_name": "MultipleGlobalOrMemberDeclarators", + "tags": [ + "readability", + "maintainability", + "scope/single-translation-unit" + ] + } + ], + "title": "A declaration should not declare more than one variable or member variable" + }, + "RULE-10-2-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "An enumeration shall be defined with an explicit underlying type.", + "kind": "problem", + "name": "An enumeration shall be defined with an explicit underlying type", + "precision": "very-high", + "severity": "error", + "short_name": "EnumerationNotDefinedWithAnExplicitUnderlyingType", + "shared_implementation_short_name": "EnumerationNotDefinedWithAnExplicitUnderlyingType", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An enumeration shall be defined with an explicit underlying type" + }, + "RULE-10-4-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The asm declaration shall not be used.", + "kind": "problem", + "name": "The asm declaration shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "AsmDeclarationShallNotBeUsed", + "shared_implementation_short_name": "AsmDeclarationUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The asm declaration shall not be used" + }, + "RULE-11-6-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique.", + "kind": "problem", + "name": "Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique", + "precision": "very-high", + "severity": "error", + "short_name": "NonUniqueEnumerationConstant", + "shared_implementation_short_name": "NonUniqueEnumerationConstant", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique" + }, + "RULE-12-2-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A bit-field shall have an appropriate type.", + "kind": "problem", + "name": "A bit-field shall have an appropriate type", + "precision": "very-high", + "severity": "error", + "short_name": "BitFieldShallHaveAnAppropriateType", + "shared_implementation_short_name": "BitFieldShallHaveAnAppropriateType", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A bit-field shall have an appropriate type" + }, + "RULE-12-2-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A named bit-field with signed integer type shall not have a length of one bit.", + "kind": "problem", + "name": "A named bit-field with signed integer type shall not have a length of one bit", + "precision": "very-high", + "severity": "error", + "short_name": "SignedIntegerNamedBitFieldHaveALengthOfOneBit", + "shared_implementation_short_name": "NamedBitFieldsWithSignedIntegerType", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A named bit-field with signed integer type shall not have a length of one bit" + }, + "RULE-13-1-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "An accessible base class shall not be both virtual and non-virtual in the same hierarchy.", + "kind": "problem", + "name": "An accessible base class shall not be both virtual and non-virtual in the same hierarchy", + "precision": "very-high", + "severity": "error", + "short_name": "VirtualAndNonVirtualClassInTheHierarchy", + "shared_implementation_short_name": "VirtualAndNonVirtualClassInTheHierarchy", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An accessible base class shall not be both virtual and non-virtual in the same hierarchy" + }, + "RULE-13-3-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Parameters in an overriding virtual function shall not specify different default arguments.", + "kind": "problem", + "name": "Parameters in an overriding virtual function shall not specify different default arguments", + "precision": "very-high", + "severity": "error", + "short_name": "OverridingShallSpecifyDifferentDefaultArguments", + "shared_implementation_short_name": "OverridingShallSpecifyDifferentDefaultArguments", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Parameters in an overriding virtual function shall not specify different default arguments" + }, + "RULE-13-3-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A comparison of a potentially virtual pointer to member function shall only be with nullptr.", + "kind": "problem", + "name": "A comparison of a potentially virtual pointer to member function shall only be with nullptr", + "precision": "very-high", + "severity": "error", + "short_name": "PotentiallyVirtualPointerOnlyComparesToNullptr", + "shared_implementation_short_name": "PotentiallyVirtualPointerOnlyComparesToNullptr", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A comparison of a potentially virtual pointer to member function shall only be with nullptr" + }, + "RULE-15-1-1": { + "properties": { + "enforcement": "undecidable", + "obligation": "required" + }, + "queries": [ + { + "description": "An object\u2019s dynamic type shall not be used from within its constructor or destructor.", + "kind": "problem", + "name": "An object\u2019s dynamic type shall not be used from within its constructor or destructor", + "precision": "very-high", + "severity": "error", + "short_name": "ObjectsDynamicTypeUsedFromConstructorOrDestructor", + "shared_implementation_short_name": "ObjectsDynamicTypeUsedFromConstructorOrDestructor", + "tags": [ + "scope/system" + ] + } + ], + "title": "An object\u2019s dynamic type shall not be used from within its constructor or destructor" + }, + "RULE-15-1-2": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "All constructors of a class should explicitly initialize all of its virtual base classes and immediate base classes.", + "kind": "problem", + "name": "All constructors of a class should explicitly initialize all of its virtual base classes and", + "precision": "very-high", + "severity": "error", + "short_name": "InitializeAllVirtualBaseClasses", + "shared_implementation_short_name": "InitializeAllVirtualBaseClasses", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "All constructors of a class should explicitly initialize all of its virtual base classes and immediate base classes" + }, + "RULE-15-1-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A class shall only define an initializer-list constructor when it is the only constructor.", + "kind": "problem", + "name": "A class shall only define an initializer-list constructor when it is the only constructor", + "precision": "very-high", + "severity": "error", + "short_name": "InitializerListConstructorIsTheOnlyConstructor", + "shared_implementation_short_name": "InitializerListConstructorIsTheOnlyConstructor", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A class shall only define an initializer-list constructor when it is the only constructor" + }, + "RULE-16-5-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The address-of operator shall not be overloaded.", + "kind": "problem", + "name": "The address-of operator shall not be overloaded", + "precision": "very-high", + "severity": "error", + "short_name": "AddressOfOperatorOverloaded", + "shared_implementation_short_name": "AddressOfOperatorOverloaded", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The address-of operator shall not be overloaded" + }, + "RULE-17-8-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Function templates shall not be explicitly specialized.", + "kind": "problem", + "name": "Function templates shall not be explicitly specialized", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionTemplatesExplicitlySpecialized", + "shared_implementation_short_name": "FunctionTemplatesExplicitlySpecialized", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Function templates shall not be explicitly specialized" + }, + "RULE-18-1-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "An exception object shall not have pointer type.", + "kind": "problem", + "name": "An exception object shall not have pointer type", + "precision": "very-high", + "severity": "error", + "short_name": "ExceptionObjectHavePointerType", + "shared_implementation_short_name": "ExceptionObjectHavePointerType", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An exception object shall not have pointer type" + }, + "RULE-18-1-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "An empty throw shall only occur within the compound-statement of a catch handler.", + "kind": "problem", + "name": "An empty throw shall only occur within the compound-statement of a catch handler", + "precision": "very-high", + "severity": "error", + "short_name": "EmptyThrowOnlyWithinACatchHandler", + "shared_implementation_short_name": "EmptyThrowOnlyWithinACatchHandler", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An empty throw shall only occur within the compound-statement of a catch handler" + }, + "RULE-18-5-1": { + "properties": { + "enforcement": "undecidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "A noexcept function should not attempt to propagate an exception to the calling function.", + "kind": "path-problem", + "name": "A noexcept function should not attempt to propagate an exception to the calling function", + "precision": "very-high", + "severity": "error", + "short_name": "NoexceptFunctionShouldNotPropagateToTheCaller", + "shared_implementation_short_name": "NoexceptFunctionShouldNotPropagateToTheCaller", + "tags": [ + "scope/system" + ] + } + ], + "title": "A noexcept function should not attempt to propagate an exception to the calling function" + }, + "RULE-19-0-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Function-like macros shall not be defined.", + "kind": "problem", + "name": "Function-like macros shall not be defined", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionLikeMacrosDefined", + "shared_implementation_short_name": "FunctionLikeMacrosDefined", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Function-like macros shall not be defined" + }, + "RULE-19-3-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A macro parameter immediately following a # operator shall not be immediately followed by a ## operator.", + "kind": "problem", + "name": "A macro parameter immediately following a # operator shall not be immediately followed by a ##", + "precision": "very-high", + "severity": "error", + "short_name": "MacroParameterFollowingHash", + "shared_implementation_short_name": "MacroParameterFollowingHash", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A macro parameter immediately following a # operator shall not be immediately followed by a ## operator" + }, + "RULE-19-3-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The argument to a mixed-use macro parameter shall not be subject to further expansion.", + "kind": "problem", + "name": "The argument to a mixed-use macro parameter shall not be subject to further expansion", + "precision": "very-high", + "severity": "error", + "short_name": "AMixedUseMacroArgumentSubjectToExpansion", + "shared_implementation_short_name": "AMixedUseMacroArgumentSubjectToExpansion", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The argument to a mixed-use macro parameter shall not be subject to further expansion" + }, + "RULE-21-10-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Signal handling contains implementation-defined and undefined behaviour.", + "kind": "problem", + "name": "The facilities provided by the standard header file shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "CsignalFacilitiesUsed", + "shared_implementation_short_name": "CsignalFunctionsUsed", + "tags": [ + "maintainability", + "correctness", + "scope/single-translation-unit" + ] + }, + { + "description": "The types provided by the standard header file shall not be used.", + "kind": "problem", + "name": "The signal-handling types of shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "CsignalTypesShallNotBeUsed", + "shared_implementation_short_name": "CsignalTypesUsed", + "tags": [ + "maintainability", + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "The facilities provided by the standard header file shall not be used" + }, + "RULE-21-2-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The library functions atof, atoi, atol and atoll from shall not be used.", + "kind": "problem", + "name": "The library functions atof, atoi, atol and atoll from shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "AtofAtoiAtolAndAtollUsed", + "shared_implementation_short_name": "AtofAtoiAtolAndAtollUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The library functions atof, atoi, atol and atoll from shall not be used" + }, + "RULE-21-2-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The macro offsetof shall not be used.", + "kind": "problem", + "name": "The macro offsetof shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "MacroOffsetofShallNotBeUsed", + "shared_implementation_short_name": "MacroOffsetofUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The macro offsetof shall not be used" + }, + "RULE-21-6-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "If a project defines the unsized version of a global operator delete, then the sized version shall be defined.", + "kind": "problem", + "name": "Sized 'operator delete' must be defined globally if unsized 'operator delete' is defined globally", + "precision": "very-high", + "severity": "error", + "short_name": "GlobalSizedOperatorDeleteShallBeDefined", + "shared_implementation_short_name": "GlobalSizedOperatorDeleteNotDefined", + "tags": [ + "maintainability", + "scope/system" + ] + }, + { + "description": "If a project defines the sized version of a global operator delete, then the unsized version shall be defined.", + "kind": "problem", + "name": "Unsized 'operator delete' must be defined globally if sized 'operator delete' is defined globally", + "precision": "very-high", + "severity": "error", + "short_name": "GlobalUnsizedOperatorDeleteShallBeDefined", + "shared_implementation_short_name": "GlobalUnsizedOperatorDeleteNotDefined", + "tags": [ + "maintainability", + "scope/system" + ] + } + ], + "title": "If a project defines either a sized or unsized version of a global operator delete, then both shall be defined" + }, + "RULE-26-3-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "std::vector should not be specialized with bool.", + "kind": "problem", + "name": "std::vector should not be specialized with bool", + "precision": "very-high", + "severity": "error", + "short_name": "VectorShouldNotBeSpecializedWithBool", + "shared_implementation_short_name": "VectorShouldNotBeSpecializedWithBool", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "std::vector should not be specialized with bool" + }, + "RULE-28-6-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Forwarding references and std::forward shall be used together.", + "kind": "problem", + "name": "Forwarding references and std::forward shall be used together", + "precision": "very-high", + "severity": "error", + "short_name": "ForwardingReferencesAndForwardNotUsedTogether", + "shared_implementation_short_name": "ForwardingReferencesAndForwardNotUsedTogether", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Forwarding references and std::forward shall be used together" + }, + "RULE-30-0-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The C Library input/output functions shall not be used.", + "kind": "problem", + "name": "The stream input/output library functions shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "CstdioFunctionsShallNotBeUsed", + "shared_implementation_short_name": "CstdioFunctionsUsed", + "tags": [ + "maintainability", + "correctness", + "scope/single-translation-unit" + ] + }, + { + "description": "The C Library input/output functions shall not be used.", + "kind": "problem", + "name": "The stream input/output library macros shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "CstdioMacrosShallNotBeUsed", + "shared_implementation_short_name": "CstdioMacrosUsed", + "tags": [ + "maintainability", + "correctness", + "scope/single-translation-unit" + ] + }, + { + "description": "The C Library input/output functions shall not be used.", + "kind": "problem", + "name": "The stream input/output library types shall not be used", + "precision": "very-high", + "severity": "warning", + "short_name": "CstdioTypesShallNotBeUsed", + "shared_implementation_short_name": "CstdioTypesUsed", + "tags": [ + "maintainability", + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "The C Library input/output functions shall not be used" + }, + "RULE-5-13-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "In character literals and non-raw string literals, \\ shall only be used to form a defined escape sequence or universal character name.", + "kind": "problem", + "name": "In character literals and non-raw string literals, \\ shall only be used to form a defined escape", + "precision": "very-high", + "severity": "error", + "short_name": "BackslashCharacterMisuse", + "shared_implementation_short_name": "BackslashCharacterMisuse", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "In character literals and non-raw string literals, \\ shall only be used to form a defined escape sequence or universal character name" + }, + "RULE-5-13-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Octal escape sequences, hexadecimal escape sequences, and universal character names shall be terminated.", + "kind": "problem", + "name": "Octal escape sequences, hexadecimal escape sequences, and universal character names shall be", + "precision": "very-high", + "severity": "error", + "short_name": "NonTerminatedEscapeSequences", + "shared_implementation_short_name": "NonTerminatedEscapeSequences", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Octal escape sequences, hexadecimal escape sequences, and universal character names shall be terminated" + }, + "RULE-5-13-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Octal constants shall not be used.", + "kind": "problem", + "name": "Octal constants shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "OctalConstantsUsed", + "shared_implementation_short_name": "UseOfNonZeroOctalLiteral", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Octal constants shall not be used" + }, + "RULE-5-13-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Unsigned integer literals shall be appropriately suffixed.", + "kind": "problem", + "name": "Unsigned integer literals shall be appropriately suffixed", + "precision": "very-high", + "severity": "error", + "short_name": "UnsignedIntegerLiteralsNotAppropriatelySuffixed", + "shared_implementation_short_name": "UnsignedIntegerLiteralsNotAppropriatelySuffixed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Unsigned integer literals shall be appropriately suffixed" + }, + "RULE-5-13-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The lowercase form of L shall not be used as the first character in a literal suffix.", + "kind": "problem", + "name": "The lowercase form of L shall not be used as the first character in a literal suffix", + "precision": "very-high", + "severity": "error", + "short_name": "LowercaseLStartsInLiteralSuffix", + "shared_implementation_short_name": "LowercaseLStartsInLiteralSuffix", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The lowercase form of L shall not be used as the first character in a literal suffix" + }, + "RULE-5-7-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The character sequence /* shall not be used within a C-style comment.", + "kind": "problem", + "name": "The character sequence /* shall not be used within a C-style comment", + "precision": "very-high", + "severity": "error", + "short_name": "CharacterSequenceUsedWithinACStyleComment", + "shared_implementation_short_name": "CharacterSequenceUsedWithinACStyleComment", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The character sequence /* shall not be used within a C-style comment" + }, + "RULE-5-7-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Line-splicing shall not be used in // comments.", + "kind": "problem", + "name": "Line-splicing shall not be used in // comments", + "precision": "very-high", + "severity": "error", + "short_name": "LineSplicingUsedInComments", + "shared_implementation_short_name": "LineSplicingUsedInComments", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Line-splicing shall not be used in // comments" + }, + "RULE-6-0-3": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "The only declarations in the global namespace should be main, namespace declarations and extern \"C\" declarations.", + "kind": "problem", + "name": "The only declarations in the global namespace should be main, namespace declarations and extern \"C\"", + "precision": "very-high", + "severity": "error", + "short_name": "GlobalNamespaceDeclarations", + "shared_implementation_short_name": "GlobalNamespaceDeclarations", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The only declarations in the global namespace should be main, namespace declarations and extern \"C\" declarations" + }, + "RULE-6-0-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "The identifier main shall not be used for a function other than the global function main.", + "kind": "problem", + "name": "The identifier main shall not be used for a function other than the global function main", + "precision": "very-high", + "severity": "error", + "short_name": "NonGlobalFunctionMain", + "shared_implementation_short_name": "NonGlobalFunctionMain", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The identifier main shall not be used for a function other than the global function main" + }, + "RULE-6-4-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A non-overriding member function definition that hides an inherited member function can result in unexpected behavior.", + "kind": "problem", + "name": "Member function hides inherited member function", + "precision": "very-high", + "severity": "error", + "short_name": "InheritedNonOverridableMemberFunction", + "shared_implementation_short_name": "HiddenInheritedNonOverridableMemberFunction", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + }, + { + "description": "An overriding member function definition thats hides an overload of the overridden inherited member function can result in unexpected behavior.", + "kind": "problem", + "name": "Member function hides inherited member function", + "precision": "very-high", + "severity": "error", + "short_name": "InheritedOverridableMemberFunction", + "shared_implementation_short_name": "HiddenInheritedOverridableMemberFunction", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + }, + { + "description": "A using declaration that makes a symbol available for unqualified lookup does not included definitions defined after the using declaration which can result in unexpected behavior.", + "kind": "problem", + "name": "Using declaration followed by new definition", + "precision": "very-high", + "severity": "error", + "short_name": "DefinitionShallBeConsideredForUnqualifiedLookup", + "shared_implementation_short_name": "DefinitionNotConsideredForUnqualifiedLookup", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "Derived classes shall not conceal functions that are inherited from their bases" + }, + "RULE-6-4-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Not using a qualified-id or `this->` syntax for identifiers used in a class template makes the code more difficult to understand.", + "kind": "problem", + "name": "In a class template with a dependent base, any name that may be found in that dependent base shall shall be referred to using a qualified-id or this->", + "precision": "very-high", + "severity": "warning", + "short_name": "NameShallBeReferredUsingAQualifiedIdOrThis", + "shared_implementation_short_name": "NameNotReferredUsingAQualifiedIdOrThis", + "tags": [ + "maintainability", + "readability", + "scope/single-translation-unit" + ] + }, + { + "description": "Not using a qualified-id or `this->` syntax for identifiers used in a class template makes the code more difficult to understand.", + "kind": "problem", + "name": "(Audit) In a class template with a dependent base, any name that may be found in that dependent base shall shall be referred to using a qualified-id or this->", + "precision": "very-high", + "severity": "warning", + "short_name": "NameShallBeReferredUsingAQualifiedIdOrThisAudit", + "shared_implementation_short_name": "NameNotReferredUsingAQualifiedIdOrThisAudit", + "tags": [ + "maintainability", + "readability", + "scope/single-translation-unit" + ] + } + ], + "title": "A name that is present in a dependent base shall not be resolved by unqualified lookup" + }, + "RULE-6-8-2": { + "properties": { + "enforcement": "decidable", + "obligation": "mandatory" + }, + "queries": [ + { + "description": "A function must not return a reference or a pointer to a local variable with automatic storage duration.", + "kind": "problem", + "name": "A function must not return a reference or a pointer to a local variable with automatic storage", + "precision": "very-high", + "severity": "error", + "short_name": "ReturnReferenceOrPointerToAutomaticLocalVariable", + "shared_implementation_short_name": "ReturnReferenceOrPointerToAutomaticLocalVariable", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A function must not return a reference or a pointer to a local variable with automatic storage duration" + }, + "RULE-7-11-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "nullptr shall be the only form of the null-pointer-constant.", + "kind": "problem", + "name": "nullptr shall be the only form of the null-pointer-constant", + "precision": "very-high", + "severity": "error", + "short_name": "NullptrNotTheOnlyFormOfTheNullPointerConstant", + "shared_implementation_short_name": "NullptrNotTheOnlyFormOfTheNullPointerConstant", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "nullptr shall be the only form of the null-pointer-constant" + }, + "RULE-7-11-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "An array passed as a function argument shall not decay to a pointer.", + "kind": "problem", + "name": "An array passed as a function argument shall not decay to a pointer", + "precision": "very-high", + "severity": "error", + "short_name": "ArrayPassedAsFunctionArgumentDecayToAPointer", + "shared_implementation_short_name": "ArrayPassedAsFunctionArgumentDecayToAPointer", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An array passed as a function argument shall not decay to a pointer" + }, + "RULE-8-18-2": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "The result of an assignment operator should not be used.", + "kind": "problem", + "name": "The result of an assignment operator should not be used", + "precision": "very-high", + "severity": "error", + "short_name": "ResultOfAnAssignmentOperatorShouldNotBeUsed", + "shared_implementation_short_name": "ResultOfAnAssignmentOperatorShouldNotBeUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The result of an assignment operator should not be used" + }, + "RULE-8-2-10": { + "properties": { + "enforcement": "undecidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Functions shall not call themselves, either directly or indirectly.", + "kind": "problem", + "name": "Functions shall not call themselves, either directly or indirectly", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionsCallThemselvesEitherDirectlyOrIndirectly", + "shared_implementation_short_name": "FunctionsCallThemselvesEitherDirectlyOrIndirectly", + "tags": [ + "scope/system" + ] + } + ], + "title": "Functions shall not call themselves, either directly or indirectly" + }, + "RULE-8-2-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Casts shall not be performed between a pointer to function and any other type.", + "kind": "problem", + "name": "Casts shall not be performed between a pointer to function and any other type", + "precision": "very-high", + "severity": "error", + "short_name": "CastsBetweenAPointerToFunctionAndAnyOtherType", + "shared_implementation_short_name": "CastsBetweenAPointerToFunctionAndAnyOtherType", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Casts shall not be performed between a pointer to function and any other type" + }, + "RULE-8-2-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "reinterpret_cast shall not be used.", + "kind": "problem", + "name": "reinterpret_cast shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "ReinterpretCastShallNotBeUsed", + "shared_implementation_short_name": "ReinterpretCastUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "reinterpret_cast shall not be used" + }, + "RULE-8-20-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "An unsigned arithmetic operation with constant operands should not wrap.", + "kind": "problem", + "name": "An unsigned arithmetic operation with constant operands should not wrap", + "precision": "very-high", + "severity": "error", + "short_name": "UnsignedOperationWithConstantOperandsWraps", + "shared_implementation_short_name": "UnsignedOperationWithConstantOperandsWraps", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An unsigned arithmetic operation with constant operands should not wrap" + }, + "RULE-8-3-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "The built-in unary - operator should not be applied to an expression of unsigned type.", + "kind": "problem", + "name": "The built-in unary - operator should not be applied to an expression of unsigned type", + "precision": "very-high", + "severity": "error", + "short_name": "BuiltInUnaryOperatorAppliedToUnsignedExpression", + "shared_implementation_short_name": "BuiltInUnaryOperatorAppliedToUnsignedExpression", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The built-in unary - operator should not be applied to an expression of unsigned type" + }, + "RULE-9-3-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "If the body of a switch is not enclosed in braces, then this can lead to incorrect execution, and hard for developers to maintain.", + "kind": "problem", + "name": "The statement forming the body of a switch shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SwitchBodyCompoundCondition", + "shared_implementation_short_name": "SwitchCompoundCondition", + "tags": [ + "maintainability", + "readability", + "scope/single-translation-unit" + ] + }, + { + "description": "If the body of a loop is not enclosed in braces, then this can lead to incorrect execution, and hard for developers to maintain.", + "kind": "problem", + "name": "The statement forming the body of a loop shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "LoopBodyCompoundCondition", + "shared_implementation_short_name": "LoopCompoundCondition", + "tags": [ + "maintainability", + "readability", + "scope/single-translation-unit" + ] + } + ], + "title": "The body of an iteration-statement or a selection-statement shall be a compound-statement" + }, + "RULE-9-6-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "The goto statement should not be used.", + "kind": "problem", + "name": "The goto statement should not be used", + "precision": "very-high", + "severity": "error", + "short_name": "GotoStatementShouldNotBeUsed", + "shared_implementation_short_name": "GotoStatementShouldNotBeUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The goto statement should not be used" + }, + "RULE-9-6-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "A goto statement shall reference a label in a surrounding block.", + "kind": "problem", + "name": "A goto statement shall reference a label in a surrounding block", + "precision": "very-high", + "severity": "error", + "short_name": "GotoReferenceALabelInSurroundingBlock", + "shared_implementation_short_name": "GotoReferenceALabelInSurroundingBlock", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A goto statement shall reference a label in a surrounding block" + } + } +} \ No newline at end of file diff --git a/rule_packages/cpp/Inheritance.json b/rule_packages/cpp/Inheritance.json index 55175e0013..fc4805fc21 100644 --- a/rule_packages/cpp/Inheritance.json +++ b/rule_packages/cpp/Inheritance.json @@ -145,6 +145,7 @@ "precision": "very-high", "severity": "warning", "short_name": "AccessibleBaseClassBothVirtualAndNonVirtualInHierarchy", + "shared_implementation_short_name": "VirtualAndNonVirtualClassInTheHierarchy", "tags": [] } ], @@ -187,6 +188,7 @@ "precision": "very-high", "severity": "error", "short_name": "DynamicTypeOfThisUsedFromConstructorOrDestructor", + "shared_implementation_short_name": "ObjectsDynamicTypeUsedFromConstructorOrDestructor", "tags": [] } ], @@ -227,7 +229,13 @@ "precision": "very-high", "severity": "error", "short_name": "DoNotInvokeVirtualFunctionsFromConstructorsOrDestructors", - "tags": [] + "tags": [ + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" + ] } ], "title": "Do not invoke virtual functions from constructors or destructors" @@ -244,7 +252,13 @@ "precision": "very-high", "severity": "error", "short_name": "DoNotSliceDerivedObjects", - "tags": [] + "tags": [ + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" + ] } ], "title": "Do not slice derived objects" @@ -261,7 +275,13 @@ "precision": "very-high", "severity": "warning", "short_name": "DoNotDeleteAPolymorphicObjectWithoutAVirtualDestructor", - "tags": [] + "tags": [ + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" + ] } ], "title": "Do not delete a polymorphic object without a virtual destructor" diff --git a/rule_packages/cpp/Initialization.json b/rule_packages/cpp/Initialization.json index 626ddb3184..e81160a273 100644 --- a/rule_packages/cpp/Initialization.json +++ b/rule_packages/cpp/Initialization.json @@ -16,6 +16,7 @@ "precision": "very-high", "severity": "warning", "short_name": "ExplicitConstructorBaseClassInitialization", + "shared_implementation_short_name": "InitializeAllVirtualBaseClasses", "tags": [ "maintainability", "correctness" @@ -304,6 +305,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "ConfusingUseOfInitializerListConstructors", + "shared_implementation_short_name": "InitializerListConstructorIsTheOnlyConstructor", "tags": [ "readability", "maintainability" @@ -328,6 +330,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "MultipleLocalDeclarators", + "shared_implementation_short_name": "MultipleLocalDeclarators", "tags": [ "readability", "maintainability" @@ -340,6 +343,7 @@ "precision": "medium", "severity": "recommendation", "short_name": "MultipleGlobalOrMemberDeclarators", + "shared_implementation_short_name": "MultipleGlobalOrMemberDeclarators", "tags": [ "readability", "maintainability" @@ -364,6 +368,7 @@ "precision": "high", "severity": "recommendation", "short_name": "UseInitBracesToMatchTypeStructure", + "shared_implementation_short_name": "UseInitializerBracesToMatchAggregateTypeStructure", "tags": [ "readability", "maintainability" @@ -412,7 +417,12 @@ "short_name": "CyclesDuringStaticObjectInit", "tags": [ "correctness", - "maintainability" + "maintainability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -432,7 +442,12 @@ "short_name": "BadlySeededRandomNumberGenerator", "tags": [ "security", - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -455,7 +470,12 @@ "correctness", "security", "maintainability", - "readability" + "readability", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Invariants.json b/rule_packages/cpp/Invariants.json index b473fb499d..215e4edff4 100644 --- a/rule_packages/cpp/Invariants.json +++ b/rule_packages/cpp/Invariants.json @@ -63,7 +63,12 @@ "shared_implementation_short_name": "OrderingPredicateMustBeStrictlyWeak", "short_name": "ProvideAValidOrderingPredicate", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -82,7 +87,12 @@ "severity": "error", "short_name": "SignalHandlerMustBeAPlainOldFunction", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -101,7 +111,12 @@ "severity": "error", "short_name": "HonorTerminationReplacementHandlerRequirements", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" ] }, { @@ -112,7 +127,12 @@ "severity": "error", "short_name": "HonorNewReplacementHandlerRequirements", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Iterators.json b/rule_packages/cpp/Iterators.json index a43740f7e7..c345adb371 100644 --- a/rule_packages/cpp/Iterators.json +++ b/rule_packages/cpp/Iterators.json @@ -61,7 +61,12 @@ "severity": "error", "short_name": "UsesValidContainerElementAccess", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -80,7 +85,12 @@ "severity": "error", "short_name": "UseValidIteratorRanges", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -99,7 +109,12 @@ "severity": "error", "short_name": "DoNotSubtractIteratorsForDifferentContainers", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -118,7 +133,12 @@ "severity": "error", "short_name": "DoNotUseAnAdditiveOperatorOnAnIterator", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -138,7 +158,12 @@ "severity": "error", "short_name": "UseValidReferencesForElementsOfString", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Lambdas.json b/rule_packages/cpp/Lambdas.json index ea43fa0231..8f973c361f 100644 --- a/rule_packages/cpp/Lambdas.json +++ b/rule_packages/cpp/Lambdas.json @@ -205,7 +205,12 @@ "shared_implementation_short_name": "DanglingCaptureWhenReturningLambdaObject", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -218,7 +223,12 @@ "shared_implementation_short_name": "DanglingCaptureWhenMovingLambdaObject", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Literals.json b/rule_packages/cpp/Literals.json index e762a9c411..7721b7dd6a 100644 --- a/rule_packages/cpp/Literals.json +++ b/rule_packages/cpp/Literals.json @@ -39,6 +39,7 @@ "precision": "very-high", "severity": "error", "short_name": "EscapeSequenceOutsideISO", + "shared_implementation_short_name": "BackslashCharacterMisuse", "tags": [ "correctness" ] @@ -85,6 +86,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "NullPointerConstantNotNullptr", + "shared_implementation_short_name": "NullptrNotTheOnlyFormOfTheNullPointerConstant", "tags": [ "readability" ] @@ -132,6 +134,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "UseOfNonZeroOctalLiteral", + "shared_implementation_short_name": "UseOfNonZeroOctalLiteral", "tags": [ "readability" ] @@ -166,6 +169,7 @@ "precision": "very-high", "severity": "warning", "short_name": "MissingUSuffix", + "shared_implementation_short_name": "UnsignedIntegerLiteralsNotAppropriatelySuffixed", "tags": [ "correctness", "readability" diff --git a/rule_packages/cpp/MoveForward.json b/rule_packages/cpp/MoveForward.json index 6278135d2c..6f071a6f53 100644 --- a/rule_packages/cpp/MoveForward.json +++ b/rule_packages/cpp/MoveForward.json @@ -40,6 +40,7 @@ "precision": "very-high", "severity": "error", "short_name": "ForwardingValuesToOtherFunctions", + "shared_implementation_short_name": "ForwardingReferencesAndForwardNotUsedTogether", "tags": [ "correctness" ] @@ -153,7 +154,12 @@ "short_name": "DoNotRelyOnTheValueOfAMovedFromObject", "shared_implementation_short_name": "MovedFromObjectsUnspecifiedState", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Naming.json b/rule_packages/cpp/Naming.json index e59f007975..34a9f2c66e 100644 --- a/rule_packages/cpp/Naming.json +++ b/rule_packages/cpp/Naming.json @@ -290,7 +290,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "DifferentIdentifiersNotTypographicallyUnambiguous", - "shared_implementation_short_name" : "DifferentIdentifiersNotTypographicallyUnambiguous", + "shared_implementation_short_name": "DifferentIdentifiersNotTypographicallyUnambiguous", "tags": [ "readability", "maintainability" @@ -314,6 +314,7 @@ "precision": "very-high", "severity": "warning", "short_name": "IdentifierMainUsedForAFunctionOtherThanTheGlobalFunctionMain", + "shared_implementation_short_name": "NonGlobalFunctionMain", "tags": [ "maintainability", "readability" @@ -381,7 +382,12 @@ "severity": "error", "short_name": "RedefiningOfStandardLibraryName", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -391,7 +397,12 @@ "severity": "error", "short_name": "ReuseOfReservedIdentifier", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -402,7 +413,12 @@ "short_name": "UseOfSingleUnderscoreReservedPrefix", "tags": [ "maintainability", - "readability" + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -413,7 +429,12 @@ "short_name": "UseOfDoubleUnderscoreReservedPrefix", "tags": [ "maintainability", - "readability" + "readability", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -425,7 +446,12 @@ "tags": [ "maintainability", "readability", - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -437,7 +463,12 @@ "tags": [ "maintainability", "readability", - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -449,7 +480,12 @@ "tags": [ "maintainability", "readability", - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -461,7 +497,12 @@ "tags": [ "maintainability", "readability", - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Null.json b/rule_packages/cpp/Null.json index 5e9cbcf4d5..543552660c 100644 --- a/rule_packages/cpp/Null.json +++ b/rule_packages/cpp/Null.json @@ -16,6 +16,7 @@ "precision": "medium", "severity": "error", "short_name": "NullPointersDereferenced", + "shared_implementation_short_name": "DereferenceOfNullPointer", "tags": [ "correctness" ] @@ -62,7 +63,12 @@ "severity": "error", "short_name": "DoNotAttemptToCreateAStringFromANullPointer", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], diff --git a/rule_packages/cpp/OperatorInvariants.json b/rule_packages/cpp/OperatorInvariants.json index b34df998e9..5eaefd68c8 100644 --- a/rule_packages/cpp/OperatorInvariants.json +++ b/rule_packages/cpp/OperatorInvariants.json @@ -39,6 +39,7 @@ "precision": "very-high", "severity": "error", "short_name": "CopyAssignmentAndAMoveHandleSelfAssignment", + "shared_implementation_short_name": "CopyAndMoveAssignmentsShallHandleSelfAssignment", "tags": [ "correctness" ] @@ -176,7 +177,12 @@ "severity": "error", "short_name": "GracefullyHandleSelfCopyAssignment", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -195,7 +201,12 @@ "severity": "error", "short_name": "CopyOperationsMustNotMutateTheSourceObject", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Operators.json b/rule_packages/cpp/Operators.json index 8bb2cb9d55..76be8a732a 100644 --- a/rule_packages/cpp/Operators.json +++ b/rule_packages/cpp/Operators.json @@ -297,6 +297,7 @@ "precision": "very-high", "severity": "error", "short_name": "UnaryMinusOperatorAppliedToAnExpressionWhoseUnderlyingTypeIsUnsigned", + "shared_implementation_short_name": "BuiltInUnaryOperatorAppliedToUnsignedExpression", "tags": [] } ], @@ -318,6 +319,7 @@ "precision": "very-high", "severity": "error", "short_name": "UnaryOperatorOverloaded", + "shared_implementation_short_name": "AddressOfOperatorOverloaded", "tags": [] } ], diff --git a/rule_packages/cpp/OrderOfEvaluation.json b/rule_packages/cpp/OrderOfEvaluation.json index c471ca8f48..00ec0dbc65 100644 --- a/rule_packages/cpp/OrderOfEvaluation.json +++ b/rule_packages/cpp/OrderOfEvaluation.json @@ -90,6 +90,18 @@ "external/autosar/audit", "readability" ] + }, + { + "description": "The use of parentheses can be used to emphasize precedence and increase code readability.", + "kind": "problem", + "name": "Limited dependence should be placed on C++ operator precedence rules in expressions", + "precision": "medium", + "severity": "recommendation", + "short_name": "InsufficientUseOfParentheses", + "tags": [ + "external/autosar/audit", + "readability" + ] } ], "title": "Limited dependence should be placed on C++ operator precedence rules in expressions." diff --git a/rule_packages/cpp/OutOfBounds.json b/rule_packages/cpp/OutOfBounds.json index a3cb8fbf91..2a657df95c 100644 --- a/rule_packages/cpp/OutOfBounds.json +++ b/rule_packages/cpp/OutOfBounds.json @@ -42,7 +42,12 @@ "shared_implementation_short_name": "ContainerAccessWithoutRangeCheck", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], @@ -62,7 +67,12 @@ "short_name": "GuaranteeGenericCppLibraryFunctionsDoNotOverflow", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" ] } ], @@ -82,7 +92,12 @@ "short_name": "RangeCheckStringElementAccess", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Pointers.json b/rule_packages/cpp/Pointers.json index 6a862e057c..fb1fbe2918 100644 --- a/rule_packages/cpp/Pointers.json +++ b/rule_packages/cpp/Pointers.json @@ -87,6 +87,7 @@ "precision": "very-high", "severity": "error", "short_name": "PointerToMemberVirtualFunctionWithNullPointerConstant", + "shared_implementation_short_name": "PotentiallyVirtualPointerOnlyComparesToNullptr", "tags": [ "correctness" ] @@ -279,6 +280,7 @@ "precision": "very-high", "severity": "warning", "short_name": "IdentifierWithArrayTypePassedAsFunctionArgumentDecayToAPointer", + "shared_implementation_short_name": "ArrayPassedAsFunctionArgumentDecayToAPointer", "tags": [ "correctness" ] @@ -325,6 +327,7 @@ "precision": "very-high", "severity": "error", "short_name": "CastNotConvertPointerToFunction", + "shared_implementation_short_name": "CastsBetweenAPointerToFunctionAndAnyOtherType", "tags": [ "correctness" ] @@ -393,7 +396,12 @@ "severity": "warning", "short_name": "DoNotUsePointerArithmeticOnPolymorphicObjects", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p9", + "external/cert/level/l2" ] } ], @@ -413,7 +421,12 @@ "short_name": "DeletingPointerToIncompleteClass", "shared_implementation_short_name": "DeleteOfPointerToIncompleteClass", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] }, { @@ -424,7 +437,12 @@ "severity": "error", "short_name": "CastOfPointerToIncompleteClass", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -444,7 +462,12 @@ "short_name": "UseOfPointerToMemberToAccessUndefinedMember", "shared_implementation_short_name": "AccessOfUndefinedMemberThroughNullPointer", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -456,7 +479,12 @@ "short_name": "MemberAccessWithUninitializedStaticPointerToMember", "shared_implementation_short_name": "AccessOfUndefinedMemberThroughUninitializedStaticPointer", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -468,7 +496,12 @@ "short_name": "UseOfPointerToMemberToAccessNonexistentMember", "shared_implementation_short_name": "AccessOfNonExistingMemberThroughPointerToMember", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Representation.json b/rule_packages/cpp/Representation.json index 4428966e87..813373afb4 100644 --- a/rule_packages/cpp/Representation.json +++ b/rule_packages/cpp/Representation.json @@ -53,6 +53,18 @@ "tags": [ "correctness" ] + }, + { + "description": "Passing a aliased pointers as parameters of certain functions is undefined behavior.", + "kind": "problem", + "name": "Do not pass aliased pointers as parameters of functions where it is undefined behaviour for those pointers to overlap", + "precision": "medium", + "severity": "error", + "short_name": "DoNotPassAliasedPointerToParam", + "shared_implementation_short_name": "DoNotPassAliasedPointerToRestrictQualifiedParamShared", + "tags": [ + "correctness" + ] } ], "title": "An object shall not be assigned to an overlapping object." @@ -96,6 +108,7 @@ "precision": "very-high", "severity": "error", "short_name": "NamedBitFieldsWithSignedIntegerTypeShallHaveALengthOfMoreThanOneBit", + "shared_implementation_short_name": "NamedBitFieldsWithSignedIntegerType", "tags": [ "correctness" ] @@ -118,7 +131,12 @@ "severity": "error", "short_name": "MemsetUsedToAccessObjectRepresentation", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -128,8 +146,14 @@ "precision": "very-high", "severity": "error", "short_name": "MemcmpUsedToAccessObjectRepresentation", + "shared_implementation_short_name": "MemcmpUsedToComparePaddingData", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] }, { @@ -140,7 +164,12 @@ "severity": "error", "short_name": "MemcpyUsedToAccessObjectRepresentation", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/high", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], diff --git a/rule_packages/cpp/Scope.json b/rule_packages/cpp/Scope.json index c169badcbe..6fc3aa8487 100644 --- a/rule_packages/cpp/Scope.json +++ b/rule_packages/cpp/Scope.json @@ -64,6 +64,7 @@ "precision": "very-high", "severity": "error", "short_name": "HiddenInheritedNonOverridableMemberFunction", + "shared_implementation_short_name": "HiddenInheritedNonOverridableMemberFunction", "tags": [ "correctness" ] @@ -75,6 +76,7 @@ "precision": "very-high", "severity": "error", "short_name": "HiddenInheritedOverridableMemberFunction", + "shared_implementation_short_name": "HiddenInheritedOverridableMemberFunction", "tags": [ "correctness" ] @@ -86,6 +88,7 @@ "precision": "very-high", "severity": "error", "short_name": "DefinitionNotConsideredForUnqualifiedLookup", + "shared_implementation_short_name": "DefinitionNotConsideredForUnqualifiedLookup", "tags": [ "correctness" ] @@ -156,6 +159,7 @@ "precision": "high", "severity": "error", "short_name": "IdentifierWithExternalLinkageShallHaveOneDefinition", + "shared_implementation_short_name": "IdentifierWithExternalLinkageOneDefinitionShared", "tags": [ "correctness" ] @@ -179,6 +183,7 @@ "precision": "very-high", "severity": "warning", "short_name": "MissingStaticSpecifierOnFunctionRedeclaration", + "shared_implementation_short_name": "MissingStaticSpecifierFunctionRedeclarationShared", "tags": [ "readability" ] @@ -202,6 +207,7 @@ "precision": "high", "severity": "warning", "short_name": "UnnecessaryExposedIdentifierDeclaration", + "shared_implementation_short_name": "UnnecessaryExposedIdentifierDeclarationShared", "tags": [ "correctness" ] @@ -225,6 +231,7 @@ "precision": "very-high", "severity": "warning", "short_name": "GlobalNamespaceMembershipViolation", + "shared_implementation_short_name": "GlobalNamespaceDeclarations", "tags": [ "readability" ] @@ -247,7 +254,12 @@ "severity": "warning", "short_name": "LocalFunctionDeclaration", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] }, { @@ -258,7 +270,12 @@ "severity": "warning", "short_name": "LocalConstructorInitializedObjectHidesIdentifier", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p2", + "external/cert/level/l3" ] } ], @@ -277,7 +294,12 @@ "severity": "error", "short_name": "SingularOverloadOfMemoryFunction", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/low", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -297,7 +319,12 @@ "short_name": "ModificationOfTheStandardNamespaces", "shared_implementation_short_name": "NonStandardEntitiesInStandardNamespaces", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], @@ -316,7 +343,12 @@ "severity": "error", "short_name": "UnnamedNamespaceInHeaderFile", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -336,7 +368,12 @@ "short_name": "OneDefinitionRuleNotObeyed", "shared_implementation_short_name": "OneDefinitionRuleViolation", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/SideEffects1.json b/rule_packages/cpp/SideEffects1.json index adddbc3b36..587a6ceb66 100644 --- a/rule_packages/cpp/SideEffects1.json +++ b/rule_packages/cpp/SideEffects1.json @@ -84,7 +84,12 @@ "severity": "warning", "short_name": "DoNotDependOnTheOrderOfScalarObjectEvaluationForSideEffects", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] }, { @@ -95,7 +100,12 @@ "severity": "warning", "short_name": "DoNotDependOnTheOrderOfEvaluationForSideEffectsInFunctionCallsAsFunctionArguments", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p8", + "external/cert/level/l2" ] } ], @@ -114,7 +124,12 @@ "severity": "warning", "short_name": "DoNotRelyOnSideEffectsInSizeOfOperand", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -125,7 +140,12 @@ "severity": "warning", "short_name": "DoNotRelyOnSideEffectsInTypeIdOperand", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -136,7 +156,12 @@ "severity": "warning", "short_name": "DoNotRelyOnSideEffectsInNoExceptOperand", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -147,7 +172,12 @@ "severity": "warning", "short_name": "DoNotRelyOnSideEffectsInDeclTypeOperand", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] }, { @@ -158,7 +188,12 @@ "severity": "warning", "short_name": "DoNotRelyOnSideEffectsInDeclValExpression", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/low", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/SideEffects2.json b/rule_packages/cpp/SideEffects2.json index 6e5e4812fb..0338b88895 100644 --- a/rule_packages/cpp/SideEffects2.json +++ b/rule_packages/cpp/SideEffects2.json @@ -165,7 +165,12 @@ "shared_implementation_short_name": "PredicateFunctionObjectsShouldNotBeMutable", "short_name": "PredicateFunctionObjectsShouldNotBeMutable", "tags": [ - "correctness" + "correctness", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/high", + "external/cert/priority/p3", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/SmartPointers2.json b/rule_packages/cpp/SmartPointers2.json index 2f89c4868a..db641b4c7e 100644 --- a/rule_packages/cpp/SmartPointers2.json +++ b/rule_packages/cpp/SmartPointers2.json @@ -16,7 +16,10 @@ "precision": "medium", "severity": "warning", "short_name": "WeakPtrNotUsedToRepresentTemporarySharedOwnership", - "tags": ["correctness", "external/autosar/audit"] + "tags": [ + "correctness", + "external/autosar/audit" + ] } ], "title": "A std::weak_ptr shall be used to represent temporary shared ownership." @@ -36,8 +39,14 @@ "severity": "error", "short_name": "OwnedPointerValueStoredInUnrelatedSmartPointerCert", "shared_implementation_short_name": "OwnedPointerValueStoredInUnrelatedSmartPointer", - - "tags": ["correctness"] + "tags": [ + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] } ], "title": "Do not store an already-owned pointer value in an unrelated smart pointer" diff --git a/rule_packages/cpp/Strings.json b/rule_packages/cpp/Strings.json index 2152684792..acccdc7753 100644 --- a/rule_packages/cpp/Strings.json +++ b/rule_packages/cpp/Strings.json @@ -133,7 +133,13 @@ "severity": "recommendation", "shared_implementation_short_name": "BasicStringMayNotBeNullTerminated", "short_name": "BasicStringMayNotBeNullTerminatedCert", - "tags": [] + "tags": [ + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] }, { "description": "Certain operations may not null terminate CStyle strings which may cause unpredictable behavior.", @@ -143,7 +149,13 @@ "severity": "recommendation", "shared_implementation_short_name": "OperationMayNotNullTerminateCStyleString", "short_name": "OperationMayNotNullTerminateCStyleStringCert", - "tags": [] + "tags": [ + "external/cert/severity/high", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p18", + "external/cert/level/l1" + ] } ], "title": "Guarantee that storage for strings has sufficient space for character data and the null terminator" diff --git a/rule_packages/cpp/Templates.json b/rule_packages/cpp/Templates.json index faf3c67155..a6520a7780 100644 --- a/rule_packages/cpp/Templates.json +++ b/rule_packages/cpp/Templates.json @@ -22,7 +22,7 @@ "readability" ] } - ], + ], "title": "A template should check if a specific template argument is suitable for this template." }, "A14-5-1": { @@ -112,6 +112,7 @@ "precision": "very-high", "severity": "warning", "short_name": "ExplicitSpecializationsOfFunctionTemplatesUsed", + "shared_implementation_short_name": "FunctionTemplatesExplicitlySpecialized", "tags": [ "maintainability", "readability" @@ -171,23 +172,25 @@ "precision": "very-high", "severity": "warning", "short_name": "NameNotReferredUsingAQualifiedIdOrThis", + "shared_implementation_short_name": "NameNotReferredUsingAQualifiedIdOrThis", "tags": [ "maintainability", "readability" ] }, { - "description": "Not using a qualified-id or `this->` syntax for identifiers used in a class template makes the code more difficult to understand.", - "kind": "problem", - "name": "(Audit) In a class template with a dependent base, any name that may be found in that dependent base shall shall be referred to using a qualified-id or this->", - "precision": "very-high", - "severity": "warning", - "short_name": "NameNotReferredUsingAQualifiedIdOrThisAudit", - "tags": [ - "maintainability", - "readability" - ] - } + "description": "Not using a qualified-id or `this->` syntax for identifiers used in a class template makes the code more difficult to understand.", + "kind": "problem", + "name": "(Audit) In a class template with a dependent base, any name that may be found in that dependent base shall shall be referred to using a qualified-id or this->", + "precision": "very-high", + "severity": "warning", + "short_name": "NameNotReferredUsingAQualifiedIdOrThisAudit", + "shared_implementation_short_name": "NameNotReferredUsingAQualifiedIdOrThisAudit", + "tags": [ + "maintainability", + "readability" + ] + } ], "title": "In a class template with a dependent base, any name that may be found in that dependent base shall be referred to using a qualified-id or this->." } diff --git a/rule_packages/cpp/TrustBoundaries.json b/rule_packages/cpp/TrustBoundaries.json index 7387fffc1f..0b697cd49c 100644 --- a/rule_packages/cpp/TrustBoundaries.json +++ b/rule_packages/cpp/TrustBoundaries.json @@ -38,7 +38,12 @@ "severity": "error", "short_name": "DoNotThrowAnExceptionAcrossExecutionBoundaries", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ] } ], @@ -57,7 +62,12 @@ "severity": "error", "short_name": "DoNotPassANonstandardObjectAcrossBoundaries", "tags": [ - "correctness" + "correctness", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ] } ], diff --git a/rule_packages/cpp/TypeRanges.json b/rule_packages/cpp/TypeRanges.json index e7f7b9425e..1e8ef914bf 100644 --- a/rule_packages/cpp/TypeRanges.json +++ b/rule_packages/cpp/TypeRanges.json @@ -16,6 +16,7 @@ "precision": "high", "severity": "error", "short_name": "UncheckedRangeDomainPoleErrors", + "shared_implementation_short_name": "UncheckedRangeDomainPoleErrors", "tags": [ "correctness" ] @@ -183,7 +184,12 @@ "short_name": "DetectErrorsWhenConvertingAStringToANumber", "shared_implementation_short_name": "StringNumberConversionMissingErrorCheck", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], @@ -202,7 +208,12 @@ "severity": "error", "short_name": "DoNotCastToAnOutOfRangeEnumerationValue", "tags": [ - "correctness" + "correctness", + "external/cert/severity/medium", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/medium", + "external/cert/priority/p4", + "external/cert/level/l3" ] } ], diff --git a/rule_packages/cpp/Uninitialized.json b/rule_packages/cpp/Uninitialized.json index 86a5b97115..1432e11603 100644 --- a/rule_packages/cpp/Uninitialized.json +++ b/rule_packages/cpp/Uninitialized.json @@ -39,9 +39,18 @@ "precision": "medium", "severity": "error", "short_name": "InformationLeakageAcrossTrustBoundaries", + "shared_implementation_short_name": "InformationLeakageAcrossBoundaries", "tags": [ - "security" - ] + "security", + "external/cert/severity/low", + "external/cert/likelihood/unlikely", + "external/cert/remediation-cost/high", + "external/cert/priority/p1", + "external/cert/level/l3" + ], + "implementation_scope": { + "description": "The rule does not detect cases where fields may have uninitialized padding but are initialized via an initializer." + } } ], "title": "Avoid information leakage when passing a class object across a trust boundary" @@ -61,7 +70,12 @@ "shared_implementation_short_name": "ReadOfUninitializedMemory", "tags": [ "correctness", - "security" + "security", + "external/cert/severity/high", + "external/cert/likelihood/probable", + "external/cert/remediation-cost/medium", + "external/cert/priority/p12", + "external/cert/level/l1" ] } ], diff --git a/rule_packages/cpp/VirtualFunctions.json b/rule_packages/cpp/VirtualFunctions.json index 198aba1bb7..eff4e15beb 100644 --- a/rule_packages/cpp/VirtualFunctions.json +++ b/rule_packages/cpp/VirtualFunctions.json @@ -177,6 +177,7 @@ "precision": "very-high", "severity": "warning", "short_name": "VirtualFunctionParametersUseTheSameDefaultArguments", + "shared_implementation_short_name": "OverridingShallSpecifyDifferentDefaultArguments", "tags": [ "correctness" ] diff --git a/rules.csv b/rules.csv old mode 100755 new mode 100644 index e05422db9e..68049625e6 --- a/rules.csv +++ b/rules.csv @@ -1,4 +1,4 @@ -Language,Standard,ID,Queryable?,Obligation level,Enforcement level,Allocated target,Description,Similar,Package,Difficulty,Justification for querability level +Language,Standard,ID,Supportable,Obligation level,Enforcement level,Allocated target,Description,Similar,Package,Difficulty,Justification for querability level cpp,AUTOSAR,A0-1-1,Yes,Required,Automated,Implementation,A project shall not contain instances of non-volatile variables being given values that are not subsequently used.,M0-1-6,DeadCode,Medium, cpp,AUTOSAR,A0-1-2,Yes,Required,Automated,Implementation,The value returned by a function having a non-void return type that is not an overloaded operator shall be used.,M0-1-7,DeadCode,Easy, cpp,AUTOSAR,A0-1-3,Yes,Required,Automated,Implementation,"Every function defined in an anonymous namespace, or static function with internal linkage, or private member function shall be used.",M0-1-10,DeadCode,Easy, @@ -14,7 +14,7 @@ cpp,AUTOSAR,A1-1-2,Yes,Required,Non-Automated,Implementation / Toolchain,A warni cpp,AUTOSAR,A1-1-3,Yes,Required,Non-Automated,Toolchain,An optimization option that disregards strict standard compliance shall not be turned on in the chosen compiler.,,Toolchain,Easy,Note: this is currently only possible for compilations that do not use response files. cpp,AUTOSAR,A1-2-1,No,Required,Non-Automated,Toolchain,"When using a compiler toolchain (including preprocessor, compiler itself, linker, C++ standard libraries) in safety-related software, the tool confidence level (TCL) shall be determined. In case of TCL2 or TCL3, the compiler shall undergo a 'Qualification of a software tool', as per ISO 26262-8.11.4.6 [6].",,,,Allocated target not covered by CodeQL cpp,AUTOSAR,A1-4-1,No,Required,Non-Automated,Implementation / Verification,Code metrics and their valid boundaries shall be defined and code shall comply with defined boundaries of code metrics.,,,,Allocated target not covered by CodeQL -cpp,AUTOSAR,A1-4-3,No,Advisory,Automated,Implementation,All code should compile free of compiler warnings.,,,,"This should be checked via the compiler output, rather than CodeQL, which adds unecessary steps." +cpp,AUTOSAR,A1-4-3,No,Advisory,Automated,Implementation,All code should compile free of compiler warnings.,,,,"This should be checked via the compiler output, rather than CodeQL, which adds unnecessary steps." cpp,AUTOSAR,A10-0-1,Yes,Required,Non-Automated,Design,Public inheritance shall be used to implement 'is-a' relationship.,,Inheritance,Audit,Report a list of Inheritance relationships. cpp,AUTOSAR,A10-0-2,Yes,Required,Non-Automated,Design,Membership or non-public inheritance shall be used to implement 'has-a' relationship.,,Inheritance,Audit,Report a list of membership relationships. cpp,AUTOSAR,A10-1-1,Yes,Required,Automated,Implementation,Class shall not be derived from more than one base class which is not an interface class.,,Inheritance,Easy, @@ -479,10 +479,10 @@ cpp,CERT-C++,STR50-CPP,Yes,Rule,,,Guarantee that storage for strings has suffici cpp,CERT-C++,STR51-CPP,Yes,Rule,,,Do not attempt to create a std::string from a null pointer,,Null,Hard, cpp,CERT-C++,STR52-CPP,Yes,Rule,,,"Use valid references, pointers, and iterators to reference elements of a basic_string",,Iterators,Hard, cpp,CERT-C++,STR53-CPP,Yes,Rule,,,Range check element access,,OutOfBounds,Hard, -c,CERT-C,ARR30-C,Yes,Rule,,,Do not form or use out-of-bounds pointers or array subscripts,,InvalidMemory,Medium, -c,CERT-C,ARR32-C,Yes,Rule,,,Ensure size arguments for variable length arrays are in a valid range,,InvalidMemory,Medium, -c,CERT-C,ARR36-C,Yes,Rule,,,Do not subtract or compare two pointers that do not refer to the same array,,Memory,Medium, -c,CERT-C,ARR37-C,Yes,Rule,,,Do not add or subtract an integer to a pointer to a non-array object,,InvalidMemory,Medium, +c,CERT-C,ARR30-C,Yes,Rule,,,Do not form or use out-of-bounds pointers or array subscripts,,OutOfBounds,Hard, +c,CERT-C,ARR32-C,Yes,Rule,,,Ensure size arguments for variable length arrays are in a valid range,,InvalidMemory2,Medium, +c,CERT-C,ARR36-C,Yes,Rule,,,Do not subtract or compare two pointers that do not refer to the same array,,Memory2,Medium, +c,CERT-C,ARR37-C,Yes,Rule,,,Do not add or subtract an integer to a pointer to a non-array object,,InvalidMemory2,Medium, c,CERT-C,ARR38-C,Yes,Rule,,,Guarantee that library functions do not form invalid pointers,,OutOfBounds,Very Hard, c,CERT-C,ARR39-C,Yes,Rule,,,Do not add or subtract a scaled integer to a pointer,,Pointers2,Medium, c,CERT-C,CON30-C,Yes,Rule,,,Clean up thread-specific storage,,Concurrency4,Very Hard, @@ -498,12 +498,12 @@ c,CERT-C,CON39-C,Yes,Rule,,,Do not join or detach a thread that was previously j c,CERT-C,CON40-C,Yes,Rule,,,Do not refer to an atomic variable twice in an expression,,Concurrency5,Medium, c,CERT-C,CON41-C,Yes,Rule,,,Wrap functions that can fail spuriously in a loop,CON53-CPP,Concurrency3,Medium, c,CERT-C,CON43-C,OutOfScope,Rule,,,Do not allow data races in multithreaded code,,,, -c,CERT-C,DCL30-C,Yes,Rule,,,Declare objects with appropriate storage durations,,Declarations,Hard, +c,CERT-C,DCL30-C,Yes,Rule,,,Declare objects with appropriate storage durations,,Declarations8,Hard, c,CERT-C,DCL31-C,Yes,Rule,,,Declare identifiers before using them,,Declarations1,Medium, -c,CERT-C,DCL36-C,Yes,Rule,,,Do not declare an identifier with conflicting linkage classifications,,Declarations,Medium, +c,CERT-C,DCL36-C,No,Rule,,,Do not declare an identifier with conflicting linkage classifications,,,,Compiler enforced in all supported compilers c,CERT-C,DCL37-C,Yes,Rule,,,Do not declare or define a reserved identifier,,Declarations1,Easy, c,CERT-C,DCL38-C,Yes,Rule,,,Use the correct syntax when declaring a flexible array member,,Declarations2,Easy, -c,CERT-C,DCL39-C,Yes,Rule,,,Avoid information leakage when passing a structure across a trust boundary,,Declarations,Hard, +c,CERT-C,DCL39-C,Yes,Rule,,,Avoid information leakage when passing a structure across a trust boundary,,Declarations7,Hard, c,CERT-C,DCL40-C,Yes,Rule,,,Do not create incompatible declarations of the same function or object,,Declarations2,Hard, c,CERT-C,DCL41-C,Yes,Rule,,,Do not declare variables inside a switch statement before the first case label,,Declarations2,Medium, c,CERT-C,ENV30-C,Yes,Rule,,,Do not modify the object referenced by the return value of certain functions,RULE-21-19,Contracts1,Medium, @@ -511,25 +511,28 @@ c,CERT-C,ENV31-C,Yes,Rule,,,Do not rely on an environment pointer following an o c,CERT-C,ENV32-C,Yes,Rule,,,All exit handlers must return normally,,Contracts2,Medium, c,CERT-C,ENV33-C,Yes,Rule,,,Do not call system(),"RULE-21-21, M18-0-3",Banned,Easy, c,CERT-C,ENV34-C,Yes,Rule,,,Do not store pointers returned by certain functions,RULE-21-20,Contracts2,Medium, -c,CERT-C,ERR30-C,Yes,Rule,,,"Set errno to zero before calling a library function known to set errno, and check errno only after the function returns a value indicating failure",M19-3-1,Contracts,Hard, -c,CERT-C,ERR32-C,Yes,Rule,,,Do not rely on indeterminate values of errno,,Contracts,Hard, -c,CERT-C,ERR33-C,Yes,Rule,,,Detect and handle standard library errors,MEM52-CPP,Contracts,Hard, +c,CERT-C,ERR30-C,Yes,Rule,,,Take care when reading errno,M19-3-1,Contracts4,Hard, +c,CERT-C,ERR32-C,Yes,Rule,,,Do not rely on indeterminate values of errno,,Contracts5,Hard, +c,CERT-C,ERR33-C,Yes,Rule,,,Detect and handle standard library errors,MEM52-CPP,Contracts5,Hard, c,CERT-C,ERR34-C,OutOfScope,Rule,,,Detect errors when converting a string to a number,,,, +c,CERT-C,EXP16-C,Yes,Recommendation,,,Do not compare function pointers to constant values,,Expressions2,Medium, c,CERT-C,EXP30-C,Yes,Rule,,,Do not depend on the order of evaluation for side effects,EXP50-CPP,SideEffects1,Easy, c,CERT-C,EXP32-C,Yes,Rule,,,Do not access a volatile object through a nonvolatile reference,,Pointers3,Easy, -c,CERT-C,EXP33-C,Yes,Rule,,,Do not read uninitialized memory,EXP53-CPP,InvalidMemory,Easy, -c,CERT-C,EXP34-C,Yes,Rule,,,Do not dereference null pointers,A5-3-2,InvalidMemory,Medium, -c,CERT-C,EXP35-C,Yes,Rule,,,Do not modify objects with temporary lifetime,,InvalidMemory,Hard, +c,CERT-C,EXP33-C,Yes,Rule,,,Do not read uninitialized memory,EXP53-CPP,InvalidMemory1,Import, +c,CERT-C,EXP34-C,Yes,Rule,,,Do not dereference null pointers,A5-3-2,InvalidMemory1,Import, +c,CERT-C,EXP35-C,Yes,Rule,,,Do not modify objects with temporary lifetime,,InvalidMemory2,Hard, c,CERT-C,EXP36-C,Yes,Rule,,,Do not cast pointers into more strictly aligned pointer types,,Pointers3,Medium, c,CERT-C,EXP37-C,Yes,Rule,,,Call functions with the correct number and type of arguments,,Expressions,Easy, c,CERT-C,EXP39-C,Yes,Rule,,,Do not access a variable through a pointer of an incompatible type,,Pointers3,Medium, -c,CERT-C,EXP40-C,Yes,Rule,,,Do not modify constant objects,,Contracts,Medium, -c,CERT-C,EXP42-C,Yes,Rule,,,Do not compare padding data,,Memory,Medium, +c,CERT-C,EXP40-C,Yes,Rule,,,Do not modify constant objects,,Contracts6,Hard, +c,CERT-C,EXP42-C,Yes,Rule,,,Do not compare padding data,,Memory2,Medium, c,CERT-C,EXP43-C,Yes,Rule,,,Avoid undefined behavior when using restrict-qualified pointers,,Pointers3,Medium, c,CERT-C,EXP44-C,Yes,Rule,,,"Do not rely on side effects in operands to sizeof, _Alignof, or _Generic",M5-3-4,SideEffects1,Medium, c,CERT-C,EXP45-C,Yes,Rule,,,Do not perform assignments in selection statements,M6-2-1,SideEffects1,Medium, c,CERT-C,EXP46-C,Yes,Rule,,,Do not use a bitwise operator with a Boolean-like operand,,Expressions,Easy, c,CERT-C,EXP47-C,OutOfScope,Rule,,,Do not call va_arg with an argument of the incorrect type,,,, +c,CERT-C,FIO03-C,Yes,Recommendation,,,Do not make assumptions about fopen() and file creation,,IO5,Hard, +c,CERT-C,FIO21-C,Yes,Recommendation,,,Do not create temporary files in shared directories,,IO5,Easy, c,CERT-C,FIO30-C,Yes,Rule,,,Exclude user input from format strings,A27-0-1,IO1,Import, c,CERT-C,FIO32-C,Yes,Rule,,,Do not perform operations on devices that are only appropriate for files,,IO3,Medium, c,CERT-C,FIO34-C,Yes,Rule,,,Distinguish between characters read from a file and EOF or WEOF,,IO1,Hard, @@ -543,33 +546,33 @@ c,CERT-C,FIO44-C,Yes,Rule,,,Only use values for fsetpos() that are returned from c,CERT-C,FIO45-C,Yes,Rule,,,Avoid TOCTOU race conditions while accessing files,,IO4,Medium, c,CERT-C,FIO46-C,Yes,Rule,,,Do not access a closed file,FIO51-CPP,IO1,Hard, c,CERT-C,FIO47-C,Yes,Rule,,,Use valid format strings,,IO4,Hard, -c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements,Easy, -c,CERT-C,FLP32-C,Yes,Rule,,,Prevent or detect domain and range errors in math functions,A0-4-4,Types,Medium, -c,CERT-C,FLP34-C,Yes,Rule,,,Ensure that floating-point conversions are within range of the new type,,Types,Medium, -c,CERT-C,FLP36-C,Yes,Rule,,,Preserve precision when converting integral values to floating-point type,,Types,Medium, -c,CERT-C,FLP37-C,Yes,Rule,,,Do not use object representations to compare floating-point values,,Types,Medium, -c,CERT-C,INT30-C,Yes,Rule,,,Ensure that unsigned integer operations do not wrap,A4-7-1,Types,Hard, -c,CERT-C,INT31-C,Yes,Rule,,,Ensure that integer conversions do not result in lost or misinterpreted data,A4-7-1,Types,Hard, -c,CERT-C,INT32-C,Yes,Rule,,,Ensure that operations on signed integers do not result in overflow,A4-7-1,Types,Hard, -c,CERT-C,INT33-C,Yes,Rule,,,Ensure that division and remainder operations do not result in divide-by-zero errors,,Types,Hard, -c,CERT-C,INT34-C,Yes,Rule,,,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,M5-8-1,Types,Import, -c,CERT-C,INT35-C,Yes,Rule,,,Use correct integer precisions,,Types,Hard, -c,CERT-C,INT36-C,Yes,Rule,,,Converting a pointer to integer or integer to pointer,M5-2-9,Types,Easy, -c,CERT-C,MEM30-C,Yes,Rule,,,Do not access freed memory,MEM50-CPP,InvalidMemory,Hard, -c,CERT-C,MEM31-C,Yes,Rule,,,Free dynamically allocated memory when no longer needed,,Memory,Very Hard, -c,CERT-C,MEM33-C,Yes,Rule,,,Allocate and copy structures containing a flexible array member dynamically,,Memory,Very Hard, -c,CERT-C,MEM34-C,Yes,Rule,,,Only free memory allocated dynamically,,Memory,Hard, -c,CERT-C,MEM35-C,Yes,Rule,,,Allocate sufficient memory for an object,,Memory,Very Hard, -c,CERT-C,MEM36-C,Yes,Rule,,,Do not modify the alignment of objects by calling realloc(),,Memory,Medium, +c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements4,Easy, +c,CERT-C,FLP32-C,Yes,Rule,,,Prevent or detect domain and range errors in math functions,A0-4-4,FloatingTypes,Medium, +c,CERT-C,FLP34-C,Yes,Rule,,,Ensure that floating-point conversions are within range of the new type,,FloatingTypes,Medium, +c,CERT-C,FLP36-C,Yes,Rule,,,Preserve precision when converting integral values to floating-point type,,FloatingTypes,Medium, +c,CERT-C,FLP37-C,Yes,Rule,,,Do not use object representations to compare floating-point values,,FloatingTypes,Medium, +c,CERT-C,INT30-C,Yes,Rule,,,Ensure that unsigned integer operations do not wrap,A4-7-1,IntegerOverflow,Hard, +c,CERT-C,INT31-C,Yes,Rule,,,Ensure that integer conversions do not result in lost or misinterpreted data,A4-7-1,IntegerOverflow,Hard, +c,CERT-C,INT32-C,Yes,Rule,,,Ensure that operations on signed integers do not result in overflow,A4-7-1,IntegerOverflow,Hard, +c,CERT-C,INT33-C,Yes,Rule,,,Ensure that division and remainder operations do not result in divide-by-zero errors,,IntegerOverflow,Hard, +c,CERT-C,INT34-C,Yes,Rule,,,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,M5-8-1,Types1,Import, +c,CERT-C,INT35-C,Yes,Rule,,,Use correct integer precisions,,IntegerOverflow,Hard, +c,CERT-C,INT36-C,Yes,Rule,,,Converting a pointer to integer or integer to pointer,M5-2-9,Types1,Easy, +c,CERT-C,MEM30-C,Yes,Rule,,,Do not access freed memory,MEM50-CPP,InvalidMemory1,Import, +c,CERT-C,MEM31-C,Yes,Rule,,,Free dynamically allocated memory when no longer needed,,Memory2,Very Hard, +c,CERT-C,MEM33-C,Yes,Rule,,,Allocate and copy structures containing a flexible array member dynamically,,Memory2,Very Hard, +c,CERT-C,MEM34-C,Yes,Rule,,,Only free memory allocated dynamically,,Memory2,Hard, +c,CERT-C,MEM35-C,Yes,Rule,,,Allocate sufficient memory for an object,,Memory3,Very Hard, +c,CERT-C,MEM36-C,Yes,Rule,,,Do not modify the alignment of objects by calling realloc(),,Memory2,Medium, c,CERT-C,MSC30-C,Yes,Rule,,,Do not use the rand() function for generating pseudorandom numbers,MSC50-CPP,Misc,Easy, c,CERT-C,MSC32-C,Yes,Rule,,,Properly seed pseudorandom number generators,MSC51-CPP,Misc,Easy, -c,CERT-C,MSC33-C,Yes,Rule,,,Do not pass invalid data to the asctime() function,,Contracts,Easy, +c,CERT-C,MSC33-C,Yes,Rule,,,Do not pass invalid data to the asctime() function,,Contracts7,Easy, c,CERT-C,MSC37-C,Yes,Rule,,,Ensure that control never reaches the end of a non-void function,,Misc,Easy, c,CERT-C,MSC38-C,Yes,Rule,,,Do not treat a predefined identifier as an object if it might only be implemented as a macro,M17-0-2,Preprocessor5,Medium, -c,CERT-C,MSC39-C,Yes,Rule,,,Do not call va_arg() on a va_list that has an indeterminate value,,Contracts,Hard, +c,CERT-C,MSC39-C,Yes,Rule,,,Do not call va_arg() on a va_list that has an indeterminate value,,Contracts7,Hard, c,CERT-C,MSC40-C,Yes,Rule,,,Do not violate constraints,,Contracts,Very Hard, c,CERT-C,MSC41-C,OutOfScope,Rule,,,Never hard code sensitive information,,,, -c,CERT-C,POS30-C,OutOfScope,Rule,,,Use the readlink() function properly,,,, +c,CERT-C,POS30-C,Yes,Rule,,,Use the readlink() function properly,,IO5,Hard, c,CERT-C,POS34-C,OutOfScope,Rule,,,Do not call putenv() with a pointer to an automatic variable as the argument,,,, c,CERT-C,POS35-C,OutOfScope,Rule,,,Avoid race conditions while checking for the existence of a symbolic link,,,, c,CERT-C,POS36-C,OutOfScope,Rule,,,Observe correct revocation order while relinquishing privileges,,,, @@ -585,13 +588,13 @@ c,CERT-C,POS51-C,OutOfScope,Rule,,,Avoid deadlock with POSIX threads by locking c,CERT-C,POS52-C,OutOfScope,Rule,,,Do not perform operations that can block while holding a POSIX lock,,,, c,CERT-C,POS53-C,OutOfScope,Rule,,,Do not use more than one mutex for concurrent waiting operations on a condition variable,,,, c,CERT-C,POS54-C,OutOfScope,Rule,,,Detect and handle POSIX library errors,,,, -c,CERT-C,PRE30-C,No,Rule,,,Do not create a universal character name through concatenation,,,Medium, -c,CERT-C,PRE31-C,Yes,Rule,,,Avoid side effects in arguments to unsafe macros,RULE-13-2,SideEffects,Medium, +c,CERT-C,PRE30-C,No,Rule,,,Do not create a universal character name through concatenation,,,Medium,Compiler enforced in all supported compilers +c,CERT-C,PRE31-C,Yes,Rule,,,Avoid side effects in arguments to unsafe macros,RULE-13-2,SideEffects4,Medium, c,CERT-C,PRE32-C,Yes,Rule,,,Do not use preprocessor directives in invocations of function-like macros,,Preprocessor5,Hard, -c,CERT-C,SIG30-C,Yes,Rule,,,Call only asynchronous-safe functions within signal handlers,,Contracts,Medium, -c,CERT-C,SIG31-C,Yes,Rule,,,Do not access shared objects in signal handlers,,Contracts,Medium, -c,CERT-C,SIG34-C,Yes,Rule,,,Do not call signal() from within interruptible signal handlers,,Contracts,Medium, -c,CERT-C,SIG35-C,Yes,Rule,,,Do not return from a computational exception signal handler,,Contracts,Easy, +c,CERT-C,SIG30-C,Yes,Rule,,,Call only asynchronous-safe functions within signal handlers,,SignalHandlers,Medium, +c,CERT-C,SIG31-C,Yes,Rule,,,Do not access shared objects in signal handlers,,SignalHandlers,Medium, +c,CERT-C,SIG34-C,Yes,Rule,,,Do not call signal() from within interruptible signal handlers,,SignalHandlers,Medium, +c,CERT-C,SIG35-C,Yes,Rule,,,Do not return from a computational exception signal handler,,SignalHandlers,Easy, c,CERT-C,STR30-C,Yes,Rule,,,Do not attempt to modify string literals,,Strings1,Medium, c,CERT-C,STR31-C,Yes,Rule,,,Guarantee that storage for strings has sufficient space for character data and the null terminator,STR50-CPP,Strings1,Very Hard, c,CERT-C,STR32-C,Yes,Rule,,,Do not pass a non-null-terminated character sequence to a library function that expects a string,STR51-CPP,Strings1,Very Hard, @@ -599,27 +602,32 @@ c,CERT-C,STR34-C,Yes,Rule,,,Cast characters to unsigned char before converting t c,CERT-C,STR37-C,Yes,Rule,,,Arguments to character-handling functions must be representable as an unsigned char,,Strings2,Medium, c,CERT-C,STR38-C,Yes,Rule,,,Do not confuse narrow and wide character strings and functions,,Strings3,Medium, c,CERT-C,WIN30-C,OutOfScope,Rule,,,Properly pair allocation and deallocation functions,DCL54-CPP,,Easy, -c,MISRA-C-2012,RULE-1-1,No,Required,,,Any implementation-defined behaviour on which the output of the program depends shall be documented and understood,,,, -c,MISRA-C-2012,RULE-2-1,Yes,Required,,,All source files shall compile without any compilation errors,A1-4-3,Language,Medium, -c,MISRA-C-2012,RULE-3-1,No,Required,,,All code shall be traceable to documented requirements,,,, -c,MISRA-C-2012,RULE-4-1,No,Required,,,Run-time failures shall be minimized,,,, -c,MISRA-C-2012,RULE-4-2,Yes,Advisory,,,All usage of assembly language should be documented,M7-4-1,Language,Import, +c,MISRA-C-2012,DIR-1-1,No,Required,,,Any implementation-defined behaviour on which the output of the program depends shall be documented and understood,,,, +c,MISRA-C-2012,DIR-2-1,No,Required,,,All source files shall compile without any compilation errors,A1-4-3,,Medium,"This should be checked via the compiler output, rather than CodeQL, which adds unnecessary steps." +c,MISRA-C-2012,DIR-3-1,No,Required,,,All code shall be traceable to documented requirements,,,, +c,MISRA-C-2012,DIR-4-1,No,Required,,,Run-time failures shall be minimized,,,, +c,MISRA-C-2012,DIR-4-2,Yes,Advisory,,,All usage of assembly language should be documented,M7-4-1,Language2,Import, c,MISRA-C-2012,DIR-4-3,Yes,Required,,,Assembly language shall be encapsulated and isolated,,Language1,Medium, -c,MISRA-C-2012,RULE-4-4,Yes,Advisory,,,Sections of code should not be commented out,A2-7-2,Syntax,Import, +c,MISRA-C-2012,DIR-4-4,Yes,Advisory,,,Sections of code should not be commented out,A2-7-2,Syntax,Import, c,MISRA-C-2012,DIR-4-5,Yes,Advisory,,,Identifiers in the same name space with overlapping visibility should be typographically unambiguous,M2-10-1,Syntax,Easy, -c,MISRA-C-2012,RULE-4-6,Yes,Advisory,,,typedefs that indicate size and signedness should be used in place of the basic numerical types,,Types,Hard, -c,MISRA-C-2012,RULE-4-7,Yes,Required,,,"If a function returns error information, then that error information shall be tested",M0-3-2,Contracts,Import, -c,MISRA-C-2012,RULE-4-8,Yes,Advisory,,,"If a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hidden",,Pointers1,Medium, -c,MISRA-C-2012,RULE-4-9,Yes,Advisory,,,A function should be used in preference to a function-like macro where they are interchangeable,,Preprocessor,Medium, -c,MISRA-C-2012,RULE-4-10,Yes,Required,,,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor2,Medium, -c,MISRA-C-2012,RULE-4-11,Yes,Required,,,The validity of values passed to library functions shall be checked,,Contracts,Hard, -c,MISRA-C-2012,RULE-4-12,Yes,Required,,,Dynamic memory allocation shall not be used,,Banned,Medium, -c,MISRA-C-2012,RULE-4-13,Yes,Advisory,,,Functions which are designed to provide operations on a resource should be called in an appropriate sequence,,Contracts,Hard, -c,MISRA-C-2012,RULE-4-14,Yes,Required,,,The validity of values received from external sources shall be checked,,Contracts,Hard, -c,MISRA-C-2012,RULE-1-1,Yes,Required,,,"The program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation�s translation limits",,Language,Easy, -c,MISRA-C-2012,RULE-1-2,Yes,Advisory,,,Language extensions should not be used,,Language,Easy, -c,MISRA-C-2012,RULE-1-3,Yes,Required,,,There shall be no occurrence of undefined or critical unspecified behaviour,,Language,Hard, -c,MISRA-C-2012,RULE-1-4,Yes,Required,,,Emergent language features shall not be used,,Language,Medium, +c,MISRA-C-2012,DIR-4-6,Yes,Advisory,,,typedefs that indicate size and signedness should be used in place of the basic numerical types,,Types1,Hard, +c,MISRA-C-2012,DIR-4-7,Yes,Required,,,"If a function returns error information, then that error information shall be tested",M0-3-2,Contracts,Import, +c,MISRA-C-2012,DIR-4-8,Yes,Advisory,,,"If a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hidden",,Pointers1,Medium, +c,MISRA-C-2012,DIR-4-9,Yes,Advisory,,,A function should be used in preference to a function-like macro where they are interchangeable,,Preprocessor6,Medium,Audit +c,MISRA-C-2012,DIR-4-10,Yes,Required,,,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor2,Medium, +c,MISRA-C-2012,DIR-4-11,Yes,Required,,,The validity of values passed to library functions shall be checked,,Contracts,Hard, +c,MISRA-C-2012,DIR-4-12,Yes,Required,,,Dynamic memory allocation shall not be used,,Banned,Medium, +c,MISRA-C-2012,DIR-4-13,No,Advisory,,,Functions which are designed to provide operations on a resource should be called in an appropriate sequence,,,,"Rule 22.1, 22.2 and 22.6 cover aspects of this rule. In other cases this is a design issue and needs to be checked manually." +c,MISRA-C-2012,DIR-4-14,Yes,Required,,,The validity of values received from external sources shall be checked,,Contracts8,Hard,This is supported by CodeQLs default C security queries. +c,MISRA-C-2012,DIR-4-15,Yes,Required,,,Evaluation of floating-point expressions shall not lead to the undetected generation of infinities and NaNs,FLP32-C and FLP04-C,FloatingTypes2,Medium, +c,MISRA-C-2012,DIR-5-1,Yes,Required,,,There shall be no data races between threads,CON43-C and CON32-C,Concurrency9,Very Hard, +c,MISRA-C-2012,DIR-5-2,Yes,Required,,,There shall be no deadlocks between threads,CON35-C,Concurrency6,Import, +c,MISRA-C-2012,DIR-5-3,Yes,Required,,,There shall be no dynamic thread creation,,Concurrency6,Easy, +c,MISRA-C-2012,RULE-1-1,No,Required,,,"The program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation's translation limits",,,Easy,"This should be checked via the compiler output, rather than CodeQL, which adds unnecessary steps." +c,MISRA-C-2012,RULE-1-2,Yes,Advisory,,,Language extensions should not be used,,Language3,Hard, +c,MISRA-C-2012,RULE-1-3,Yes,Required,,,There shall be no occurrence of undefined or critical unspecified behaviour,,Language3,Hard, +c,MISRA-C-2012,RULE-1-4,Yes,Required,,,Emergent language features shall not be used,,Language2,Medium, +c,MISRA-C-2012,RULE-1-5,Yes,Required,,,Obsolencent language features shall not be used,,Language4,Medium, c,MISRA-C-2012,RULE-2-1,Yes,Required,,,A project shall not contain unreachable code,M0-1-1,DeadCode,Import, c,MISRA-C-2012,RULE-2-2,Yes,Required,,,There shall be no dead code,M0-1-9,DeadCode,Import, c,MISRA-C-2012,RULE-2-3,Yes,Advisory,,,A project should not contain unused type declarations,A0-1-6,DeadCode,Import, @@ -627,52 +635,61 @@ c,MISRA-C-2012,RULE-2-4,Yes,Advisory,,,A project should not contain unused tag d c,MISRA-C-2012,RULE-2-5,Yes,Advisory,,,A project should not contain unused macro declarations,,DeadCode,Easy, c,MISRA-C-2012,RULE-2-6,Yes,Advisory,,,A function should not contain unused label declarations,,DeadCode,Easy, c,MISRA-C-2012,RULE-2-7,Yes,Advisory,,,There should be no unused parameters in functions,A0-1-4 A0-1-5,DeadCode,Easy, +c,MISRA-C-2012,RULE-2-8,Yes,Advisory,,,A project should not contain unused object definitions,Rules 2.3-2.7,DeadCode2,Medium, c,MISRA-C-2012,RULE-3-1,Yes,Required,,,The character sequences /* and // shall not be used within a comment,M2-7-1,Syntax,Easy, c,MISRA-C-2012,RULE-3-2,Yes,Required,,,Line-splicing shall not be used in // comments,,Syntax,Easy, c,MISRA-C-2012,RULE-4-1,Yes,Required,,,Octal and hexadecimal escape sequences shall be terminated,A2-13-1 M2-13-2,Syntax,Medium, c,MISRA-C-2012,RULE-4-2,No,Advisory,,,Trigraphs should not be used,A2-5-1,,Import, c,MISRA-C-2012,RULE-5-1,Yes,Required,,,External identifiers shall be distinct,,Declarations1,Medium, -c,MISRA-C-2012,RULE-5-2,Yes,Required,,,Identifiers declared in the same scope and name space shall be distinct,,Declarations,Medium, +c,MISRA-C-2012,RULE-5-2,Yes,Required,,,Identifiers declared in the same scope and name space shall be distinct,,Declarations5,Medium, c,MISRA-C-2012,RULE-5-3,Yes,Required,,,An identifier declared in an inner scope shall not hide an identifier declared in an outer scope,A2-10-1,Declarations3,Import, c,MISRA-C-2012,RULE-5-4,Yes,Required,,,Macro identifiers shall be distinct,,Declarations1,Easy, c,MISRA-C-2012,RULE-5-5,Yes,Required,,,Identifiers shall be distinct from macro names,,Declarations3,Easy, c,MISRA-C-2012,RULE-5-6,Yes,Required,,,A typedef name shall be a unique identifier,,Declarations3,Easy, c,MISRA-C-2012,RULE-5-7,Yes,Required,,,A tag name shall be a unique identifier,,Declarations3,Easy, -c,MISRA-C-2012,RULE-5-8,Yes,Required,,,Identifiers that define objects or functions with external linkage shall be unique,,Declarations,Easy, -c,MISRA-C-2012,RULE-5-9,Yes,Advisory,,,Identifiers that define objects or functions with internal linkage should be unique,,Declarations,Easy, -c,MISRA-C-2012,RULE-6-1,Yes,Required,,,Bit-fields shall only be declared with an appropriate type,M9-6-4,Types,Medium, -c,MISRA-C-2012,RULE-6-2,Yes,Required,,,Single-bit named bit fields shall not be of a signed type,M9-6-4,Types,Import, +c,MISRA-C-2012,RULE-5-8,Yes,Required,,,Identifiers that define objects or functions with external linkage shall be unique,,Declarations6,Easy, +c,MISRA-C-2012,RULE-5-9,Yes,Advisory,,,Identifiers that define objects or functions with internal linkage should be unique,,Declarations6,Easy, +c,MISRA-C-2012,RULE-6-1,Yes,Required,,,Bit-fields shall only be declared with an appropriate type,M9-6-4,BitfieldTypes,Medium, +c,MISRA-C-2012,RULE-6-2,Yes,Required,,,Single-bit named bit fields shall not be of a signed type,M9-6-4,BitfieldTypes,Import, +c,MISRA-C-2012,RULE-6-3,Yes,Required,,,A bit field shall not be declared as a member of a union,DCL39-C,BitfieldTypes2,Easy, c,MISRA-C-2012,RULE-7-1,Yes,Required,,,Octal constants shall not be used,M2-13-2,Banned,Import, -c,MISRA-C-2012,RULE-7-2,Yes,Required,,,A �u� or �U� suffix shall be applied to all integer constants that are represented in an unsigned type,M2-13-3,Syntax,Easy, -c,MISRA-C-2012,RULE-7-3,Yes,Required,,,The lowercase character �l� shall not be used in a literal suffix,M2-13-4,Syntax,Easy, -c,MISRA-C-2012,RULE-7-4,Yes,Required,,,A string literal shall not be assigned to an object unless the object�s type is �pointer to const-qualified char�,A2-13-4,Types,Easy, +c,MISRA-C-2012,RULE-7-2,Yes,Required,,,A 'u' or 'U' suffix shall be applied to all integer constants that are represented in an unsigned type,M2-13-3,Syntax,Easy, +c,MISRA-C-2012,RULE-7-3,Yes,Required,,,The lowercase character 'l' shall not be used in a literal suffix,M2-13-4,Syntax,Easy, +c,MISRA-C-2012,RULE-7-4,Yes,Required,,,A string literal shall not be assigned to an object unless the object's type is 'pointer to const-qualified char',A2-13-4,Types1,Easy, +c,MISRA-C-2012,RULE-7-5,Yes,Required,,,The argument of an integer constant macro shall have an appropriate form,,Types2,Medium, +c,MISRA-C-2012,RULE-7-6,Yes,Required,,,The small integer variants of the minimum-width integer constant macros shall not be used,,Types2,Easy, c,MISRA-C-2012,RULE-8-1,Yes,Required,,,Types shall be explicitly specified,,Declarations3,Medium, -c,MISRA-C-2012,RULE-8-2,Yes,Required,,,Function types shall be in prototype form with named parameters,,Declarations,Medium, -c,MISRA-C-2012,RULE-8-3,Yes,Required,,,All declarations of an object or function shall use the same names and type qualifiers,M3-2-1,Declarations,Medium, -c,MISRA-C-2012,RULE-8-4,Yes,Required,,,A compatible declaration shall be visible when an object or function with external linkage is defined,,Declarations,Medium, -c,MISRA-C-2012,RULE-8-5,Yes,Required,,,An external object or function shall be declared once in one and only one file,,Declarations,Medium, -c,MISRA-C-2012,RULE-8-6,Yes,Required,,,An identifier with external linkage shall have exactly one external definition,M3-2-4,Declarations,Import, -c,MISRA-C-2012,RULE-8-7,Yes,Advisory,,,Functions and objects should not be defined with external linkage if they are referenced in only one translation unit,,Declarations,Medium, -c,MISRA-C-2012,RULE-8-8,Yes,Required,,,The static storage class specifier shall be used in all declarations of objects and functions that have internal linkage,M3-3-2,Declarations,Medium, -c,MISRA-C-2012,RULE-8-9,Yes,Advisory,,,An object should be defined at block scope if its identifier only appears in a single function,M3-4-1,Declarations,Medium, -c,MISRA-C-2012,RULE-8-10,Yes,Required,,,An inline function shall be declared with the static storage class,,Declarations,Medium, -c,MISRA-C-2012,RULE-8-11,Yes,Advisory,,,"When an array with external linkage is declared, its size should be explicitly specified",,Declarations,Medium, -c,MISRA-C-2012,RULE-8-12,Yes,Required,,,"Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique",,Declarations,Medium, +c,MISRA-C-2012,RULE-8-2,Yes,Required,,,Function types shall be in prototype form with named parameters,,Declarations4,Medium, +c,MISRA-C-2012,RULE-8-3,Yes,Required,,,All declarations of an object or function shall use the same names and type qualifiers,M3-2-1,Declarations4,Medium, +c,MISRA-C-2012,RULE-8-4,Yes,Required,,,A compatible declaration shall be visible when an object or function with external linkage is defined,,Declarations4,Medium, +c,MISRA-C-2012,RULE-8-5,Yes,Required,,,An external object or function shall be declared once in one and only one file,,Declarations5,Medium, +c,MISRA-C-2012,RULE-8-6,Yes,Required,,,An identifier with external linkage shall have exactly one external definition,M3-2-4,Declarations4,Import, +c,MISRA-C-2012,RULE-8-7,Yes,Advisory,,,Functions and objects should not be defined with external linkage if they are referenced in only one translation unit,,Declarations6,Medium, +c,MISRA-C-2012,RULE-8-8,Yes,Required,,,The static storage class specifier shall be used in all declarations of objects and functions that have internal linkage,M3-3-2,Declarations5,Medium, +c,MISRA-C-2012,RULE-8-9,Yes,Advisory,,,An object should be defined at block scope if its identifier only appears in a single function,M3-4-1,Declarations5,Medium, +c,MISRA-C-2012,RULE-8-10,Yes,Required,,,An inline function shall be declared with the static storage class,,Declarations6,Medium, +c,MISRA-C-2012,RULE-8-11,Yes,Advisory,,,"When an array with external linkage is declared, its size should be explicitly specified",,Declarations6,Medium, +c,MISRA-C-2012,RULE-8-12,Yes,Required,,,"Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique",,Declarations7,Medium, c,MISRA-C-2012,RULE-8-13,Yes,Advisory,,,A pointer should point to a const-qualified type whenever possible,,Pointers1,Medium, c,MISRA-C-2012,RULE-8-14,Yes,Required,,,The restrict type qualifier shall not be used,,Banned,Easy, -c,MISRA-C-2012,RULE-9-1,Yes,Mandatory,,,The value of an object with automatic storage duration shall not be read before it has been set,,InvalidMemory,Medium, -c,MISRA-C-2012,RULE-9-2,Yes,Required,,,The initializer for an aggregate or union shall be enclosed in braces,,Memory,Easy, -c,MISRA-C-2012,RULE-9-3,Yes,Required,,,Arrays shall not be partially initialized,,Memory,Medium, -c,MISRA-C-2012,RULE-9-4,Yes,Required,,,An element of an object shall not be initialized more than once,,Memory,Medium, -c,MISRA-C-2012,RULE-9-5,Yes,Required,,,Where designated initializers are used to initialize an array object the size of the array shall be specified explicitly,,Memory,Medium, -c,MISRA-C-2012,RULE-10-1,Yes,Required,,,Operands shall not be of an inappropriate essential type,,Types,Hard, -c,MISRA-C-2012,RULE-10-2,Yes,Required,,,Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations,,Types,Medium, -c,MISRA-C-2012,RULE-10-3,Yes,Required,,,The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category,,Types,Hard, -c,MISRA-C-2012,RULE-10-4,Yes,Required,,,Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category,,Types,Medium, -c,MISRA-C-2012,RULE-10-5,Yes,Advisory,,,The value of an expression should not be cast to an inappropriate essential type,,Types,Medium, -c,MISRA-C-2012,RULE-10-6,Yes,Required,,,The value of a composite expression shall not be assigned to an object with wider essential type,,Types,Medium, -c,MISRA-C-2012,RULE-10-7,Yes,Required,,,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,,Types,Medium, -c,MISRA-C-2012,RULE-10-8,Yes,Required,,,The value of a composite expression shall not be cast to a different essential type category or a wider essential type,,Types,Medium, +c,MISRA-C-2012,RULE-8-15,Yes,Required,,,All declarations of an object with an explicit alignment specification shall specify the same alignment,,Alignment,Easy, +c,MISRA-C-2012,RULE-8-16,Yes,Advisory,,,The alignment specification of zero should not appear in an object declaration,,Alignment,Easy, +c,MISRA-C-2012,RULE-8-17,Yes,Advisory,,,At most one explicit alignment specifier should appear in an object declaration,,Alignment,Easy, +c,MISRA-C-2012,RULE-9-1,Yes,Mandatory,,,The value of an object with automatic storage duration shall not be read before it has been set,,InvalidMemory1,Import, +c,MISRA-C-2012,RULE-9-2,Yes,Required,,,The initializer for an aggregate or union shall be enclosed in braces,,Memory1,Easy, +c,MISRA-C-2012,RULE-9-3,Yes,Required,,,Arrays shall not be partially initialized,,Memory1,Medium, +c,MISRA-C-2012,RULE-9-4,Yes,Required,,,An element of an object shall not be initialized more than once,,Memory1,Medium, +c,MISRA-C-2012,RULE-9-5,No,Required,,,Where designated initializers are used to initialize an array object the size of the array shall be specified explicitly,,,Medium, +c,MISRA-C-2012,RULE-9-6,Yes,Required,,,An initializer using chained designators shall not contain initializers without designators,,Declarations10,Hard, +c,MISRA-C-2012,RULE-9-7,Yes,Mandatory,,,Atomic objects shall be appropriately initialized before being accessed,,Concurrency7,Hard, +c,MISRA-C-2012,RULE-10-1,Yes,Required,,,Operands shall not be of an inappropriate essential type,,EssentialTypes,Hard, +c,MISRA-C-2012,RULE-10-2,Yes,Required,,,Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations,,EssentialTypes,Medium, +c,MISRA-C-2012,RULE-10-3,Yes,Required,,,The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category,,EssentialTypes,Hard, +c,MISRA-C-2012,RULE-10-4,Yes,Required,,,Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category,,EssentialTypes,Medium, +c,MISRA-C-2012,RULE-10-5,Yes,Advisory,,,The value of an expression should not be cast to an inappropriate essential type,,EssentialTypes,Medium, +c,MISRA-C-2012,RULE-10-6,Yes,Required,,,The value of a composite expression shall not be assigned to an object with wider essential type,,EssentialTypes,Medium, +c,MISRA-C-2012,RULE-10-7,Yes,Required,,,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,,EssentialTypes,Medium, +c,MISRA-C-2012,RULE-10-8,Yes,Required,,,The value of a composite expression shall not be cast to a different essential type category or a wider essential type,,EssentialTypes,Medium, c,MISRA-C-2012,RULE-11-1,Yes,Required,,,Conversions shall not be performed between a pointer to a function and any other type,M5-2-6,Pointers1,Import, c,MISRA-C-2012,RULE-11-2,Yes,Required,,,Conversions shall not be performed between a pointer to an incomplete type and any other type,,Pointers1,Easy, c,MISRA-C-2012,RULE-11-3,Yes,Required,,,A cast shall not be performed between a pointer to object type and a pointer to a different object type,,Pointers1,Easy, @@ -682,52 +699,61 @@ c,MISRA-C-2012,RULE-11-6,Yes,Required,,,A cast shall not be performed between po c,MISRA-C-2012,RULE-11-7,Yes,Required,,,A cast shall not be performed between pointer to object and a non- integer arithmetic type,,Pointers1,Easy, c,MISRA-C-2012,RULE-11-8,Yes,Required,,,A cast shall not remove any const or volatile qualification from the type pointed to by a pointer,,Pointers1,Easy, c,MISRA-C-2012,RULE-11-9,Yes,Required,,,The macro NULL shall be the only permitted form of integer null pointer constant,,Pointers1,Easy, +c,MISRA-C-2012,RULE-11-10,Yes,Required,,,The _Atomic qualifier shall not be applied to the incomplete type void,,Declarations9,Easy, c,MISRA-C-2012,RULE-12-1,Yes,Advisory,,,The precedence of operators within expressions should be made explicit,,SideEffects1,Medium, -c,MISRA-C-2012,RULE-12-2,Yes,Required,,,The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand,,Contracts,Hard, +c,MISRA-C-2012,RULE-12-2,Yes,Required,,,The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand,,Contracts7,Medium, c,MISRA-C-2012,RULE-12-3,Yes,Advisory,,,The comma operator should not be used,M5-18-1,Banned,Import, -c,MISRA-C-2012,RULE-12-4,Yes,Advisory,,,Evaluation of constant expressions should not lead to unsigned integer wrap-around,INT30-C,Types,Easy, -c,MISRA-C-2012,RULE-12-5,Yes,Mandatory,,,The sizeof operator shall not have an operand which is a function parameter declared as �array of type�,,Types,Medium, +c,MISRA-C-2012,RULE-12-4,Yes,Advisory,,,Evaluation of constant expressions should not lead to unsigned integer wrap-around,INT30-C,IntegerOverflow,Easy, +c,MISRA-C-2012,RULE-12-5,Yes,Mandatory,,,The sizeof operator shall not have an operand which is a function parameter declared as 'array of type',,Types1,Medium, +c,MISRA-C-2012,RULE-12-6,Yes,Required,,,Structure and union members of atomic objects shall not be directly accessed,,Concurrency6,Easy, c,MISRA-C-2012,RULE-13-1,Yes,Required,,,Initializer lists shall not contain persistent side effects,,SideEffects1,Medium, -c,MISRA-C-2012,RULE-13-2,Yes,Required,,,The value of an expression and its persistent side effects shall be the same under all permitted evaluation orders,PRE31-C,SideEffects,Medium, +c,MISRA-C-2012,RULE-13-2,Yes,Required,,,The value of an expression and its persistent side effects shall be the same under all permitted evaluation orders,PRE31-C,SideEffects3,Medium, c,MISRA-C-2012,RULE-13-3,Yes,Advisory,,,A full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operator,,SideEffects2,Medium, c,MISRA-C-2012,RULE-13-4,Yes,Advisory,,,The result of an assignment operator should not be used,M6-2-1,SideEffects1,Easy, c,MISRA-C-2012,RULE-13-5,Yes,Required,,,The right hand operand of a logical && or || operator shall not contain persistent side effects,M5-14-1,SideEffects1,Import, -c,MISRA-C-2012,RULE-13-6,Yes,Mandatory,,,The operand of the sizeof operator shall not contain any expression which has potential side effects,M5-3-4,SideEffects1,Import, -c,MISRA-C-2012,RULE-14-1,Yes,Required,,,A loop counter shall not have essentially floating type,FLP30-C A6-5-2,Types,Hard, -c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements,Medium, -c,MISRA-C-2012,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements,Medium, -c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements,Medium, -c,MISRA-C-2012,RULE-15-1,No,Advisory,,,The goto statement should not be used,A6-6-1,,Import, -c,MISRA-C-2012,RULE-15-2,Yes,Required,,,The goto statement shall jump to a label declared later in the same function,M6-6-2,Statements,Import, -c,MISRA-C-2012,RULE-15-3,Yes,Required,,,"Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement",M6-6-1,Statements,Import, -c,MISRA-C-2012,RULE-15-4,Yes,Advisory,,,There should be no more than one break or goto statement used to terminate any iteration statement,,Statements,Medium, -c,MISRA-C-2012,RULE-15-5,Yes,Advisory,,,A function should have a single point of exit at the end,,Statements,Medium, -c,MISRA-C-2012,RULE-15-6,Yes,Required,,,The body of an iteration-statement or a selection-statement shall be a compund-statement,M6-3-1,Statements,Import, -c,MISRA-C-2012,RULE-15-7,Yes,Required,,,All if / else if constructs shall be terminated with an else statement,M6-4-2,Statements,Import, -c,MISRA-C-2012,RULE-16-1,Yes,Required,,,All switch statements shall be well-formed,M6-4-3,Statements,Import, -c,MISRA-C-2012,RULE-16-2,Yes,Required,,,A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement,M6-4-4,Statements,Import, -c,MISRA-C-2012,RULE-16-3,Yes,Required,,,An unconditional break statement shall terminate every switch-clause,M6-4-5,Statements,Import, -c,MISRA-C-2012,RULE-16-4,Yes,Required,,,Every switch statement shall have a default label,M6-4-6,Statements,Easy, -c,MISRA-C-2012,RULE-16-5,Yes,Required,,,A default label shall appear as either the first or the last switch label of a switch statement,M6-4-6,Statements,Easy, -c,MISRA-C-2012,RULE-16-6,Yes,Required,,,Every switch statement shall have at least two switch-clauses,A6-4-1,Statements,Medium, -c,MISRA-C-2012,RULE-16-7,Yes,Required,,,A switch-expression shall not have essentially Boolean type,M6-4-7,Statements,Medium, +c,MISRA-C-2012,RULE-13-6,Yes,Required,,,The operand of the sizeof operator shall not contain any expressiosn which has potential side effects,M5-3-4,SideEffects1,Import, +c,MISRA-C-2012,RULE-14-1,Yes,Required,,,A loop counter shall not have essentially floating type,FLP30-C A6-5-2,EssentialTypes,Hard, +c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements4,Medium, +c,MISRA-C-2012,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements5,Medium, +c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements4,Medium, +c,MISRA-C-2012,RULE-15-1,Yes,Advisory,,,The goto statement should not be used,A6-6-1,Statements6,Import, +c,MISRA-C-2012,RULE-15-2,Yes,Required,,,The goto statement shall jump to a label declared later in the same function,M6-6-2,Statements2,Import, +c,MISRA-C-2012,RULE-15-3,Yes,Required,,,"Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement",M6-6-1,Statements2,Import, +c,MISRA-C-2012,RULE-15-4,Yes,Advisory,,,There should be no more than one break or goto statement used to terminate any iteration statement,,Statements2,Medium, +c,MISRA-C-2012,RULE-15-5,Yes,Advisory,,,A function should have a single point of exit at the end,,Statements5,Medium, +c,MISRA-C-2012,RULE-15-6,Yes,Required,,,The body of an iteration-statement or a selection-statement shall be a compund-statement,M6-3-1,Statements3,Import, +c,MISRA-C-2012,RULE-15-7,Yes,Required,,,All if else if constructs shall be terminated with an else statement,M6-4-2,Statements3,Import, +c,MISRA-C-2012,RULE-16-1,Yes,Required,,,All switch statements shall be well-formed,M6-4-3,Statements3,Import, +c,MISRA-C-2012,RULE-16-2,Yes,Required,,,A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement,M6-4-4,Statements1,Import, +c,MISRA-C-2012,RULE-16-3,Yes,Required,,,An unconditional break statement shall terminate every switch-clause,M6-4-5,Statements1,Import, +c,MISRA-C-2012,RULE-16-4,Yes,Required,,,Every switch statement shall have a default label,M6-4-6,Statements1,Easy, +c,MISRA-C-2012,RULE-16-5,Yes,Required,,,A default label shall appear as either the first or the last switch label of a switch statement,M6-4-6,Statements1,Easy, +c,MISRA-C-2012,RULE-16-6,Yes,Required,,,Every switch statement shall have at least two switch-clauses,A6-4-1,Statements2,Medium, +c,MISRA-C-2012,RULE-16-7,Yes,Required,,,A switch-expression shall not have essentially Boolean type,M6-4-7,Statements2,Medium, c,MISRA-C-2012,RULE-17-1,Yes,Required,,,The features of shall not be used,,Banned,Easy, -c,MISRA-C-2012,RULE-17-2,Yes,Required,,,"Functions shall not call themselves, either directly or indirectly",A7-5-2,Statements,Import, -c,MISRA-C-2012,RULE-17-3,Yes,Mandatory,,,A function shall not be declared implicitly,,Declarations,Medium, -c,MISRA-C-2012,RULE-17-4,Yes,Mandatory,,,All exit paths from a function with non-void return type shall have an explicit return statement with an expression,MSC52-CPP,Statements,Medium, -c,MISRA-C-2012,RULE-17-5,Yes,Advisory,,,The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements,,Contracts,Hard, -c,MISRA-C-2012,RULE-17-6,No,Mandatory,,,The declaration of an array parameter shall not contain the static keyword between the [ ],,,, -c,MISRA-C-2012,RULE-17-7,Yes,Required,,,The value returned by a function having non-void return type shall be used,A0-1-2,Contracts,Import, +c,MISRA-C-2012,RULE-17-2,Yes,Required,,,"Functions shall not call themselves, either directly or indirectly",A7-5-2,Statements3,Import, +c,MISRA-C-2012,RULE-17-3,Yes,Mandatory,,,A function shall not be declared implicitly,,Declarations6,Medium, +c,MISRA-C-2012,RULE-17-4,Yes,Mandatory,,,All exit paths from a function with non-void return type shall have an explicit return statement with an expression,MSC52-CPP,Statements5,Medium, +c,MISRA-C-2012,RULE-17-5,Yes,Required,,,The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements,,Contracts6,Hard, +c,MISRA-C-2012,RULE-17-6,Yes,Mandatory,,,The declaration of an array parameter shall not contain the static keyword between the [ ],,Static,Easy, +c,MISRA-C-2012,RULE-17-7,Yes,Required,,,The value returned by a function having non-void return type shall be used,A0-1-2,Contracts6,Easy, c,MISRA-C-2012,RULE-17-8,Yes,Advisory,,,A function parameter should not be modified,,SideEffects2,Medium, +c,MISRA-C-2012,RULE-17-9,Yes,Mandatory,,,Verify that a function declared with _Noreturn does not return,,NoReturn,Easy, +c,MISRA-C-2012,RULE-17-10,Yes,Required,,,A function declared with _noreturn shall have a return type of void,,NoReturn,Easy, +c,MISRA-C-2012,RULE-17-11,Yes,Advisory,,,A function without a branch that returns shall be declared with _Noreturn,,NoReturn,Easy, +c,MISRA-C-2012,RULE-17-12,Yes,Advisory,,,A function identifier should only be called with a parenthesized parameter list or used with a & (address-of),,FunctionTypes,Easy, +c,MISRA-C-2012,RULE-17-13,No,Required,,,"A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)",,,Easy, c,MISRA-C-2012,RULE-18-1,Yes,Required,,,A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand,M5-0-16,Pointers1,Import, c,MISRA-C-2012,RULE-18-2,Yes,Required,,,Subtraction between pointers shall only be applied to pointers that address elements of the same array,M5-0-17,Pointers1,Import, c,MISRA-C-2012,RULE-18-3,Yes,Required,,,"The relational operators >, >=, < and <= shall not be applied to objects of pointer type except where they point into the same object",M5-0-18,Pointers1,Import, c,MISRA-C-2012,RULE-18-4,Yes,Advisory,,,"The +, -, += and -= operators should not be applied to an expression of pointer type",M5-0-15,Pointers1,Medium, c,MISRA-C-2012,RULE-18-5,Yes,Advisory,,,Declarations should contain no more than two levels of pointer nesting,A5-0-3,Pointers1,Import, c,MISRA-C-2012,RULE-18-6,Yes,Required,,,The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist,M7-5-2,Pointers1,Import, -c,MISRA-C-2012,RULE-18-7,Yes,Required,,,Flexible array members shall not be declared,,Declarations,Medium, -c,MISRA-C-2012,RULE-18-8,Yes,Required,,,Variable-length array types shall not be used,,Declarations,Medium, -c,MISRA-C-2012,RULE-19-1,Yes,Mandatory,,,An object shall not be assigned or copied to an overlapping object,M0-2-1,Contracts,Hard, +c,MISRA-C-2012,RULE-18-7,Yes,Required,,,Flexible array members shall not be declared,,Declarations6,Medium, +c,MISRA-C-2012,RULE-18-8,Yes,Required,,,Variable-length array types shall not be used,,Declarations7,Medium, +c,MISRA-C-2012,RULE-18-9,Yes,Required,,,An object with temporary lifetime shall not undergo array to pointer conversion,EXP35-C,InvalidMemory3,Hard, +c,MISRA-C-2012,RULE-18-10,Yes,Mandatory,,,Pointers to variably-modified array types shall not be used,,InvalidMemory3,Import, +c,MISRA-C-2012,RULE-19-1,Yes,Mandatory,,,An object shall not be assigned or copied to an overlapping object,M0-2-1,Contracts7,Hard, c,MISRA-C-2012,RULE-19-2,Yes,Advisory,,,The union keyword should not be used,A9-5-1,Banned,Import, c,MISRA-C-2012,RULE-20-1,Yes,Advisory,,,#include directives should only be preceded by preprocessor directives or comments,M16-0-1,Preprocessor1,Import, c,MISRA-C-2012,RULE-20-2,Yes,Required,,,"The ', "" or \ characters and the /* or // character sequences shall not occur in a header file name",A16-2-1,Preprocessor1,Import, @@ -753,19 +779,24 @@ c,MISRA-C-2012,RULE-21-7,Yes,Required,,,"The Standard Library functions atof, at c,MISRA-C-2012,RULE-21-8,Yes,Required,,,The Standard Library termination functions of shall not be used,ERR50-CPP,Banned,Easy, c,MISRA-C-2012,RULE-21-9,Yes,Required,,,The Standard Library functions bsearch and qsort of shall not be used,,Banned,Easy, c,MISRA-C-2012,RULE-21-10,Yes,Required,,,The Standard Library time and date functions shall not be used,,Banned,Easy, -c,MISRA-C-2012,RULE-21-11,Yes,Required,,,The standard header file shall not be used,,Banned,Easy, +c,MISRA-C-2012,RULE-21-11,Yes,Advisory,,,The standard header file shall not be used,,Banned,Easy, c,MISRA-C-2012,RULE-21-12,Yes,Advisory,,,The exception handling features of should not be used,,Banned,Easy, -c,MISRA-C-2012,RULE-21-13,Yes,Mandatory,,,Any value passed to a function in shall be representable as an unsigned char or be the value EOF,,Types,Medium, -c,MISRA-C-2012,RULE-21-14,Yes,Required,,,The Standard Library function memcmp shall not be used to compare null terminated strings,,Types,Hard, -c,MISRA-C-2012,RULE-21-15,Yes,Required,,,"The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible types",,Types,Medium, -c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type",,Types,Medium, -c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,Memory,Hard, +c,MISRA-C-2012,RULE-21-13,Yes,Mandatory,,,Any value passed to a function in shall be representable as an unsigned char or be the value EOF,,StandardLibraryFunctionTypes,Medium, +c,MISRA-C-2012,RULE-21-14,Yes,Required,,,The Standard Library function memcmp shall not be used to compare null terminated strings,,EssentialTypes,Hard, +c,MISRA-C-2012,RULE-21-15,Yes,Required,,,"The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible types",,StandardLibraryFunctionTypes,Medium, +c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type",,EssentialTypes,Medium, +c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,OutOfBounds,Hard, c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,OutOfBounds,Hard, c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts2,Medium, -c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV34-C",Contracts2,Import, +c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function",ENV34-C,Contracts2,Import, c,MISRA-C-2012,RULE-21-21,Yes,Required,,,The Standard Library function system of shall not be used,ENV33-C,Banned,Import, -c,MISRA-C-2012,RULE-22-1,Yes,Required,,,All resources obtained dynamically by means of Standard Library functions shall be explicitly released,,Memory,Hard, -c,MISRA-C-2012,RULE-22-2,Yes,Mandatory,,,A block of memory shall only be freed if it was allocated by means of a Standard Library function,,Memory,Hard, +c,MISRA-C-2012,RULE-21-22,Yes,Mandatory,,,All operand arguments to any type-generic macros in shall have an appropriate essential type,EXP37-C,EssentialTypes2,Hard, +c,MISRA-C-2012,RULE-21-23,Yes,Required,,,All operand arguments to any multi-argument type-generic macros in shall have the same standard type,Rule-21-22,EssentialTypes2,Easy, +c,MISRA-C-2012,RULE-21-24,Yes,Required,,,The random number generator functions of shall not be used,MSC30-C,Banned2,Easy, +c,MISRA-C-2012,RULE-21-25,Yes,Required,,,All memory synchronization operations shall be executed in sequentially consistent order,,Concurrency6,Medium, +c,MISRA-C-2012,RULE-21-26,Yes,Required,,,The Standard Library function mtx_timedlock() shall only be invoked on mutex objects of appropriate mutex type,,Concurrency7,Hard, +c,MISRA-C-2012,RULE-22-1,Yes,Required,,,All resources obtained dynamically by means of Standard Library functions shall be explicitly released,,Memory2,Hard, +c,MISRA-C-2012,RULE-22-2,Yes,Mandatory,,,A block of memory shall only be freed if it was allocated by means of a Standard Library function,,Memory2,Hard, c,MISRA-C-2012,RULE-22-3,Yes,Required,,,The same file shall not be open for read and write access at the same time on different streams,,IO3,Hard, c,MISRA-C-2012,RULE-22-4,Yes,Mandatory,,,There shall be no attempt to write to a stream which has been opened as read-only,,IO3,Medium, c,MISRA-C-2012,RULE-22-5,Yes,Mandatory,,,A pointer to a FILE object shall not be dereferenced,,IO3,Medium, @@ -774,3 +805,200 @@ c,MISRA-C-2012,RULE-22-7,Yes,Required,,,The macro EOF shall only be compared wit c,MISRA-C-2012,RULE-22-8,Yes,Required,,,The value of errno shall be set to zero prior to a call to an errno-setting-function,ERR30-C,Contracts3,Medium, c,MISRA-C-2012,RULE-22-9,Yes,Required,,,The value of errno shall be tested against zero after calling an errno-setting-function,,Contracts3,Medium, c,MISRA-C-2012,RULE-22-10,Yes,Required,,,The value of errno shall only be tested when the last function to be called was an errno-setting-function,,Contracts3,Medium, +c,MISRA-C-2012,RULE-22-11,Yes,Required,,,A thread that was previously either joined or detached shall not be subsequently joined nor detached,CON39-C,Concurrency6,Import, +c,MISRA-C-2012,RULE-22-12,Yes,Mandatory,,,"Thread objects, thread synchronization objects, and thread-specific storage pointers shall only be accessed by the appropriate Standard Library functions",,Concurrency8,Medium, +c,MISRA-C-2012,RULE-22-13,Yes,Required,,,"Thread objects, thread synchronization objects, and thread specific storage pointers shall have appropriate storage duration",EXP54-CPP and CON34-C,Concurrency8,Medium, +c,MISRA-C-2012,RULE-22-14,Yes,Mandatory,,,Thread synchronization objects shall be initialized before being accessed,EXP53-CPP,Concurrency8,Hard, +c,MISRA-C-2012,RULE-22-15,Yes,Required,,,Thread synchronization objects and thread-specific storage pointers shall not be destroyed until after all threads accessing them have terminated,,Concurrency9,Hard, +c,MISRA-C-2012,RULE-22-16,Yes,Required,,,All mutex objects locked by a thread shall be explicitly unlocked by the same thread,MEM51-CPP,Concurrency8,Hard, +c,MISRA-C-2012,RULE-22-17,Yes,Required,,,No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked before,Rule 22.2,Concurrency9,Medium, +c,MISRA-C-2012,RULE-22-18,Yes,Required,,,Non-recursive mutexes shall not be recursively locked,CON56-CPP,Concurrency9,Medium, +c,MISRA-C-2012,RULE-22-19,Yes,Required,,,A condition variable shall be associated with at most one mutex object,,Concurrency9,Medium, +c,MISRA-C-2012,RULE-22-20,Yes,Mandatory,,,Thread-specific storage pointers shall be created before being accessed,,Concurrency9,Hard, +c,MISRA-C-2012,RULE-23-1,Yes,Advisory,,,A generic selection should only be expanded from a macro,,Generics,Medium, +c,MISRA-C-2012,RULE-23-2,Yes,Required,,,A generic selection that is not expanded from a macro shall not contain potential side effects in the controlling expression,,Generics,Hard, +c,MISRA-C-2012,RULE-23-3,Yes,Advisory,,,A generic selection should contain at least one non-default association,,Generics,Easy, +c,MISRA-C-2012,RULE-23-4,Yes,Required,,,A generic association shall list an appropriate type,,Generics,Medium, +c,MISRA-C-2012,RULE-23-5,Yes,Advisory,,,A generic selection should not depend on implicit pointer type conversion,,Generics,Medium, +c,MISRA-C-2012,RULE-23-6,Yes,Required,,,The controlling expression of a generic selection shall have an essential type that matches its standard type,,Generics,Medium, +c,MISRA-C-2012,RULE-23-7,Yes,Advisory,,,A generic selection that is expanded from a macro should evaluate its argument only once,,Generics,Medium, +c,MISRA-C-2012,RULE-23-8,Yes,Required,,,A default association shall appear as either the first or the last association of a generic selection,,Generics,Easy, +cpp,MISRA-C++-2023,RULE-0-0-1,Yes,Required,Decidable,Single Translation Unit,A function shall not contain unreachable statements,M0-1-1,DeadCode2,Medium, +cpp,MISRA-C++-2023,RULE-0-0-2,Yes,Advisory,Undecidable,System,Controlling expressions should not be invariant,M0-1-2,DeadCode2,Easy, +cpp,MISRA-C++-2023,RULE-0-1-1,Yes,Advisory,Undecidable,System,A value should not be unnecessarily written to a local object,A0-1-1,DeadCode2,Medium, +cpp,MISRA-C++-2023,RULE-0-1-2,Yes,Required,Decidable,Single Translation Unit,The value returned by a function shall be used,A0-1-2,DeadCode2,Easy, +cpp,MISRA-C++-2023,RULE-0-2-1,Yes,Advisory,Decidable,Single Translation Unit,Variables with limited visibility should be used at least once,M0-1-3,DeadCode2,Easy, +cpp,MISRA-C++-2023,RULE-0-2-2,Yes,Required,Decidable,Single Translation Unit,A named function parameter shall be used at least once,"A0-1-4, A0-1-5",DeadCode2,Easy, +cpp,MISRA-C++-2023,RULE-0-2-3,Yes,Advisory,Decidable,Single Translation Unit,Types with limited visibility should be used at least once,A0-1-6,DeadCode2,Easy, +cpp,MISRA-C++-2023,RULE-0-2-4,Yes,Advisory,Decidable,System,Functions with limited visibility should be used at least once,A0-1-3,DeadCode2,Easy, +cpp,MISRA-C++-2023,DIR-0-3-1,Yes,Advisory,,,Floating-point arithmetic should be used appropriately,,FloatingPoint,Hard, +cpp,MISRA-C++-2023,DIR-0-3-2,Yes,Required,,,A function call shall not violate the function’s preconditions,,Preconditions,Hard, +cpp,MISRA-C++-2023,RULE-4-1-1,Yes,Required,Undecidable,System,A program shall conform to ISO/IEC 14882:2017 (C++17),,Toolchain2,Hard, +cpp,MISRA-C++-2023,RULE-4-1-2,Yes,Advisory,Decidable,Single Translation Unit,Deprecated features should not be used,,Toolchain2,Very Hard, +cpp,MISRA-C++-2023,RULE-4-1-3,Yes,Required,Undecidable,System,There shall be no occurrence of undefined or critical unspecified behaviour,,Undefined,Very Hard, +cpp,MISRA-C++-2023,RULE-4-6-1,Yes,Required,Undecidable,System,Operations on a memory location shall be sequenced appropriately,RULE-13-2,SideEffects3,Easy, +cpp,MISRA-C++-2023,RULE-5-0-1,Yes,Advisory,Decidable,Single Translation Unit,Trigraph-like sequences should not be used,A2-5-1,Trigraph,Very Hard, +cpp,MISRA-C++-2023,RULE-5-7-1,Yes,Required,Decidable,Single Translation Unit,The character sequence /* shall not be used within a C-style comment,M2-7-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,DIR-5-7-2,Yes,Advisory,,,Sections of code should not be “commented out”,A2-7-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-7-3,Yes,Required,Decidable,Single Translation Unit,Line-splicing shall not be used in // comments,A2-7-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-10-1,Yes,Required,Decidable,Single Translation Unit,User-defined identifiers shall have an appropriate form,,Naming2,Easy, +cpp,MISRA-C++-2023,RULE-5-13-1,Yes,Required,Decidable,Single Translation Unit,"In character literals and non-raw string literals, \ shall only be used to form a defined escape sequence or universal character name",A2-13-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-13-2,Yes,Required,Decidable,Single Translation Unit,"Octal escape sequences, hexadecimal escape sequences, and universal character names shall be terminated",RULE-4-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-13-3,Yes,Required,Decidable,Single Translation Unit,Octal constants shall not be used,M2-13-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-13-4,Yes,Required,Decidable,Single Translation Unit,Unsigned integer literals shall be appropriately suffixed,M2-13-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-13-5,Yes,Required,Decidable,Single Translation Unit,The lowercase form of L shall not be used as the first character in a literal suffix,RULE-7-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-5-13-6,Yes,Required,Decidable,Single Translation Unit,An integer-literal of type long long shall not use a single L or l in any suffix,,Expressions2,Easy, +cpp,MISRA-C++-2023,RULE-5-13-7,No,Required,Decidable,Single Translation Unit,String literals with different encoding prefixes shall not be concatenated,A2-13-2,,, +cpp,MISRA-C++-2023,RULE-6-0-1,Yes,Required,Decidable,Single Translation Unit,Block scope declarations shall not be visually ambiguous,"M3-1-2,DCL53-CPP",Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-6-0-2,Yes,Advisory,Decidable,Single Translation Unit,"When an array with external linkage is declared, its size should be explicitly specified",RULE-18-8,Linkage,Easy, +cpp,MISRA-C++-2023,RULE-6-0-3,Yes,Advisory,Decidable,Single Translation Unit,"The only declarations in the global namespace should be main, namespace declarations and extern ""C"" declarations",M7-3-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-0-4,Yes,Required,Decidable,Single Translation Unit,The identifier main shall not be used for a function other than the global function main,M7-3-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-2-1,Yes,Required,Decidable,System,The one-definition rule shall not be violated,M3-2-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-2-2,Yes,Required,Decidable,System,All declarations of a variable or function shall have the same type,"M3-9-1,DCL40-C",Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-6-2-3,Yes,Required,Decidable,System,The source code used to implement an entity shall appear only once,,Declarations2,Medium, +cpp,MISRA-C++-2023,RULE-6-2-4,Yes,Required,Decidable,Single Translation Unit,A header file shall not contain definitions of functions or objects that are non-inline and have external linkage,,Linkage,Easy, +cpp,MISRA-C++-2023,RULE-6-4-1,Yes,Required,Decidable,Single Translation Unit,A variable declared in an inner scope shall not hide a variable declared in an outer scope,A2-10-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-4-2,Yes,Required,Decidable,Single Translation Unit,Derived classes shall not conceal functions that are inherited from their bases,A7-3-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-4-3,Yes,Required,Decidable,Single Translation Unit,A name that is present in a dependent base shall not be resolved by unqualified lookup,M14-6-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-5-1,Yes,Advisory,Decidable,Single Translation Unit,A function or object with external linkage should be introduced in a header file,,Linkage,Medium, +cpp,MISRA-C++-2023,RULE-6-5-2,Yes,Advisory,Decidable,Single Translation Unit,Internal linkage should be specified appropriately,,Linkage,Medium, +cpp,MISRA-C++-2023,RULE-6-7-1,Yes,Required,Decidable,Single Translation Unit,Local variables shall not have static storage duration,,Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-6-7-2,Yes,Required,Decidable,Single Translation Unit,Global variables shall not be used,,Banned,Easy, +cpp,MISRA-C++-2023,RULE-6-8-1,Yes,Required,Undecidable,System,An object shall not be accessed outside of its lifetime,A3-8-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-8-2,Yes,Mandatory,Decidable,Single Translation Unit,A function must not return a reference or a pointer to a local variable with automatic storage duration,M7-5-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-6-8-3,Yes,Required,Decidable,Single Translation Unit,An assignment operator shall not assign the address of an object with automatic storage duration to an object with a greater lifetime,,Lifetime,Medium, +cpp,MISRA-C++-2023,RULE-6-8-4,Yes,Advisory,Decidable,Single Translation Unit,Member functions returning references to their object should be refqualified appropriately,,Declarations2,Medium, +cpp,MISRA-C++-2023,RULE-6-9-1,Yes,Required,Decidable,Single Translation Unit,The same type aliases shall be used in all declarations of the same entity,,Declarations2,Medium, +cpp,MISRA-C++-2023,RULE-6-9-2,Yes,Advisory,Decidable,Single Translation Unit,The names of the standard signed integer types and standard unsigned integer types should not be used,A3-9-1,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-7-0-1,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion from type bool,,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-7-0-2,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion to type bool,,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-7-0-3,Yes,Required,Decidable,Single Translation Unit,The numerical value of a character shall not be used,M5-0-11,Conversions,Medium, +cpp,MISRA-C++-2023,RULE-7-0-4,Yes,Required,Decidable,Single Translation Unit,The operands of bitwise operators and shift operators shall be appropriate,RULE-10-1,Preconditions,Medium, +cpp,MISRA-C++-2023,RULE-7-0-5,Yes,Required,Decidable,Single Translation Unit,Integral promotion and the usual arithmetic conversions shall not change the signedness or the type category of an operand,"M5-0-4,M5-0-9,INT31-C",Conversions,Medium, +cpp,MISRA-C++-2023,RULE-7-0-6,Yes,Required,Decidable,Single Translation Unit,Assignment between numeric types shall be appropriate,,Conversions,Hard, +cpp,MISRA-C++-2023,RULE-7-11-1,Yes,Required,Decidable,Single Translation Unit,nullptr shall be the only form of the null-pointer-constant,A4-10-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-7-11-2,Yes,Required,Decidable,Single Translation Unit,An array passed as a function argument shall not decay to a pointer,M5-2-12,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-7-11-3,Yes,Required,Decidable,Single Translation Unit,A conversion from function type to pointer-to-function type shall only occur in appropriate contexts,,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-0-1,Yes,Advisory,Decidable,Single Translation Unit,Parentheses should be used to make the meaning of an expression appropriately explicit,M5-0-2,Expressions2,Medium, +cpp,MISRA-C++-2023,RULE-8-1-1,Yes,Required,Decidable,Single Translation Unit,A non-transient lambda shall not implicitly capture this,,Expressions2,Easy, +cpp,MISRA-C++-2023,RULE-8-1-2,Yes,Advisory,Decidable,Single Translation Unit,Variables should be captured explicitly in a non-transient lambda,A5-1-2,Expressions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-1,Yes,Required,Decidable,Single Translation Unit,A virtual base class shall only be cast to a derived class by means of dynamic_cast,,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-2,Yes,Required,Decidable,Single Translation Unit,C-style casts and functional notation casts shall not be used,A5-2-2,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-3,Yes,Required,Decidable,Single Translation Unit,A cast shall not remove any const or volatile qualification from the type accessed via a pointer or by reference,A5-2-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-2-4,Yes,Required,Decidable,Single Translation Unit,Casts shall not be performed between a pointer to function and any other type,M5-2-6,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-2-5,Yes,Required,Decidable,Single Translation Unit,reinterpret_cast shall not be used,A5-2-4,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-2-6,Yes,Required,Decidable,Single Translation Unit,"An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type","RULE-11-6, INT36-C",Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-7,Yes,Advisory,Decidable,Single Translation Unit,A cast should not convert a pointer type to an integral type,"RULE-11-6, INT36-C",Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-8,Yes,Required,Decidable,Single Translation Unit,An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t,"RULE-11-6, INT36-C",Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-9,Yes,Required,Decidable,Single Translation Unit,The operand to typeid shall not be an expression of polymorphic class type,,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-10,Yes,Required,Undecidable,System,"Functions shall not call themselves, either directly or indirectly",A7-5-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-2-11,Yes,Required,Decidable,Single Translation Unit,An argument passed via ellipsis shall have an appropriate type,,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-8-3-1,Yes,Advisory,Decidable,Single Translation Unit,The built-in unary - operator should not be applied to an expression of unsigned type,M5-3-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-3-2,Yes,Advisory,Decidable,Single Translation Unit,The built-in unary + operator should not be used,,Banned,Easy, +cpp,MISRA-C++-2023,RULE-8-7-1,Yes,Required,Undecidable,System,Pointer arithmetic shall not form an invalid pointer,ARR30-C,Memory,Easy, +cpp,MISRA-C++-2023,RULE-8-7-2,Yes,Required,Undecidable,System,Subtraction between pointers shall only be applied to pointers that address elements of the same array,ARR36-C,Memory,Easy, +cpp,MISRA-C++-2023,RULE-8-9-1,Yes,Required,Undecidable,System,"The built-in relational operators >, >=, < and <= shall not be applied to objects of pointer type, except where they point to elements of the same array",ARR36-C,Memory,Easy, +cpp,MISRA-C++-2023,RULE-8-14-1,Yes,Advisory,Undecidable,System,The right-hand operand of a logical && or operator should not contain persistent side effects,"M5-14-1, RULE-13-5",SideEffects3,Medium, +cpp,MISRA-C++-2023,RULE-8-18-1,Yes,Mandatory,Undecidable,System,An object or subobject must not be copied to an overlapping object,"M0-2-1, RULE-19-1",Memory,Hard, +cpp,MISRA-C++-2023,RULE-8-18-2,Yes,Advisory,Decidable,Single Translation Unit,The result of an assignment operator should not be used,RULE-13-4,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-19-1,Yes,Advisory,Decidable,Single Translation Unit,The comma operator should not be used,M5-18-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-8-20-1,Yes,Advisory,Decidable,Single Translation Unit,An unsigned arithmetic operation with constant operands should not wrap,INT30-C,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-2-1,Yes,Required,Decidable,Single Translation Unit,An explicit type conversion shall not be an expression statement,DCL53-CPP,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-9-3-1,Yes,Required,Decidable,Single Translation Unit,The body of an iteration-statement or a selection-statement shall be a compound-statement,RULE-15-6,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-4-1,Yes,Required,Decidable,Single Translation Unit,All if ... else if constructs shall be terminated with an else statement,RULE-15-7,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-4-2,Yes,Required,Decidable,Single Translation Unit,The structure of a switch statement shall be appropriate,"RULE-16-1, RULE-16-2,RULE-16-3,RULE-16-4,RULE-16-5,RULE-16-6,RULE-16-7",Statements,Medium, +cpp,MISRA-C++-2023,RULE-9-5-1,Yes,Advisory,Decidable,Single Translation Unit,Legacy for statements should be simple,,Statements,Hard, +cpp,MISRA-C++-2023,RULE-9-5-2,Yes,Required,Decidable,Single Translation Unit,A for-range-initializer shall contain at most one function call,,Statements,Easy, +cpp,MISRA-C++-2023,RULE-9-6-1,Yes,Advisory,Decidable,Single Translation Unit,The goto statement should not be used,RULE-15-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-6-2,Yes,Required,Decidable,Single Translation Unit,A goto statement shall reference a label in a surrounding block,RULE-15-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-6-3,Yes,Required,Decidable,Single Translation Unit,The goto statement shall jump to a label declared later in the function body,RULE-15-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-6-4,Yes,Required,Undecidable,System,A function declared with the [[noreturn]] attribute shall not return,MSC53-CPP,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-9-6-5,Yes,Required,Decidable,Single Translation Unit,A function with non-void return type shall return a value on all paths,MSC52-CPP,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-10-0-1,Yes,Advisory,Decidable,Single Translation Unit,A declaration should not declare more than one variable or member variable,M8-0-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-10-1-1,Yes,Advisory,Decidable,Single Translation Unit,The target type of a pointer or lvalue reference parameter should be const-qualified appropriately,RULE-8-13,Declarations2,Hard, +cpp,MISRA-C++-2023,RULE-10-1-2,Yes,Required,Decidable,Single Translation Unit,The volatile qualifier shall be used appropriately,,Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-10-2-1,Yes,Required,Decidable,Single Translation Unit,An enumeration shall be defined with an explicit underlying type,A7-2-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-10-2-2,Yes,Advisory,Decidable,Single Translation Unit,Unscoped enumerations should not be declared,A7-2-3,Banned,Easy, +cpp,MISRA-C++-2023,RULE-10-2-3,Yes,Required,Decidable,Single Translation Unit,The numeric value of an unscoped enumeration with no fixed underlying type shall not be used,A4-5-1,Banned,Easy, +cpp,MISRA-C++-2023,RULE-10-3-1,Yes,Advisory,Decidable,Single Translation Unit,There should be no unnamed namespaces in header files,"DCL59-CPP, M7-3-3",Banned,Easy, +cpp,MISRA-C++-2023,RULE-10-4-1,Yes,Required,Decidable,Single Translation Unit,The asm declaration shall not be used,A7-4-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-11-3-1,Yes,Advisory,Decidable,Single Translation Unit,Variables of array type should not be declared,,Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-11-3-2,Yes,Advisory,Decidable,Single Translation Unit,The declaration of an object should contain no more than two levels of pointer indirection,A5-0-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-11-6-1,Yes,Advisory,Decidable,Single Translation Unit,All variables should be initialized,,Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-11-6-2,Yes,Mandatory,Undecidable,System,The value of an object must not be read before it has been set,A8-5-0,Lifetime,Very Hard, +cpp,MISRA-C++-2023,RULE-11-6-3,Yes,Required,Decidable,Single Translation Unit,"Within an enumerator list, the value of an implicitly-specified enumeration constant shall be unique",RULE-8-12,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-12-2-1,Yes,Advisory,Decidable,Single Translation Unit,Bit-fields should not be declared,A9-6-2,Banned,Easy, +cpp,MISRA-C++-2023,RULE-12-2-2,Yes,Required,Decidable,Single Translation Unit,A bit-field shall have an appropriate type,RULE-6-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-12-2-3,Yes,Required,Decidable,Single Translation Unit,A named bit-field with signed integer type shall not have a length of one bit,M9-6-4,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-12-3-1,Yes,Required,Decidable,Single Translation Unit,The union keyword shall not be used,RULE-19-2,Banned,Easy, +cpp,MISRA-C++-2023,RULE-13-1-1,Yes,Advisory,Decidable,Single Translation Unit,Classes should not be inherited virtually,,Classes2,Easy, +cpp,MISRA-C++-2023,RULE-13-1-2,Yes,Required,Decidable,Single Translation Unit,An accessible base class shall not be both virtual and non-virtual in the same hierarchy,M10-1-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-13-3-1,Yes,Required,Decidable,Single Translation Unit,"User-declared member functions shall use the virtual, override and final specifiers appropriately",,Classes2,Easy, +cpp,MISRA-C++-2023,RULE-13-3-2,Yes,Required,Decidable,Single Translation Unit,Parameters in an overriding virtual function shall not specify different default arguments,M8-3-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-13-3-3,Yes,Required,Decidable,System,The parameters in all declarations or overrides of a function shall either be unnamed or have identical names,RULE-8-3,Declarations2,Easy, +cpp,MISRA-C++-2023,RULE-13-3-4,Yes,Required,Decidable,Single Translation Unit,A comparison of a potentially virtual pointer to member function shall only be with nullptr,A5-10-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-14-1-1,Yes,Advisory,Decidable,Single Translation Unit,Non-static data members should be either all private or all public,,Classes2,Easy, +cpp,MISRA-C++-2023,RULE-15-0-1,Yes,Required,Decidable,Single Translation Unit,Special member functions shall be provided appropriately,A12-0-1,Classes2,Medium, +cpp,MISRA-C++-2023,RULE-15-0-2,Yes,Advisory,Decidable,Single Translation Unit,User-provided copy and move member functions of a class should have appropriate signatures,,Classes2,Easy, +cpp,MISRA-C++-2023,RULE-15-1-1,Yes,Required,Undecidable,System,An object’s dynamic type shall not be used from within its constructor or destructor,M12-1-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-15-1-2,Yes,Advisory,Decidable,Single Translation Unit,All constructors of a class should explicitly initialize all of its virtual base classes and immediate base classes,A12-1-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-15-1-3,Yes,Required,Decidable,Single Translation Unit,Conversion operators and constructors that are callable with a single argument shall be explicit,"A12-1-4,A13-5-2",Classes2,Easy, +cpp,MISRA-C++-2023,RULE-15-1-4,Yes,Advisory,Decidable,Single Translation Unit,"All direct, non-static data members of a class should be initialized before the class object is accessible",,Classes2,Hard, +cpp,MISRA-C++-2023,RULE-15-1-5,Yes,Required,Decidable,Single Translation Unit,A class shall only define an initializer-list constructor when it is the only constructor,A8-5-4,ImportMisra23,Import, +cpp,MISRA-C++-2023,DIR-15-8-1,Yes,Required,Decidable,Implementation,User-provided copy assignment operators and move assignment operators shall handle self-assignment,A12-8-5,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-16-5-1,Yes,Required,Decidable,Single Translation Unit,The logical AND and logical OR operators shall not be overloaded,M5-2-11,Classes2,Easy, +cpp,MISRA-C++-2023,RULE-16-5-2,Yes,Required,Decidable,Single Translation Unit,The address-of operator shall not be overloaded,M5-3-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-16-6-1,Yes,Advisory,Decidable,Single Translation Unit,Symmetrical operators should only be implemented as non-member functions,,Classes2,Medium, +cpp,MISRA-C++-2023,RULE-17-8-1,Yes,Required,Decidable,Single Translation Unit,Function templates shall not be explicitly specialized,A14-8-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-18-1-1,Yes,Required,Decidable,Single Translation Unit,An exception object shall not have pointer type,A15-1-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-18-1-2,Yes,Required,Decidable,Single Translation Unit,An empty throw shall only occur within the compound-statement of a catch handler,M15-1-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-18-3-1,Yes,Advisory,Decidable,Single Translation Unit,There should be at least one exception handler to catch all otherwise unhandled exceptions,A15-3-3,Exceptions3,Easy, +cpp,MISRA-C++-2023,RULE-18-3-2,Yes,Required,Decidable,Single Translation Unit,An exception of class type shall be caught by const reference or reference,A15-3-5,Exceptions3,Easy, +cpp,MISRA-C++-2023,RULE-18-3-3,Yes,Required,Decidable,Single Translation Unit,Handlers for a function-try-block of a constructor or destructor shall not refer to non-static members from their class or its bases,M15-3-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-18-4-1,Yes,Required,Decidable,Single Translation Unit,Exception-unfriendly functions shall be noexcept,A15-5-1,Exceptions3,Easy, +cpp,MISRA-C++-2023,RULE-18-5-1,Yes,Advisory,Undecidable,System,A noexcept function should not attempt to propagate an exception to the calling function,A15-4-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-18-5-2,Yes,Advisory,Decidable,Single Translation Unit,Program-terminating functions should not be used,,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-19-0-1,No,Required,Decidable,Single Translation Unit,A line whose first token is # shall be a valid preprocessing directive,,,, +cpp,MISRA-C++-2023,RULE-19-0-2,Yes,Required,Decidable,Single Translation Unit,Function-like macros shall not be defined,DIR-4-9,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-0-3,Yes,Advisory,Decidable,Single Translation Unit,#include directives should only be preceded by preprocessor directives or comments,RULE-20-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-0-4,Yes,Advisory,Decidable,Single Translation Unit,#undef should only be used for macros defined previously in the same file,,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-19-1-1,Yes,Required,Decidable,Single Translation Unit,The defined preprocessor operator shall be used appropriately,M16-1-1,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-19-1-2,No,Required,Decidable,Single Translation Unit,"All #else, #elif and #endif preprocessor directives shall reside in the same file as the #if, #ifdef or #ifndef directive to which they are related",M16-1-2,,, +cpp,MISRA-C++-2023,RULE-19-1-3,Yes,Required,Decidable,Single Translation Unit,All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be defined prior to evaluation,M16-0-7,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-2-1,Yes,Required,Decidable,Single Translation Unit,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-19-2-2,Yes,Required,Decidable,Single Translation Unit,"The #include directive shall be followed by either a or ""filename"" sequence",,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-19-2-3,Yes,Required,Decidable,Single Translation Unit,"The ' or "" or \ characters and the /* or // character sequences shall not occur in a header file name",A16-2-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-3-1,Yes,Advisory,Decidable,Single Translation Unit,The # and ## preprocessor operators should not be used,M16-3-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-3-2,Yes,Required,Decidable,Single Translation Unit,A macro parameter immediately following a # operator shall not be immediately followed by a ## operator,RULE-20-11,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-3-3,Yes,Required,Decidable,Single Translation Unit,The argument to a mixed-use macro parameter shall not be subject to further expansion,RULE-20-12,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-3-4,Yes,Required,Decidable,Single Translation Unit,Parentheses shall be used to ensure macro arguments are expanded appropriately,M16-0-6,Preprocessor,Medium, +cpp,MISRA-C++-2023,RULE-19-3-5,Yes,Required,Decidable,Single Translation Unit,Tokens that look like a preprocessing directive shall not occur within a macro argument,RULE-20-6,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-19-6-1,Yes,Advisory,Decidable,Single Translation Unit,The #pragma directive and the _Pragma operator should not be used,A16-7-1,Preprocessor,Easy, +cpp,MISRA-C++-2023,RULE-21-2-1,Yes,Required,Decidable,Single Translation Unit,"The library functions atof, atoi, atol and atoll from shall not be used",RULE-21-7,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-21-2-2,Yes,Required,Decidable,Single Translation Unit,"The string handling functions from , , and shall not be used",M18-0-5,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-21-2-3,Yes,Required,Decidable,Single Translation Unit,The library function system from shall not be used,M18-0-3,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-21-2-4,Yes,Required,Decidable,Single Translation Unit,The macro offsetof shall not be used,M18-2-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-21-6-1,Yes,Advisory,Undecidable,Single Translation Unit,Dynamic memory should not be used,DIR-4-12,Banned,Easy, +cpp,MISRA-C++-2023,RULE-21-6-2,Yes,Required,Decidable,Single Translation Unit,Dynamic memory shall be managed automatically,,Memory,Easy, +cpp,MISRA-C++-2023,RULE-21-6-3,Yes,Required,Decidable,Single Translation Unit,Advanced memory management shall not be used,,Memory,Medium, +cpp,MISRA-C++-2023,RULE-21-6-4,Yes,Required,Decidable,System,"If a project defines either a sized or unsized version of a global operator delete, then both shall be defined",A18-5-4,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-21-6-5,Yes,Required,Decidable,Single Translation Unit,A pointer to an incomplete class type shall not be deleted,A5-3-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-21-10-1,Yes,Required,Decidable,Single Translation Unit,The features of shall not be used,DCL50-CPP,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-21-10-2,Yes,Required,Decidable,Single Translation Unit,The standard header file shall not be used,ERR52-CPP,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-21-10-3,Yes,Required,Decidable,Single Translation Unit,The facilities provided by the standard header file shall not be used,M18-7-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-22-3-1,Yes,Required,Decidable,Single Translation Unit,The assert macro shall not be used with a constant-expression,,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-22-4-1,Yes,Required,Decidable,Single Translation Unit,The literal value zero shall be the only value assigned to errno,,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-23-11-1,Yes,Advisory,Decidable,Single Translation Unit,The raw pointer constructors of std::shared_ptr and std::unique_ptr should not be used,,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-24-5-1,Yes,Required,Decidable,Single Translation Unit,The character handling functions from and shall not be used,,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-24-5-2,Yes,Required,Decidable,Single Translation Unit,"The C++ Standard Library functions memcpy, memmove and memcmp from shall not be used",,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-25-5-1,Yes,Required,Decidable,Single Translation Unit,The setlocale and std::locale::global functions shall not be called,,BannedAPIs,Easy, +cpp,MISRA-C++-2023,RULE-25-5-2,Yes,Mandatory,Decidable,Single Translation Unit,"The pointers returned by the C++ Standard Library functions localeconv, getenv, setlocale or strerror must only be used as if they have pointer to const-qualified type",RULE-21-19,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-25-5-3,Yes,Mandatory,Undecidable,System,"The pointer returned by the C++ Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror must not be used following a subsequent call to the same function",RULE-21-20,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-26-3-1,Yes,Advisory,Decidable,Single Translation Unit,std::vector should not be specialized with bool,A18-1-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-28-3-1,Yes,Required,Undecidable,System,Predicates shall not have persistent side effects,A25-1-1,SideEffects3,Easy, +cpp,MISRA-C++-2023,RULE-28-6-1,Yes,Required,Decidable,Single Translation Unit,The argument to std::move shall be a non-const lvalue,A18-9-3,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-28-6-2,Yes,Required,Decidable,Single Translation Unit,Forwarding references and std::forward shall be used together,A18-9-2,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-28-6-3,Yes,Required,Decidable,Single Translation Unit,An object shall not be used while in a potentially moved-from state,A12-8-3,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-28-6-4,Yes,Required,Decidable,Single Translation Unit,"The result of std::remove, std::remove_if, std::unique and empty shall be used",,DeadCode2,Easy, +cpp,MISRA-C++-2023,RULE-30-0-1,Yes,Required,Decidable,Single Translation Unit,The C Library input/output functions shall not be used,M27-0-1,ImportMisra23,Import, +cpp,MISRA-C++-2023,RULE-30-0-2,Yes,Required,Undecidable,System,Reads and writes on the same file stream shall be separated by a positioning operation,A27-0-3,ImportMisra23,Import, diff --git a/schemas/coding-standards-schema-1.0.0.json b/schemas/coding-standards-schema-1.0.0.json new file mode 100644 index 0000000000..d7ed2fc240 --- /dev/null +++ b/schemas/coding-standards-schema-1.0.0.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://raw.githubusercontent.com/github/codeql-coding-standards/main/schemas/coding-standards-schema-1.0.0.json", + "additionalProperties": false, + "definitions": { + "guideline-category": { + "enum": [ + "mandatory", + "required", + "advisory", + "disapplied" + ] + }, + "guideline-recategorization": { + "type": "object", + "properties": { + "rule-id": { + "type": "string" + }, + "category": { + "$ref": "#/definitions/guideline-category" + } + }, + "required": [ + "rule-id", + "category" + ] + } + }, + "properties": { + "report-deviated-alerts": { + "description": "When true includes alerts with an applicable deviation. Used for report generation.", + "type": "boolean" + }, + "deviations": { + "description": "A set of deviation records.", + "type": "array" + }, + "deviation-permits": { + "description": "A set of deviation permits.", + "type": "array" + }, + "guideline-recategorizations": { + "type": "array", + "minProperties": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/guideline-recategorization" + } + } + }, + "required": [], + "type": "object" +} \ No newline at end of file diff --git a/schemas/rule-package.schema.json b/schemas/rule-package.schema.json index 4d3c7f401a..fff79fede0 100644 --- a/schemas/rule-package.schema.json +++ b/schemas/rule-package.schema.json @@ -141,7 +141,8 @@ "obligation": { "type": "string", "enum": [ - "rule" + "rule", + "recommendation" ] } }, @@ -179,6 +180,58 @@ "type": "object", "patternProperties": { "^RULE-\\d+-\\d+": { + "description": "A coding standard rule", + "type": "object", + "properties": { + "properties": { + "type": "object", + "properties": { + "obligation": { + "type": "string", + "enum": [ + "required", + "advisory", + "mandatory" + ] + } + }, + "required": [ + "obligation" + ] + }, + "queries": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/query" + } + }, + "title": { + "type": "string" + }, + "implementation_scope": { + "$ref": "#/$defs/implementation_scope" + } + }, + "required": [ + "properties", + "queries", + "title" + ], + "additionalProperties": false + } + }, + "minProperties": 1 + } + } + }, + { + "properties": { + "MISRA-C++-2023": { + "description": "Rules part of the MISRA C++ 2023 standard", + "type": "object", + "patternProperties": { + "^RULE-\\d+-\\d+-\\d+": { "description": "A coding standard rule", "type": "object", "properties": { @@ -289,27 +342,41 @@ "external/cert/default-disabled", "external/autosar/strict", "scope/single-translation-unit", - "scope/system" + "scope/system", + "external/misra/audit", + "external/misra/c/2012/third-edition-first-revision", + "external/misra/c/2012/amendment2", + "external/misra/c/2012/amendment3", + "external/misra/c/2012/amendment4", + "external/misra/c/strict", + "external/cert/severity/low", + "external/cert/severity/medium", + "external/cert/severity/high", + "external/cert/likelihood/unlikely", + "external/cert/likelihood/probable", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/low", + "external/cert/remediation-cost/medium", + "external/cert/remediation-cost/high", + "external/cert/priority/p1", + "external/cert/priority/p2", + "external/cert/priority/p3", + "external/cert/priority/p4", + "external/cert/priority/p6", + "external/cert/priority/p8", + "external/cert/priority/p9", + "external/cert/priority/p12", + "external/cert/priority/p18", + "external/cert/priority/p27", + "external/cert/level/l1", + "external/cert/level/l2", + "external/cert/level/l3" ] }, "minLength": 1 }, "implementation_scope": { - "type": "object", - "properties": { - "description": { - "kind": "string" - }, - "items": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "description" - ] + "$ref": "/schemas/implementation_scope" } }, "required": [ @@ -320,6 +387,25 @@ "short_name", "tags" ] + }, + "implementation_scope": { + "$id": "/schemas/implementation_scope", + "type": "object", + "properties": { + "description": { + "kind": "string" + }, + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "description" + ], + "additionalProperties": false } } } \ No newline at end of file diff --git a/schemas/sarif-schema-2.1.0.json b/schemas/sarif-schema-2.1.0.json new file mode 100644 index 0000000000..e0b6524571 --- /dev/null +++ b/schemas/sarif-schema-2.1.0.json @@ -0,0 +1,3370 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Static Analysis Results Format (SARIF) Version 2.1.0 JSON Schema", + "$id": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "description": "Static Analysis Results Format (SARIF) Version 2.1.0 JSON Schema: a standard format for the output of static analysis tools.", + "additionalProperties": false, + "type": "object", + "properties": { + + "$schema": { + "description": "The URI of the JSON schema corresponding to the version.", + "type": "string", + "format": "uri" + }, + + "version": { + "description": "The SARIF format version of this log file.", + "enum": [ "2.1.0" ] + }, + + "runs": { + "description": "The set of runs contained in this log file.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "items": { + "$ref": "#/definitions/run" + } + }, + + "inlineExternalProperties": { + "description": "References to external property files that share data between runs.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/externalProperties" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the log file.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "version", "runs" ], + + "definitions": { + + "address": { + "description": "A physical or virtual address, or a range of addresses, in an 'addressable region' (memory or a binary file).", + "additionalProperties": false, + "type": "object", + "properties": { + + "absoluteAddress": { + "description": "The address expressed as a byte offset from the start of the addressable region.", + "type": "integer", + "minimum": -1, + "default": -1 + + }, + + "relativeAddress": { + "description": "The address expressed as a byte offset from the absolute address of the top-most parent object.", + "type": "integer" + + }, + + "length": { + "description": "The number of bytes in this range of addresses.", + "type": "integer" + }, + + "kind": { + "description": "An open-ended string that identifies the address kind. 'data', 'function', 'header','instruction', 'module', 'page', 'section', 'segment', 'stack', 'stackFrame', 'table' are well-known values.", + "type": "string" + }, + + "name": { + "description": "A name that is associated with the address, e.g., '.text'.", + "type": "string" + }, + + "fullyQualifiedName": { + "description": "A human-readable fully qualified name that is associated with the address.", + "type": "string" + }, + + "offsetFromParent": { + "description": "The byte offset of this address from the absolute or relative address of the parent object.", + "type": "integer" + }, + + "index": { + "description": "The index within run.addresses of the cached object for this address.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "parentIndex": { + "description": "The index within run.addresses of the parent object.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the address.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "artifact": { + "description": "A single artifact. In some cases, this artifact might be nested within another artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + + "description": { + "description": "A short description of the artifact.", + "$ref": "#/definitions/message" + }, + + "location": { + "description": "The location of the artifact.", + "$ref": "#/definitions/artifactLocation" + }, + + "parentIndex": { + "description": "Identifies the index of the immediate parent of the artifact, if this artifact is nested.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "offset": { + "description": "The offset in bytes of the artifact within its containing artifact.", + "type": "integer", + "minimum": 0 + }, + + "length": { + "description": "The length of the artifact in bytes.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "roles": { + "description": "The role or roles played by the artifact in the analysis.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "enum": [ + "analysisTarget", + "attachment", + "responseFile", + "resultFile", + "standardStream", + "tracedFile", + "unmodified", + "modified", + "added", + "deleted", + "renamed", + "uncontrolled", + "driver", + "extension", + "translation", + "taxonomy", + "policy", + "referencedOnCommandLine", + "memoryContents", + "directory", + "userSpecifiedConfiguration", + "toolSpecifiedConfiguration", + "debugOutputFile" + ] + } + }, + + "mimeType": { + "description": "The MIME type (RFC 2045) of the artifact.", + "type": "string", + "pattern": "[^/]+/.+" + }, + + "contents": { + "description": "The contents of the artifact.", + "$ref": "#/definitions/artifactContent" + }, + + "encoding": { + "description": "Specifies the encoding for an artifact object that refers to a text file.", + "type": "string" + }, + + "sourceLanguage": { + "description": "Specifies the source language for any artifact object that refers to a text file that contains source code.", + "type": "string" + }, + + "hashes": { + "description": "A dictionary, each of whose keys is the name of a hash function and each of whose values is the hashed value of the artifact produced by the specified hash function.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "lastModifiedTimeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which the artifact was most recently modified. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the artifact.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "artifactChange": { + "description": "A change to a single artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + + "artifactLocation": { + "description": "The location of the artifact to change.", + "$ref": "#/definitions/artifactLocation" + }, + + "replacements": { + "description": "An array of replacement objects, each of which represents the replacement of a single region in a single artifact specified by 'artifactLocation'.", + "type": "array", + "minItems": 1, + "uniqueItems": false, + "items": { + "$ref": "#/definitions/replacement" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the change.", + "$ref": "#/definitions/propertyBag" + } + + }, + + "required": [ "artifactLocation", "replacements" ] + }, + + "artifactContent": { + "description": "Represents the contents of an artifact.", + "type": "object", + "additionalProperties": false, + "properties": { + + "text": { + "description": "UTF-8-encoded content from a text artifact.", + "type": "string" + }, + + "binary": { + "description": "MIME Base64-encoded content from a binary artifact, or from a text artifact in its original encoding.", + "type": "string" + }, + + "rendered": { + "description": "An alternate rendered representation of the artifact (e.g., a decompiled representation of a binary region).", + "$ref": "#/definitions/multiformatMessageString" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the artifact content.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "artifactLocation": { + "description": "Specifies the location of an artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + + "uri": { + "description": "A string containing a valid relative or absolute URI.", + "type": "string", + "format": "uri-reference" + }, + + "uriBaseId": { + "description": "A string which indirectly specifies the absolute URI with respect to which a relative URI in the \"uri\" property is interpreted.", + "type": "string" + }, + + "index": { + "description": "The index within the run artifacts array of the artifact object associated with the artifact location.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "description": { + "description": "A short description of the artifact location.", + "$ref": "#/definitions/message" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the artifact location.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "attachment": { + "description": "An artifact relevant to a result.", + "type": "object", + "additionalProperties": false, + "properties": { + + "description": { + "description": "A message describing the role played by the attachment.", + "$ref": "#/definitions/message" + }, + + "artifactLocation": { + "description": "The location of the attachment.", + "$ref": "#/definitions/artifactLocation" + }, + + "regions": { + "description": "An array of regions of interest within the attachment.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/region" + } + }, + + "rectangles": { + "description": "An array of rectangles specifying areas of interest within the image.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/rectangle" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the attachment.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "artifactLocation" ] + }, + + "codeFlow": { + "description": "A set of threadFlows which together describe a pattern of code execution relevant to detecting a result.", + "additionalProperties": false, + "type": "object", + "properties": { + + "message": { + "description": "A message relevant to the code flow.", + "$ref": "#/definitions/message" + }, + + "threadFlows": { + "description": "An array of one or more unique threadFlow objects, each of which describes the progress of a program through a thread of execution.", + "type": "array", + "minItems": 1, + "uniqueItems": false, + "items": { + "$ref": "#/definitions/threadFlow" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the code flow.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "threadFlows" ] + }, + + "configurationOverride": { + "description": "Information about how a specific rule or notification was reconfigured at runtime.", + "type": "object", + "additionalProperties": false, + "properties": { + + "configuration": { + "description": "Specifies how the rule or notification was configured during the scan.", + "$ref": "#/definitions/reportingConfiguration" + }, + + "descriptor": { + "description": "A reference used to locate the descriptor whose configuration was overridden.", + "$ref": "#/definitions/reportingDescriptorReference" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the configuration override.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "configuration", "descriptor" ] + }, + + "conversion": { + "description": "Describes how a converter transformed the output of a static analysis tool from the analysis tool's native output format into the SARIF format.", + "additionalProperties": false, + "type": "object", + "properties": { + + "tool": { + "description": "A tool object that describes the converter.", + "$ref": "#/definitions/tool" + }, + + "invocation": { + "description": "An invocation object that describes the invocation of the converter.", + "$ref": "#/definitions/invocation" + }, + + "analysisToolLogFiles": { + "description": "The locations of the analysis tool's per-run log files.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/artifactLocation" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the conversion.", + "$ref": "#/definitions/propertyBag" + } + + }, + + "required": [ "tool" ] + }, + + "edge": { + "description": "Represents a directed edge in a graph.", + "type": "object", + "additionalProperties": false, + "properties": { + + "id": { + "description": "A string that uniquely identifies the edge within its graph.", + "type": "string" + }, + + "label": { + "description": "A short description of the edge.", + "$ref": "#/definitions/message" + }, + + "sourceNodeId": { + "description": "Identifies the source node (the node at which the edge starts).", + "type": "string" + }, + + "targetNodeId": { + "description": "Identifies the target node (the node at which the edge ends).", + "type": "string" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the edge.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "id", "sourceNodeId", "targetNodeId" ] + }, + + "edgeTraversal": { + "description": "Represents the traversal of a single edge during a graph traversal.", + "type": "object", + "additionalProperties": false, + "properties": { + + "edgeId": { + "description": "Identifies the edge being traversed.", + "type": "string" + }, + + "message": { + "description": "A message to display to the user as the edge is traversed.", + "$ref": "#/definitions/message" + }, + + "finalState": { + "description": "The values of relevant expressions after the edge has been traversed.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "stepOverEdgeCount": { + "description": "The number of edge traversals necessary to return from a nested graph.", + "type": "integer", + "minimum": 0 + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the edge traversal.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "edgeId" ] + }, + + "exception": { + "description": "Describes a runtime exception encountered during the execution of an analysis tool.", + "type": "object", + "additionalProperties": false, + "properties": { + + "kind": { + "type": "string", + "description": "A string that identifies the kind of exception, for example, the fully qualified type name of an object that was thrown, or the symbolic name of a signal." + }, + + "message": { + "description": "A message that describes the exception.", + "type": "string" + }, + + "stack": { + "description": "The sequence of function calls leading to the exception.", + "$ref": "#/definitions/stack" + }, + + "innerExceptions": { + "description": "An array of exception objects each of which is considered a cause of this exception.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/exception" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the exception.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "externalProperties": { + "description": "The top-level element of an external property file.", + "type": "object", + "additionalProperties": false, + "properties": { + + "schema": { + "description": "The URI of the JSON schema corresponding to the version of the external property file format.", + "type": "string", + "format": "uri" + }, + + "version": { + "description": "The SARIF format version of this external properties object.", + "enum": [ "2.1.0" ] + }, + + "guid": { + "description": "A stable, unique identifer for this external properties object, in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "runGuid": { + "description": "A stable, unique identifer for the run associated with this external properties object, in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "conversion": { + "description": "A conversion object that will be merged with a separate run.", + "$ref": "#/definitions/conversion" + }, + + "graphs": { + "description": "An array of graph objects that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "default": [], + "uniqueItems": true, + "items": { + "$ref": "#/definitions/graph" + } + }, + + "externalizedProperties": { + "description": "Key/value pairs that provide additional information that will be merged with a separate run.", + "$ref": "#/definitions/propertyBag" + }, + + "artifacts": { + "description": "An array of artifact objects that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/artifact" + } + }, + + "invocations": { + "description": "Describes the invocation of the analysis tool that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/invocation" + } + }, + + "logicalLocations": { + "description": "An array of logical locations such as namespaces, types or functions that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/logicalLocation" + } + }, + + "threadFlowLocations": { + "description": "An array of threadFlowLocation objects that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/threadFlowLocation" + } + }, + + "results": { + "description": "An array of result objects that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/result" + } + }, + + "taxonomies": { + "description": "Tool taxonomies that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "driver": { + "description": "The analysis tool object that will be merged with a separate run.", + "$ref": "#/definitions/toolComponent" + }, + + "extensions": { + "description": "Tool extensions that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "policies": { + "description": "Tool policies that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "translations": { + "description": "Tool translations that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "addresses": { + "description": "Addresses that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/address" + } + }, + + "webRequests": { + "description": "Requests that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/webRequest" + } + }, + + "webResponses": { + "description": "Responses that will be merged with a separate run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/webResponse" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the external properties.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "externalPropertyFileReference": { + "description": "Contains information that enables a SARIF consumer to locate the external property file that contains the value of an externalized property associated with the run.", + "type": "object", + "additionalProperties": false, + "properties": { + + "location": { + "description": "The location of the external property file.", + "$ref": "#/definitions/artifactLocation" + }, + + "guid": { + "description": "A stable, unique identifer for the external property file in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "itemCount": { + "description": "A non-negative integer specifying the number of items contained in the external property file.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the external property file.", + "$ref": "#/definitions/propertyBag" + } + }, + "anyOf": [ + { "required": [ "location" ] }, + { "required": [ "guid" ] } + ] + }, + + "externalPropertyFileReferences": { + "description": "References to external property files that should be inlined with the content of a root log file.", + "additionalProperties": false, + "type": "object", + "properties": { + + "conversion": { + "description": "An external property file containing a run.conversion object to be merged with the root log file.", + "$ref": "#/definitions/externalPropertyFileReference" + }, + + "graphs": { + "description": "An array of external property files containing a run.graphs object to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "externalizedProperties": { + "description": "An external property file containing a run.properties object to be merged with the root log file.", + "$ref": "#/definitions/externalPropertyFileReference" + }, + + "artifacts": { + "description": "An array of external property files containing run.artifacts arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "invocations": { + "description": "An array of external property files containing run.invocations arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "logicalLocations": { + "description": "An array of external property files containing run.logicalLocations arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "threadFlowLocations": { + "description": "An array of external property files containing run.threadFlowLocations arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "results": { + "description": "An array of external property files containing run.results arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "taxonomies": { + "description": "An array of external property files containing run.taxonomies arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "addresses": { + "description": "An array of external property files containing run.addresses arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "driver": { + "description": "An external property file containing a run.driver object to be merged with the root log file.", + "$ref": "#/definitions/externalPropertyFileReference" + }, + + "extensions": { + "description": "An array of external property files containing run.extensions arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "policies": { + "description": "An array of external property files containing run.policies arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "translations": { + "description": "An array of external property files containing run.translations arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "webRequests": { + "description": "An array of external property files containing run.requests arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "webResponses": { + "description": "An array of external property files containing run.responses arrays to be merged with the root log file.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/externalPropertyFileReference" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the external property files.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "fix": { + "description": "A proposed fix for the problem represented by a result object. A fix specifies a set of artifacts to modify. For each artifact, it specifies a set of bytes to remove, and provides a set of new bytes to replace them.", + "additionalProperties": false, + "type": "object", + "properties": { + + "description": { + "description": "A message that describes the proposed fix, enabling viewers to present the proposed change to an end user.", + "$ref": "#/definitions/message" + }, + + "artifactChanges": { + "description": "One or more artifact changes that comprise a fix for a result.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/artifactChange" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the fix.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "artifactChanges" ] + }, + + "graph": { + "description": "A network of nodes and directed edges that describes some aspect of the structure of the code (for example, a call graph).", + "type": "object", + "additionalProperties": false, + "properties": { + + "description": { + "description": "A description of the graph.", + "$ref": "#/definitions/message" + }, + + "nodes": { + "description": "An array of node objects representing the nodes of the graph.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/node" + } + }, + + "edges": { + "description": "An array of edge objects representing the edges of the graph.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/edge" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the graph.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "graphTraversal": { + "description": "Represents a path through a graph.", + "type": "object", + "additionalProperties": false, + "properties": { + + "runGraphIndex": { + "description": "The index within the run.graphs to be associated with the result.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "resultGraphIndex": { + "description": "The index within the result.graphs to be associated with the result.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "description": { + "description": "A description of this graph traversal.", + "$ref": "#/definitions/message" + }, + + "initialState": { + "description": "Values of relevant expressions at the start of the graph traversal that may change during graph traversal.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "immutableState": { + "description": "Values of relevant expressions at the start of the graph traversal that remain constant for the graph traversal.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "edgeTraversals": { + "description": "The sequences of edges traversed by this graph traversal.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/edgeTraversal" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the graph traversal.", + "$ref": "#/definitions/propertyBag" + } + }, + "oneOf": [ + { "required": [ "runGraphIndex" ] }, + { "required": [ "resultGraphIndex" ] } + ] + }, + + "invocation": { + "description": "The runtime environment of the analysis tool run.", + "additionalProperties": false, + "type": "object", + "properties": { + + "commandLine": { + "description": "The command line used to invoke the tool.", + "type": "string" + }, + + "arguments": { + "description": "An array of strings, containing in order the command line arguments passed to the tool from the operating system.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "items": { + "type": "string" + } + }, + + "responseFiles": { + "description": "The locations of any response files specified on the tool's command line.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/artifactLocation" + } + }, + + "startTimeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which the invocation started. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + + "endTimeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which the invocation ended. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + + "exitCode": { + "description": "The process exit code.", + "type": "integer" + }, + + "ruleConfigurationOverrides": { + "description": "An array of configurationOverride objects that describe rules related runtime overrides.", + "type": "array", + "minItems": 0, + "default": [], + "uniqueItems": true, + "items": { + "$ref": "#/definitions/configurationOverride" + } + }, + + "notificationConfigurationOverrides": { + "description": "An array of configurationOverride objects that describe notifications related runtime overrides.", + "type": "array", + "minItems": 0, + "default": [], + "uniqueItems": true, + "items": { + "$ref": "#/definitions/configurationOverride" + } + }, + + "toolExecutionNotifications": { + "description": "A list of runtime conditions detected by the tool during the analysis.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/notification" + } + }, + + "toolConfigurationNotifications": { + "description": "A list of conditions detected by the tool that are relevant to the tool's configuration.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/notification" + } + }, + + "exitCodeDescription": { + "description": "The reason for the process exit.", + "type": "string" + }, + + "exitSignalName": { + "description": "The name of the signal that caused the process to exit.", + "type": "string" + }, + + "exitSignalNumber": { + "description": "The numeric value of the signal that caused the process to exit.", + "type": "integer" + }, + + "processStartFailureMessage": { + "description": "The reason given by the operating system that the process failed to start.", + "type": "string" + }, + + "executionSuccessful": { + "description": "Specifies whether the tool's execution completed successfully.", + "type": "boolean" + }, + + "machine": { + "description": "The machine on which the invocation occurred.", + "type": "string" + }, + + "account": { + "description": "The account under which the invocation occurred.", + "type": "string" + }, + + "processId": { + "description": "The id of the process in which the invocation occurred.", + "type": "integer" + }, + + "executableLocation": { + "description": "An absolute URI specifying the location of the executable that was invoked.", + "$ref": "#/definitions/artifactLocation" + }, + + "workingDirectory": { + "description": "The working directory for the invocation.", + "$ref": "#/definitions/artifactLocation" + }, + + "environmentVariables": { + "description": "The environment variables associated with the analysis tool process, expressed as key/value pairs.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "stdin": { + "description": "A file containing the standard input stream to the process that was invoked.", + "$ref": "#/definitions/artifactLocation" + }, + + "stdout": { + "description": "A file containing the standard output stream from the process that was invoked.", + "$ref": "#/definitions/artifactLocation" + }, + + "stderr": { + "description": "A file containing the standard error stream from the process that was invoked.", + "$ref": "#/definitions/artifactLocation" + }, + + "stdoutStderr": { + "description": "A file containing the interleaved standard output and standard error stream from the process that was invoked.", + "$ref": "#/definitions/artifactLocation" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the invocation.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "executionSuccessful" ] + }, + + "location": { + "description": "A location within a programming artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + + "id": { + "description": "Value that distinguishes this location from all other locations within a single result object.", + "type": "integer", + "minimum": -1, + "default": -1 + }, + + "physicalLocation": { + "description": "Identifies the artifact and region.", + "$ref": "#/definitions/physicalLocation" + }, + + "logicalLocations": { + "description": "The logical locations associated with the result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/logicalLocation" + } + }, + + "message": { + "description": "A message relevant to the location.", + "$ref": "#/definitions/message" + }, + + "annotations": { + "description": "A set of regions relevant to the location.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/region" + } + }, + + "relationships": { + "description": "An array of objects that describe relationships between this location and others.", + "type": "array", + "default": [], + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/locationRelationship" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the location.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "locationRelationship": { + "description": "Information about the relation of one location to another.", + "type": "object", + "additionalProperties": false, + "properties": { + + "target": { + "description": "A reference to the related location.", + "type": "integer", + "minimum": 0 + }, + + "kinds": { + "description": "A set of distinct strings that categorize the relationship. Well-known kinds include 'includes', 'isIncludedBy' and 'relevant'.", + "type": "array", + "default": [ "relevant" ], + "uniqueItems": true, + "items": { + "type": "string" + } + }, + + "description": { + "description": "A description of the location relationship.", + "$ref": "#/definitions/message" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the location relationship.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "target" ] + }, + + "logicalLocation": { + "description": "A logical location of a construct that produced a result.", + "additionalProperties": false, + "type": "object", + "properties": { + + "name": { + "description": "Identifies the construct in which the result occurred. For example, this property might contain the name of a class or a method.", + "type": "string" + }, + + "index": { + "description": "The index within the logical locations array.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "fullyQualifiedName": { + "description": "The human-readable fully qualified name of the logical location.", + "type": "string" + }, + + "decoratedName": { + "description": "The machine-readable name for the logical location, such as a mangled function name provided by a C++ compiler that encodes calling convention, return type and other details along with the function name.", + "type": "string" + }, + + "parentIndex": { + "description": "Identifies the index of the immediate parent of the construct in which the result was detected. For example, this property might point to a logical location that represents the namespace that holds a type.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "kind": { + "description": "The type of construct this logical location component refers to. Should be one of 'function', 'member', 'module', 'namespace', 'parameter', 'resource', 'returnType', 'type', 'variable', 'object', 'array', 'property', 'value', 'element', 'text', 'attribute', 'comment', 'declaration', 'dtd' or 'processingInstruction', if any of those accurately describe the construct.", + "type": "string" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the logical location.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "message": { + "description": "Encapsulates a message intended to be read by the end user.", + "type": "object", + "additionalProperties": false, + + "properties": { + + "text": { + "description": "A plain text message string.", + "type": "string" + }, + + "markdown": { + "description": "A Markdown message string.", + "type": "string" + }, + + "id": { + "description": "The identifier for this message.", + "type": "string" + }, + + "arguments": { + "description": "An array of strings to substitute into the message string.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "type": "string" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the message.", + "$ref": "#/definitions/propertyBag" + } + }, + "anyOf": [ + { "required": [ "text" ] }, + { "required": [ "id" ] } + ] + }, + + "multiformatMessageString": { + "description": "A message string or message format string rendered in multiple formats.", + "type": "object", + "additionalProperties": false, + + "properties": { + + "text": { + "description": "A plain text message string or format string.", + "type": "string" + }, + + "markdown": { + "description": "A Markdown message string or format string.", + "type": "string" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the message.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "text" ] + }, + + "node": { + "description": "Represents a node in a graph.", + "type": "object", + "additionalProperties": false, + + "properties": { + + "id": { + "description": "A string that uniquely identifies the node within its graph.", + "type": "string" + }, + + "label": { + "description": "A short description of the node.", + "$ref": "#/definitions/message" + }, + + "location": { + "description": "A code location associated with the node.", + "$ref": "#/definitions/location" + }, + + "children": { + "description": "Array of child nodes.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/node" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the node.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "id" ] + }, + + "notification": { + "description": "Describes a condition relevant to the tool itself, as opposed to being relevant to a target being analyzed by the tool.", + "type": "object", + "additionalProperties": false, + "properties": { + + "locations": { + "description": "The locations relevant to this notification.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/location" + } + }, + + "message": { + "description": "A message that describes the condition that was encountered.", + "$ref": "#/definitions/message" + }, + + "level": { + "description": "A value specifying the severity level of the notification.", + "default": "warning", + "enum": [ "none", "note", "warning", "error" ] + }, + + "threadId": { + "description": "The thread identifier of the code that generated the notification.", + "type": "integer" + }, + + "timeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which the analysis tool generated the notification.", + "type": "string", + "format": "date-time" + }, + + "exception": { + "description": "The runtime exception, if any, relevant to this notification.", + "$ref": "#/definitions/exception" + }, + + "descriptor": { + "description": "A reference used to locate the descriptor relevant to this notification.", + "$ref": "#/definitions/reportingDescriptorReference" + }, + + "associatedRule": { + "description": "A reference used to locate the rule descriptor associated with this notification.", + "$ref": "#/definitions/reportingDescriptorReference" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the notification.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "message" ] + }, + + "physicalLocation": { + "description": "A physical location relevant to a result. Specifies a reference to a programming artifact together with a range of bytes or characters within that artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + + "address": { + "description": "The address of the location.", + "$ref": "#/definitions/address" + }, + + "artifactLocation": { + "description": "The location of the artifact.", + "$ref": "#/definitions/artifactLocation" + }, + + "region": { + "description": "Specifies a portion of the artifact.", + "$ref": "#/definitions/region" + }, + + "contextRegion": { + "description": "Specifies a portion of the artifact that encloses the region. Allows a viewer to display additional context around the region.", + "$ref": "#/definitions/region" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the physical location.", + "$ref": "#/definitions/propertyBag" + } + }, + + "anyOf": [ + { + "required": [ "address" ] + }, + { + "required": [ "artifactLocation" ] + } + ] + }, + + "propertyBag": { + "description": "Key/value pairs that provide additional information about the object.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + }, + + "rectangle": { + "description": "An area within an image.", + "additionalProperties": false, + "type": "object", + "properties": { + + "top": { + "description": "The Y coordinate of the top edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + + "left": { + "description": "The X coordinate of the left edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + + "bottom": { + "description": "The Y coordinate of the bottom edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + + "right": { + "description": "The X coordinate of the right edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + + "message": { + "description": "A message relevant to the rectangle.", + "$ref": "#/definitions/message" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the rectangle.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "region": { + "description": "A region within an artifact where a result was detected.", + "additionalProperties": false, + "type": "object", + "properties": { + + "startLine": { + "description": "The line number of the first character in the region.", + "type": "integer", + "minimum": 1 + }, + + "startColumn": { + "description": "The column number of the first character in the region.", + "type": "integer", + "minimum": 1 + }, + + "endLine": { + "description": "The line number of the last character in the region.", + "type": "integer", + "minimum": 1 + }, + + "endColumn": { + "description": "The column number of the character following the end of the region.", + "type": "integer", + "minimum": 1 + }, + + "charOffset": { + "description": "The zero-based offset from the beginning of the artifact of the first character in the region.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "charLength": { + "description": "The length of the region in characters.", + "type": "integer", + "minimum": 0 + }, + + "byteOffset": { + "description": "The zero-based offset from the beginning of the artifact of the first byte in the region.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "byteLength": { + "description": "The length of the region in bytes.", + "type": "integer", + "minimum": 0 + }, + + "snippet": { + "description": "The portion of the artifact contents within the specified region.", + "$ref": "#/definitions/artifactContent" + }, + + "message": { + "description": "A message relevant to the region.", + "$ref": "#/definitions/message" + }, + + "sourceLanguage": { + "description": "Specifies the source language, if any, of the portion of the artifact specified by the region object.", + "type": "string" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the region.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "replacement": { + "description": "The replacement of a single region of an artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + + "deletedRegion": { + "description": "The region of the artifact to delete.", + "$ref": "#/definitions/region" + }, + + "insertedContent": { + "description": "The content to insert at the location specified by the 'deletedRegion' property.", + "$ref": "#/definitions/artifactContent" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the replacement.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "deletedRegion" ] + }, + + "reportingDescriptor": { + "description": "Metadata that describes a specific report produced by the tool, as part of the analysis it provides or its runtime reporting.", + "additionalProperties": false, + "type": "object", + "properties": { + + "id": { + "description": "A stable, opaque identifier for the report.", + "type": "string" + }, + + "deprecatedIds": { + "description": "An array of stable, opaque identifiers by which this report was known in some previous version of the analysis tool.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "type": "string" + } + }, + + "guid": { + "description": "A unique identifer for the reporting descriptor in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "deprecatedGuids": { + "description": "An array of unique identifies in the form of a GUID by which this report was known in some previous version of the analysis tool.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + } + }, + + "name": { + "description": "A report identifier that is understandable to an end user.", + "type": "string" + }, + + "deprecatedNames": { + "description": "An array of readable identifiers by which this report was known in some previous version of the analysis tool.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "type": "string" + } + }, + + "shortDescription": { + "description": "A concise description of the report. Should be a single sentence that is understandable when visible space is limited to a single line of text.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "fullDescription": { + "description": "A description of the report. Should, as far as possible, provide details sufficient to enable resolution of any problem indicated by the result.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "messageStrings": { + "description": "A set of name/value pairs with arbitrary names. Each value is a multiformatMessageString object, which holds message strings in plain text and (optionally) Markdown format. The strings can include placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "defaultConfiguration": { + "description": "Default reporting configuration information.", + "$ref": "#/definitions/reportingConfiguration" + }, + + "helpUri": { + "description": "A URI where the primary documentation for the report can be found.", + "type": "string", + "format": "uri" + }, + + "help": { + "description": "Provides the primary documentation for the report, useful when there is no online documentation.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "relationships": { + "description": "An array of objects that describe relationships between this reporting descriptor and others.", + "type": "array", + "default": [], + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/reportingDescriptorRelationship" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the report.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "id" ] + }, + + "reportingConfiguration": { + "description": "Information about a rule or notification that can be configured at runtime.", + "type": "object", + "additionalProperties": false, + "properties": { + + "enabled": { + "description": "Specifies whether the report may be produced during the scan.", + "type": "boolean", + "default": true + }, + + "level": { + "description": "Specifies the failure level for the report.", + "default": "warning", + "enum": [ "none", "note", "warning", "error" ] + }, + + "rank": { + "description": "Specifies the relative priority of the report. Used for analysis output only.", + "type": "number", + "default": -1.0, + "minimum": -1.0, + "maximum": 100.0 + }, + + "parameters": { + "description": "Contains configuration information specific to a report.", + "$ref": "#/definitions/propertyBag" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the reporting configuration.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "reportingDescriptorReference": { + "description": "Information about how to locate a relevant reporting descriptor.", + "type": "object", + "additionalProperties": false, + "properties": { + + "id": { + "description": "The id of the descriptor.", + "type": "string" + }, + + "index": { + "description": "The index into an array of descriptors in toolComponent.ruleDescriptors, toolComponent.notificationDescriptors, or toolComponent.taxonomyDescriptors, depending on context.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "guid": { + "description": "A guid that uniquely identifies the descriptor.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "toolComponent": { + "description": "A reference used to locate the toolComponent associated with the descriptor.", + "$ref": "#/definitions/toolComponentReference" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the reporting descriptor reference.", + "$ref": "#/definitions/propertyBag" + } + }, + "anyOf": [ + { "required": [ "index" ] }, + { "required": [ "guid" ] }, + { "required": [ "id" ] } + ] + }, + + "reportingDescriptorRelationship": { + "description": "Information about the relation of one reporting descriptor to another.", + "type": "object", + "additionalProperties": false, + "properties": { + + "target": { + "description": "A reference to the related reporting descriptor.", + "$ref": "#/definitions/reportingDescriptorReference" + }, + + "kinds": { + "description": "A set of distinct strings that categorize the relationship. Well-known kinds include 'canPrecede', 'canFollow', 'willPrecede', 'willFollow', 'superset', 'subset', 'equal', 'disjoint', 'relevant', and 'incomparable'.", + "type": "array", + "default": [ "relevant" ], + "uniqueItems": true, + "items": { + "type": "string" + } + }, + + "description": { + "description": "A description of the reporting descriptor relationship.", + "$ref": "#/definitions/message" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the reporting descriptor reference.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "target" ] + }, + + "result": { + "description": "A result produced by an analysis tool.", + "additionalProperties": false, + "type": "object", + "properties": { + + "ruleId": { + "description": "The stable, unique identifier of the rule, if any, to which this result is relevant.", + "type": "string" + }, + + "ruleIndex": { + "description": "The index within the tool component rules array of the rule object associated with this result.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "rule": { + "description": "A reference used to locate the rule descriptor relevant to this result.", + "$ref": "#/definitions/reportingDescriptorReference" + }, + + "kind": { + "description": "A value that categorizes results by evaluation state.", + "default": "fail", + "enum": [ "notApplicable", "pass", "fail", "review", "open", "informational" ] + }, + + "level": { + "description": "A value specifying the severity level of the result.", + "default": "warning", + "enum": [ "none", "note", "warning", "error" ] + }, + + "message": { + "description": "A message that describes the result. The first sentence of the message only will be displayed when visible space is limited.", + "$ref": "#/definitions/message" + }, + + "analysisTarget": { + "description": "Identifies the artifact that the analysis tool was instructed to scan. This need not be the same as the artifact where the result actually occurred.", + "$ref": "#/definitions/artifactLocation" + }, + + "locations": { + "description": "The set of locations where the result was detected. Specify only one location unless the problem indicated by the result can only be corrected by making a change at every specified location.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/location" + } + }, + + "guid": { + "description": "A stable, unique identifer for the result in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "correlationGuid": { + "description": "A stable, unique identifier for the equivalence class of logically identical results to which this result belongs, in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "occurrenceCount": { + "description": "A positive integer specifying the number of times this logically unique result was observed in this run.", + "type": "integer", + "minimum": 1 + }, + + "partialFingerprints": { + "description": "A set of strings that contribute to the stable, unique identity of the result.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "fingerprints": { + "description": "A set of strings each of which individually defines a stable, unique identity for the result.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "stacks": { + "description": "An array of 'stack' objects relevant to the result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/stack" + } + }, + + "codeFlows": { + "description": "An array of 'codeFlow' objects relevant to the result.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/codeFlow" + } + }, + + "graphs": { + "description": "An array of zero or more unique graph objects associated with the result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/graph" + } + }, + + "graphTraversals": { + "description": "An array of one or more unique 'graphTraversal' objects.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/graphTraversal" + } + }, + + "relatedLocations": { + "description": "A set of locations relevant to this result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/location" + } + }, + + "suppressions": { + "description": "A set of suppressions relevant to this result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/suppression" + } + }, + + "baselineState": { + "description": "The state of a result relative to a baseline of a previous run.", + "enum": [ + "new", + "unchanged", + "updated", + "absent" + ] + }, + + "rank": { + "description": "A number representing the priority or importance of the result.", + "type": "number", + "default": -1.0, + "minimum": -1.0, + "maximum": 100.0 + }, + + "attachments": { + "description": "A set of artifacts relevant to the result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/attachment" + } + }, + + "hostedViewerUri": { + "description": "An absolute URI at which the result can be viewed.", + "type": "string", + "format": "uri" + }, + + "workItemUris": { + "description": "The URIs of the work items associated with this result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "type": "string", + "format": "uri" + } + }, + + "provenance": { + "description": "Information about how and when the result was detected.", + "$ref": "#/definitions/resultProvenance" + }, + + "fixes": { + "description": "An array of 'fix' objects, each of which represents a proposed fix to the problem indicated by the result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/fix" + } + }, + + "taxa": { + "description": "An array of references to taxonomy reporting descriptors that are applicable to the result.", + "type": "array", + "default": [], + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/reportingDescriptorReference" + } + }, + + "webRequest": { + "description": "A web request associated with this result.", + "$ref": "#/definitions/webRequest" + }, + + "webResponse": { + "description": "A web response associated with this result.", + "$ref": "#/definitions/webResponse" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the result.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "message" ] + }, + + "resultProvenance": { + "description": "Contains information about how and when a result was detected.", + "additionalProperties": false, + "type": "object", + "properties": { + + "firstDetectionTimeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which the result was first detected. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + + "lastDetectionTimeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which the result was most recently detected. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + + "firstDetectionRunGuid": { + "description": "A GUID-valued string equal to the automationDetails.guid property of the run in which the result was first detected.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "lastDetectionRunGuid": { + "description": "A GUID-valued string equal to the automationDetails.guid property of the run in which the result was most recently detected.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "invocationIndex": { + "description": "The index within the run.invocations array of the invocation object which describes the tool invocation that detected the result.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "conversionSources": { + "description": "An array of physicalLocation objects which specify the portions of an analysis tool's output that a converter transformed into the result.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/physicalLocation" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the result.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "run": { + "description": "Describes a single run of an analysis tool, and contains the reported output of that run.", + "additionalProperties": false, + "type": "object", + "properties": { + + "tool": { + "description": "Information about the tool or tool pipeline that generated the results in this run. A run can only contain results produced by a single tool or tool pipeline. A run can aggregate results from multiple log files, as long as context around the tool run (tool command-line arguments and the like) is identical for all aggregated files.", + "$ref": "#/definitions/tool" + }, + + "invocations": { + "description": "Describes the invocation of the analysis tool.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/invocation" + } + }, + + "conversion": { + "description": "A conversion object that describes how a converter transformed an analysis tool's native reporting format into the SARIF format.", + "$ref": "#/definitions/conversion" + }, + + "language": { + "description": "The language of the messages emitted into the log file during this run (expressed as an ISO 639-1 two-letter lowercase culture code) and an optional region (expressed as an ISO 3166-1 two-letter uppercase subculture code associated with a country or region). The casing is recommended but not required (in order for this data to conform to RFC5646).", + "type": "string", + "default": "en-US", + "pattern": "^[a-zA-Z]{2}|^[a-zA-Z]{2}-[a-zA-Z]{2}]?$" + }, + + "versionControlProvenance": { + "description": "Specifies the revision in version control of the artifacts that were scanned.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/versionControlDetails" + } + }, + + "originalUriBaseIds": { + "description": "The artifact location specified by each uriBaseId symbol on the machine where the tool originally ran.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/artifactLocation" + } + }, + + "artifacts": { + "description": "An array of artifact objects relevant to the run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/artifact" + } + }, + + "logicalLocations": { + "description": "An array of logical locations such as namespaces, types or functions.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/logicalLocation" + } + }, + + "graphs": { + "description": "An array of zero or more unique graph objects associated with the run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/graph" + } + }, + + "results": { + "description": "The set of results contained in an SARIF log. The results array can be omitted when a run is solely exporting rules metadata. It must be present (but may be empty) if a log file represents an actual scan.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "items": { + "$ref": "#/definitions/result" + } + }, + + "automationDetails": { + "description": "Automation details that describe this run.", + "$ref": "#/definitions/runAutomationDetails" + }, + + "runAggregates": { + "description": "Automation details that describe the aggregate of runs to which this run belongs.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/runAutomationDetails" + } + }, + + "baselineGuid": { + "description": "The 'guid' property of a previous SARIF 'run' that comprises the baseline that was used to compute result 'baselineState' properties for the run.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "redactionTokens": { + "description": "An array of strings used to replace sensitive information in a redaction-aware property.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + }, + + "defaultEncoding": { + "description": "Specifies the default encoding for any artifact object that refers to a text file.", + "type": "string" + }, + + "defaultSourceLanguage": { + "description": "Specifies the default source language for any artifact object that refers to a text file that contains source code.", + "type": "string" + }, + + "newlineSequences": { + "description": "An ordered list of character sequences that were treated as line breaks when computing region information for the run.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "default": [ "\r\n", "\n" ], + "items": { + "type": "string" + } + }, + + "columnKind": { + "description": "Specifies the unit in which the tool measures columns.", + "enum": [ "utf16CodeUnits", "unicodeCodePoints" ] + }, + + "externalPropertyFileReferences": { + "description": "References to external property files that should be inlined with the content of a root log file.", + "$ref": "#/definitions/externalPropertyFileReferences" + }, + + "threadFlowLocations": { + "description": "An array of threadFlowLocation objects cached at run level.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/threadFlowLocation" + } + }, + + "taxonomies": { + "description": "An array of toolComponent objects relevant to a taxonomy in which results are categorized.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "addresses": { + "description": "Addresses associated with this run instance, if any.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "$ref": "#/definitions/address" + } + }, + + "translations": { + "description": "The set of available translations of the localized data provided by the tool.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "policies": { + "description": "Contains configurations that may potentially override both reportingDescriptor.defaultConfiguration (the tool's default severities) and invocation.configurationOverrides (severities established at run-time from the command line).", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "webRequests": { + "description": "An array of request objects cached at run level.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/webRequest" + } + }, + + "webResponses": { + "description": "An array of response objects cached at run level.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/webResponse" + } + }, + + "specialLocations": { + "description": "A specialLocations object that defines locations of special significance to SARIF consumers.", + "$ref": "#/definitions/specialLocations" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the run.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "tool" ] + }, + + "runAutomationDetails": { + "description": "Information that describes a run's identity and role within an engineering system process.", + "additionalProperties": false, + "type": "object", + "properties": { + + "description": { + "description": "A description of the identity and role played within the engineering system by this object's containing run object.", + "$ref": "#/definitions/message" + }, + + "id": { + "description": "A hierarchical string that uniquely identifies this object's containing run object.", + "type": "string" + }, + + "guid": { + "description": "A stable, unique identifer for this object's containing run object in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "correlationGuid": { + "description": "A stable, unique identifier for the equivalence class of runs to which this object's containing run object belongs in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the run automation details.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "specialLocations": { + "description": "Defines locations of special significance to SARIF consumers.", + "type": "object", + "additionalProperties": false, + "properties": { + + "displayBase": { + "description": "Provides a suggestion to SARIF consumers to display file paths relative to the specified location.", + "$ref": "#/definitions/artifactLocation" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the special locations.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "stack": { + "description": "A call stack that is relevant to a result.", + "additionalProperties": false, + "type": "object", + "properties": { + + "message": { + "description": "A message relevant to this call stack.", + "$ref": "#/definitions/message" + }, + + "frames": { + "description": "An array of stack frames that represents a sequence of calls, rendered in reverse chronological order, that comprise the call stack.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "items": { + "$ref": "#/definitions/stackFrame" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the stack.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "frames" ] + }, + + "stackFrame": { + "description": "A function call within a stack trace.", + "additionalProperties": false, + "type": "object", + "properties": { + + "location": { + "description": "The location to which this stack frame refers.", + "$ref": "#/definitions/location" + }, + + "module": { + "description": "The name of the module that contains the code of this stack frame.", + "type": "string" + }, + + "threadId": { + "description": "The thread identifier of the stack frame.", + "type": "integer" + }, + + "parameters": { + "description": "The parameters of the call that is executing.", + "type": "array", + "minItems": 0, + "uniqueItems": false, + "default": [], + "items": { + "type": "string", + "default": [] + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the stack frame.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "suppression": { + "description": "A suppression that is relevant to a result.", + "additionalProperties": false, + "type": "object", + "properties": { + + "guid": { + "description": "A stable, unique identifer for the suprression in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "kind": { + "description": "A string that indicates where the suppression is persisted.", + "enum": [ + "inSource", + "external" + ] + }, + + "status": { + "description": "A string that indicates the review status of the suppression.", + "enum": [ + "accepted", + "underReview", + "rejected" + ] + }, + + "justification": { + "description": "A string representing the justification for the suppression.", + "type": "string" + }, + + "location": { + "description": "Identifies the location associated with the suppression.", + "$ref": "#/definitions/location" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the suppression.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "kind" ] + }, + + "threadFlow": { + "description": "Describes a sequence of code locations that specify a path through a single thread of execution such as an operating system or fiber.", + "type": "object", + "additionalProperties": false, + "properties": { + + "id": { + "description": "An string that uniquely identifies the threadFlow within the codeFlow in which it occurs.", + "type": "string" + }, + + "message": { + "description": "A message relevant to the thread flow.", + "$ref": "#/definitions/message" + }, + + + "initialState": { + "description": "Values of relevant expressions at the start of the thread flow that may change during thread flow execution.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "immutableState": { + "description": "Values of relevant expressions at the start of the thread flow that remain constant.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "locations": { + "description": "A temporally ordered array of 'threadFlowLocation' objects, each of which describes a location visited by the tool while producing the result.", + "type": "array", + "minItems": 1, + "uniqueItems": false, + "items": { + "$ref": "#/definitions/threadFlowLocation" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the thread flow.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "locations" ] + }, + + "threadFlowLocation": { + "description": "A location visited by an analysis tool while simulating or monitoring the execution of a program.", + "additionalProperties": false, + "type": "object", + "properties": { + + "index": { + "description": "The index within the run threadFlowLocations array.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "location": { + "description": "The code location.", + "$ref": "#/definitions/location" + }, + + "stack": { + "description": "The call stack leading to this location.", + "$ref": "#/definitions/stack" + }, + + "kinds": { + "description": "A set of distinct strings that categorize the thread flow location. Well-known kinds include 'acquire', 'release', 'enter', 'exit', 'call', 'return', 'branch', 'implicit', 'false', 'true', 'caution', 'danger', 'unknown', 'unreachable', 'taint', 'function', 'handler', 'lock', 'memory', 'resource', 'scope' and 'value'.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + }, + + "taxa": { + "description": "An array of references to rule or taxonomy reporting descriptors that are applicable to the thread flow location.", + "type": "array", + "default": [], + "minItems": 0, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/reportingDescriptorReference" + } + }, + + "module": { + "description": "The name of the module that contains the code that is executing.", + "type": "string" + }, + + "state": { + "description": "A dictionary, each of whose keys specifies a variable or expression, the associated value of which represents the variable or expression value. For an annotation of kind 'continuation', for example, this dictionary might hold the current assumed values of a set of global variables.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "nestingLevel": { + "description": "An integer representing a containment hierarchy within the thread flow.", + "type": "integer", + "minimum": 0 + }, + + "executionOrder": { + "description": "An integer representing the temporal order in which execution reached this location.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "executionTimeUtc": { + "description": "The Coordinated Universal Time (UTC) date and time at which this location was executed.", + "type": "string", + "format": "date-time" + }, + + "importance": { + "description": "Specifies the importance of this location in understanding the code flow in which it occurs. The order from most to least important is \"essential\", \"important\", \"unimportant\". Default: \"important\".", + "enum": [ "important", "essential", "unimportant" ], + "default": "important" + }, + + "webRequest": { + "description": "A web request associated with this thread flow location.", + "$ref": "#/definitions/webRequest" + }, + + "webResponse": { + "description": "A web response associated with this thread flow location.", + "$ref": "#/definitions/webResponse" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the threadflow location.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "tool": { + "description": "The analysis tool that was run.", + "additionalProperties": false, + "type": "object", + "properties": { + + "driver": { + "description": "The analysis tool that was run.", + "$ref": "#/definitions/toolComponent" + }, + + "extensions": { + "description": "Tool extensions that contributed to or reconfigured the analysis tool that was run.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponent" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the tool.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "driver" ] + }, + + "toolComponent": { + "description": "A component, such as a plug-in or the driver, of the analysis tool that was run.", + "additionalProperties": false, + "type": "object", + "properties": { + + "guid": { + "description": "A unique identifer for the tool component in the form of a GUID.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "name": { + "description": "The name of the tool component.", + "type": "string" + }, + + "organization": { + "description": "The organization or company that produced the tool component.", + "type": "string" + }, + + "product": { + "description": "A product suite to which the tool component belongs.", + "type": "string" + }, + + "productSuite": { + "description": "A localizable string containing the name of the suite of products to which the tool component belongs.", + "type": "string" + }, + + "shortDescription": { + "description": "A brief description of the tool component.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "fullDescription": { + "description": "A comprehensive description of the tool component.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "fullName": { + "description": "The name of the tool component along with its version and any other useful identifying information, such as its locale.", + "type": "string" + }, + + "version": { + "description": "The tool component version, in whatever format the component natively provides.", + "type": "string" + }, + + "semanticVersion": { + "description": "The tool component version in the format specified by Semantic Versioning 2.0.", + "type": "string" + }, + + "dottedQuadFileVersion": { + "description": "The binary version of the tool component's primary executable file expressed as four non-negative integers separated by a period (for operating systems that express file versions in this way).", + "type": "string", + "pattern": "[0-9]+(\\.[0-9]+){3}" + }, + + "releaseDateUtc": { + "description": "A string specifying the UTC date (and optionally, the time) of the component's release.", + "type": "string" + }, + + "downloadUri": { + "description": "The absolute URI from which the tool component can be downloaded.", + "type": "string", + "format": "uri" + }, + + "informationUri": { + "description": "The absolute URI at which information about this version of the tool component can be found.", + "type": "string", + "format": "uri" + }, + + "globalMessageStrings": { + "description": "A dictionary, each of whose keys is a resource identifier and each of whose values is a multiformatMessageString object, which holds message strings in plain text and (optionally) Markdown format. The strings can include placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/multiformatMessageString" + } + }, + + "notifications": { + "description": "An array of reportingDescriptor objects relevant to the notifications related to the configuration and runtime execution of the tool component.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/reportingDescriptor" + } + }, + + "rules": { + "description": "An array of reportingDescriptor objects relevant to the analysis performed by the tool component.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/reportingDescriptor" + } + }, + + "taxa": { + "description": "An array of reportingDescriptor objects relevant to the definitions of both standalone and tool-defined taxonomies.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/reportingDescriptor" + } + }, + + "locations": { + "description": "An array of the artifactLocation objects associated with the tool component.", + "type": "array", + "minItems": 0, + "default": [], + "items": { + "$ref": "#/definitions/artifactLocation" + } + }, + + "language": { + "description": "The language of the messages emitted into the log file during this run (expressed as an ISO 639-1 two-letter lowercase language code) and an optional region (expressed as an ISO 3166-1 two-letter uppercase subculture code associated with a country or region). The casing is recommended but not required (in order for this data to conform to RFC5646).", + "type": "string", + "default": "en-US", + "pattern": "^[a-zA-Z]{2}|^[a-zA-Z]{2}-[a-zA-Z]{2}]?$" + }, + + "contents": { + "description": "The kinds of data contained in this object.", + "type": "array", + "uniqueItems": true, + "default": [ "localizedData", "nonLocalizedData" ], + "items": { + "enum": [ + "localizedData", + "nonLocalizedData" + ] + } + }, + + "isComprehensive": { + "description": "Specifies whether this object contains a complete definition of the localizable and/or non-localizable data for this component, as opposed to including only data that is relevant to the results persisted to this log file.", + "type": "boolean", + "default": false + }, + + "localizedDataSemanticVersion": { + "description": "The semantic version of the localized strings defined in this component; maintained by components that provide translations.", + "type": "string" + }, + + "minimumRequiredLocalizedDataSemanticVersion": { + "description": "The minimum value of localizedDataSemanticVersion required in translations consumed by this component; used by components that consume translations.", + "type": "string" + }, + + "associatedComponent": { + "description": "The component which is strongly associated with this component. For a translation, this refers to the component which has been translated. For an extension, this is the driver that provides the extension's plugin model.", + "$ref": "#/definitions/toolComponentReference" + }, + + "translationMetadata": { + "description": "Translation metadata, required for a translation, not populated by other component types.", + "$ref": "#/definitions/translationMetadata" + }, + + "supportedTaxonomies": { + "description": "An array of toolComponentReference objects to declare the taxonomies supported by the tool component.", + "type": "array", + "minItems": 0, + "uniqueItems": true, + "default": [], + "items": { + "$ref": "#/definitions/toolComponentReference" + } + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the tool component.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "name" ] + }, + + "toolComponentReference": { + "description": "Identifies a particular toolComponent object, either the driver or an extension.", + "type": "object", + "additionalProperties": false, + "properties": { + + "name": { + "description": "The 'name' property of the referenced toolComponent.", + "type": "string" + }, + + "index": { + "description": "An index into the referenced toolComponent in tool.extensions.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "guid": { + "description": "The 'guid' property of the referenced toolComponent.", + "type": "string", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the toolComponentReference.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "translationMetadata": { + "description": "Provides additional metadata related to translation.", + "type": "object", + "additionalProperties": false, + "properties": { + + "name": { + "description": "The name associated with the translation metadata.", + "type": "string" + }, + + "fullName": { + "description": "The full name associated with the translation metadata.", + "type": "string" + }, + + "shortDescription": { + "description": "A brief description of the translation metadata.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "fullDescription": { + "description": "A comprehensive description of the translation metadata.", + "$ref": "#/definitions/multiformatMessageString" + }, + + "downloadUri": { + "description": "The absolute URI from which the translation metadata can be downloaded.", + "type": "string", + "format": "uri" + }, + + "informationUri": { + "description": "The absolute URI from which information related to the translation metadata can be downloaded.", + "type": "string", + "format": "uri" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the translation metadata.", + "$ref": "#/definitions/propertyBag" + } + }, + "required": [ "name" ] + }, + + "versionControlDetails": { + "description": "Specifies the information necessary to retrieve a desired revision from a version control system.", + "type": "object", + "additionalProperties": false, + "properties": { + + "repositoryUri": { + "description": "The absolute URI of the repository.", + "type": "string", + "format": "uri" + }, + + "revisionId": { + "description": "A string that uniquely and permanently identifies the revision within the repository.", + "type": "string" + }, + + "branch": { + "description": "The name of a branch containing the revision.", + "type": "string" + }, + + "revisionTag": { + "description": "A tag that has been applied to the revision.", + "type": "string" + }, + + "asOfTimeUtc": { + "description": "A Coordinated Universal Time (UTC) date and time that can be used to synchronize an enlistment to the state of the repository at that time.", + "type": "string", + "format": "date-time" + }, + + "mappedTo": { + "description": "The location in the local file system to which the root of the repository was mapped at the time of the analysis.", + "$ref": "#/definitions/artifactLocation" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the version control details.", + "$ref": "#/definitions/propertyBag" + } + }, + + "required": [ "repositoryUri" ] + }, + + "webRequest": { + "description": "Describes an HTTP request.", + "type": "object", + "additionalProperties": false, + "properties": { + + "index": { + "description": "The index within the run.webRequests array of the request object associated with this result.", + "type": "integer", + "default": -1, + "minimum": -1 + + }, + + "protocol": { + "description": "The request protocol. Example: 'http'.", + "type": "string" + }, + + "version": { + "description": "The request version. Example: '1.1'.", + "type": "string" + }, + + "target": { + "description": "The target of the request.", + "type": "string" + }, + + "method": { + "description": "The HTTP method. Well-known values are 'GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT'.", + "type": "string" + }, + + "headers": { + "description": "The request headers.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "parameters": { + "description": "The request parameters.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "body": { + "description": "The body of the request.", + "$ref": "#/definitions/artifactContent" + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the request.", + "$ref": "#/definitions/propertyBag" + } + } + }, + + "webResponse": { + "description": "Describes the response to an HTTP request.", + "type": "object", + "additionalProperties": false, + "properties": { + + "index": { + "description": "The index within the run.webResponses array of the response object associated with this result.", + "type": "integer", + "default": -1, + "minimum": -1 + }, + + "protocol": { + "description": "The response protocol. Example: 'http'.", + "type": "string" + }, + + "version": { + "description": "The response version. Example: '1.1'.", + "type": "string" + }, + + "statusCode": { + "description": "The response status code. Example: 451.", + "type": "integer" + }, + + "reasonPhrase": { + "description": "The response reason. Example: 'Not found'.", + "type": "string" + }, + + "headers": { + "description": "The response headers.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + + "body": { + "description": "The body of the response.", + "$ref": "#/definitions/artifactContent" + }, + + "noResponseReceived": { + "description": "Specifies whether a response was received from the server.", + "type": "boolean", + "default": false + }, + + "properties": { + "description": "Key/value pairs that provide additional information about the response.", + "$ref": "#/definitions/propertyBag" + } + } + } + } +} diff --git a/scripts/PSCodingStandards/CodingStandards.psm1 b/scripts/PSCodingStandards/CodingStandards.psm1 index 65d3fe38f6..bc1590ae9d 100644 --- a/scripts/PSCodingStandards/CodingStandards.psm1 +++ b/scripts/PSCodingStandards/CodingStandards.psm1 @@ -1,4 +1,4 @@ -$Functions = @( Get-ChildItem -Path $PSScriptRoot\*.ps1 -ErrorAction SilentlyContinue ) +$Functions = @( Get-ChildItem -Path $PSScriptRoot/*.ps1 -ErrorAction SilentlyContinue ) foreach($i in $Functions){ . $i.FullName @@ -8,4 +8,8 @@ foreach($i in $Functions){ Export-ModuleMember -Function $Functions.BaseName +Write-Host "Importing Configuration.... " +Export-ModuleMember -Variable AVAILABLE_SUITES +Export-ModuleMember -Variable AVAILABLE_LANGUAGES + Write-Host "IMPORTING " \ No newline at end of file diff --git a/scripts/PSCodingStandards/Config.ps1 b/scripts/PSCodingStandards/Config.ps1 new file mode 100644 index 0000000000..53605c89f3 --- /dev/null +++ b/scripts/PSCodingStandards/Config.ps1 @@ -0,0 +1,2 @@ +$AVAILABLE_SUITES = @("CERT-C++", "AUTOSAR", "MISRA-C-2012", "CERT-C", "MISRA-C++-2023") +$AVAILABLE_LANGUAGES = @("c", "cpp") \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-ATestDirectory.ps1 b/scripts/PSCodingStandards/Get-ATestDirectory.ps1 new file mode 100644 index 0000000000..8b7a1517d3 --- /dev/null +++ b/scripts/PSCodingStandards/Get-ATestDirectory.ps1 @@ -0,0 +1,20 @@ +function Get-ATestDirectory { + param( + [Parameter(Mandatory)] + [object] + $RuleObject, + [Parameter(Mandatory)] + [ValidateSet('c', 'cpp')] + [string] + $Language + ) + + $ruleDir = Get-TestDirectory -RuleObject $RuleObject -Language $Language + + # return value MUST include the explicit test directory + $dirs = @($ruleDir) + + $dirs += (Get-Item "$($ruleDir).*" | ForEach-Object { $_.FullName }) + + $dirs +} diff --git a/scripts/PSCodingStandards/Get-LanguageForPath.ps1 b/scripts/PSCodingStandards/Get-LanguageForPath.ps1 new file mode 100644 index 0000000000..7ffebd0ccb --- /dev/null +++ b/scripts/PSCodingStandards/Get-LanguageForPath.ps1 @@ -0,0 +1,17 @@ +function Get-LanguageForPath { + param([Parameter(Mandatory)] + [string] + $Path) + + $parts = $Path -split '/' + + $Language = $parts[0] + + foreach($L in $AVAILABLE_LANGUAGES){ + if($Language -eq $L){ + return $L + } + } + + throw "Unsupported Language: $Language" +} \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-Packages.ps1 b/scripts/PSCodingStandards/Get-Packages.ps1 index 980766b78d..db29206272 100644 --- a/scripts/PSCodingStandards/Get-Packages.ps1 +++ b/scripts/PSCodingStandards/Get-Packages.ps1 @@ -4,5 +4,5 @@ function Get-Packages { [string] $Language) - return Get-ChildItem (Join-Path (Get-RepositoryRoot) "\rule_packages\$Language\*.json") + return Get-ChildItem (Join-Path (Get-RepositoryRoot) "/rule_packages/$Language/*.json") } \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-RuleForPath.ps1 b/scripts/PSCodingStandards/Get-RuleForPath.ps1 new file mode 100644 index 0000000000..34e927e854 --- /dev/null +++ b/scripts/PSCodingStandards/Get-RuleForPath.ps1 @@ -0,0 +1,84 @@ +# takes paths like this: +# c/cert/src/rules/DCL39-C/InformationLeakageAcrossTrustBoundariesC.ql +# c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected +# c/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql +# c/common/test/rules/informationleakageacrossboundaries/arrays.c +# c/common/test/rules/informationleakageacrossboundaries/interprocedural.c +# c/common/test/rules/informationleakageacrossboundaries/multilayer.c +# c/common/test/rules/informationleakageacrossboundaries/test.c +# c/misra/src/rules/RULE-18-8/VariableLengthArrayTypesUsed.ql +# c/misra/src/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.ql +# c/misra/test/rules/RULE-18-8/VariableLengthArrayTypesUsed.expected +# c/misra/test/rules/RULE-18-8/test.c +# c/misra/test/rules/RULE-8-12/ValueImplicitEnumerationConstantNotUnique.expected +# c/misra/test/rules/RULE-8-12/test.c +# cpp/cert/src/rules/DCL55-CPP/InformationLeakageAcrossTrustBoundaries.ql +# cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.expected +# cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossBoundaries.ql +# cpp/common/test/rules/informationleakageacrossboundaries/InformationLeakageAcrossTrustBoundaries.expected +# cpp/common/test/rules/informationleakageacrossboundaries/arrays.cpp +# cpp/common/test/rules/informationleakageacrossboundaries/inheritance.cpp +# cpp/common/test/rules/informationleakageacrossboundaries/interprocedural.cpp +# cpp/common/test/rules/informationleakageacrossboundaries/multilayer.cpp +# cpp/common/test/rules/informationleakageacrossboundaries/test.cpp + +# And produces one or more rules for it. It does this by loading every rule +# and computing the test directory for it. This test directory is then +# used to see if a) it is a substring of the supplied path or if b) it +# is a substring of the path once the substitution `/src/` -> `/test/` is +# applied + +function Get-RuleForPath { + param([Parameter(Mandatory)] + [string] + $Path, + [ValidateSet('c', 'cpp')] + [string] + $Language + ) + + # load all the queries for all languages + $allQueries = @() + $queriesToCheck = @() + + + foreach ($s in $AVAILABLE_SUITES) { + $allQueries += Get-RulesInSuite -Suite $s -Language $Language + } + + + $modifiedPathWithReplacement = Join-Path (Resolve-Path . -Relative) $Path + # replace "src" with "test" to make it match up + $sep = [IO.Path]::DirectorySeparatorChar + $modifiedPathWithReplacement = $modifiedPathWithReplacement.Replace( ($sep + "src" + $sep + "rules"), ($sep + "test" + $sep + "rules")) + $modifiedPath = Join-Path (Resolve-Path . -Relative) $Path + + + $matchingRules = @() + + # for each query, create the test directory + foreach($q in $allQueries){ + # get test directory + $testDirs = (Get-ATestDirectory -RuleObject $q -Language $Language) + foreach($testDirectory in $testDirs){ + # resolve path to be compatible + $testPath = (Join-Path (Resolve-Path . -Relative) $testDirectory) + + if((Split-Path $modifiedPath -Parent) -eq $testPath){ + $matchingRules += $q + continue + } + + if((Split-Path $modifiedPathWithReplacement -Parent) -eq $testPath){ + $matchingRules += $q + continue + } + } + } + + if($matchingRules.Count -gt 0){ + return $matchingRules + } + + throw "Path does not appear to be part of a rule." +} \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-RulesFromCSV.ps1 b/scripts/PSCodingStandards/Get-RulesFromCSV.ps1 new file mode 100644 index 0000000000..54c29668e4 --- /dev/null +++ b/scripts/PSCodingStandards/Get-RulesFromCSV.ps1 @@ -0,0 +1,29 @@ +function Get-RulesFromCSV { + param( + [ValidateSet('c', 'cpp', 'all')] + [string] + $Language = 'all') + + + $csvFile = (Join-Path (Get-RepositoryRoot) "rules.csv") + + Write-Host "Loading rules for language=$Language from file $csvFile..." + + $csv = Import-Csv $csvFile + $filteredCSV = @() + # don't filter if not neeeded + if ($Language -eq 'all'){ + $filteredCSV = $csv + }else{ + foreach($rule in $csv){ + if($rule.Language -eq $Language){ + $filteredCSV += $rule + } + } + } + + Write-Host "Loaded $($filteredCSV.Length) rules." + + return $csv + +} \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-RulesInPackageAndSuite.ps1 b/scripts/PSCodingStandards/Get-RulesInPackageAndSuite.ps1 index 95c041924e..36643c455c 100644 --- a/scripts/PSCodingStandards/Get-RulesInPackageAndSuite.ps1 +++ b/scripts/PSCodingStandards/Get-RulesInPackageAndSuite.ps1 @@ -6,8 +6,12 @@ function Get-RulesInPackageAndSuite { $Package, [Parameter(Mandatory)] [string] - $Suite - ) + $Suite, + [Parameter(Mandatory)] + [ValidateSet('c', 'cpp')] + [string] + $Language + ) $rulesInPackage = @() @@ -30,6 +34,7 @@ function Get-RulesInPackageAndSuite { $queries | Add-Member -NotePropertyName __memberof_suite -NotePropertyValue $Suite $queries | Add-Member -NotePropertyName __memberof_package -NotePropertyValue $Package.BaseName $queries | Add-Member -NotePropertyName __memberof_rule -NotePropertyValue $n.Name + $queries | Add-Member -NotePropertyName __memberof_language -NotePropertyValue $Language $rulesInPackage += $queries } diff --git a/scripts/PSCodingStandards/Get-RulesInSuite.ps1 b/scripts/PSCodingStandards/Get-RulesInSuite.ps1 index f0683e34a1..ad117f8d8a 100644 --- a/scripts/PSCodingStandards/Get-RulesInSuite.ps1 +++ b/scripts/PSCodingStandards/Get-RulesInSuite.ps1 @@ -13,7 +13,7 @@ function Get-RulesInSuite { foreach ($p in Get-Packages -Language $Language) { Write-Host "Reading package: [$Language/$Suite/$($p.BaseName)]" - $tmpQueries += Get-RulesInPackageAndSuite -Package $p -Suite $Suite + $tmpQueries += Get-RulesInPackageAndSuite -Package $p -Suite $Suite -Language $Language } return $tmpQueries diff --git a/scripts/PSCodingStandards/Get-TestDirectory.ps1 b/scripts/PSCodingStandards/Get-TestDirectory.ps1 index 341cb3d7d9..154a49dabe 100644 --- a/scripts/PSCodingStandards/Get-TestDirectory.ps1 +++ b/scripts/PSCodingStandards/Get-TestDirectory.ps1 @@ -27,6 +27,9 @@ function Get-TestDirectory { elseif ($RuleObject.__memberof_suite -eq "MISRA-C-2012") { $standardString = "misra" } + elseif ($RuleObject.__memberof_suite -eq "MISRA-C++-2023") { + $standardString = "misra" + } else { throw "Unknown standard $($RuleObject.__memberof_suite)" } diff --git a/scripts/PSCodingStandards/README.md b/scripts/PSCodingStandards/README.md index 633dd42ee0..954547f4f8 100644 --- a/scripts/PSCodingStandards/README.md +++ b/scripts/PSCodingStandards/README.md @@ -3,13 +3,13 @@ To use this on the command line, do: ``` -Import-Module -Name .\scripts\PSCodingStandards\CodingStandards +Import-Module -Name ./scripts/PSCodingStandards/CodingStandards ``` To use this in your scripts you can do: ``` -Import-Module -Name "$PSScriptRoot\..\PSCodingStandards\CodingStandards" +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" ``` # Summary of Available Commands diff --git a/scripts/PSCodingStandards/Test-GetRuleForPath.ps1 b/scripts/PSCodingStandards/Test-GetRuleForPath.ps1 new file mode 100644 index 0000000000..b3c78cc3dc --- /dev/null +++ b/scripts/PSCodingStandards/Test-GetRuleForPath.ps1 @@ -0,0 +1,39 @@ +function Test-GetRuleForPath { +param( + [Parameter(Mandatory)] + [string] + $PR +) + +$prData = (gh pr view -R github/codeql-coding-standards $PR --json headRefOid,headRepository,author,isCrossRepository,headRepositoryOwner,headRefName,files) | ConvertFrom-Json + +foreach($f in $prData.files){ + try { + Write-Host "[C] Scanning file for relationship $($f.path)..." + $rulesToTest = Get-RuleForPath -Language c -Path "$($f.path)" + + Write-Host "[C] Got $($rulesToTest.Count) potential C rules..." + + foreach($r in $rulesToTest){ + $ruleNames += $r.__memberof_rule + Write-Host "[C] Found rule $r " + } + }catch{ + Write-Host "No $Language rules found for path: $($f.path)" + } + + + try { + Write-Host "[CPP] Scanning file for relationship $($f.path)..." + $rulesToTest = Get-RuleForPath -Language cpp -Path "$($f.path)" + + Write-Host "[CPP] Got $($rulesToTest.Count) potential CPP rules..." + + foreach($r in $rulesToTest){ + Write-Host "[CPP] Found rule $r " + } + }catch{ + Write-Host "No CPP rules found for path: $($f.path)" + } +} +} \ No newline at end of file diff --git a/scripts/add_risk_assessment_tags.py b/scripts/add_risk_assessment_tags.py new file mode 100644 index 0000000000..6560d82a44 --- /dev/null +++ b/scripts/add_risk_assessment_tags.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" +Add risk assessment tags to rule package JSON files. + +This script: +1. Iterates through each JSON file in rule_packages directory +2. Looks for CERT-C or CERT-CPP sections +3. For each rule, finds the corresponding markdown file +4. Extracts risk assessment data from the markdown file +5. Adds risk assessment data as tags to each query in the JSON file +""" + +import os +import json +import re +import glob +from bs4 import BeautifulSoup +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +def find_rule_packages(): + """Find all JSON rule package files in the rule_packages directory.""" + repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + rule_packages_dir = os.path.join(repo_root, "rule_packages") + return glob.glob(os.path.join(rule_packages_dir, "**", "*.json"), recursive=True) + +def extract_risk_assessment_from_md(md_file_path): + """Extract risk assessment data from the markdown file.""" + risk_data = {} + + try: + with open(md_file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Find the Risk Assessment section + risk_section_match = re.search(r'## Risk Assessment(.*?)##', content, re.DOTALL) + if not risk_section_match: + # Try to find it as the last section + risk_section_match = re.search(r'## Risk Assessment(.*?)$', content, re.DOTALL) + if not risk_section_match: + logger.warning(f"No Risk Assessment section found in {md_file_path}") + return risk_data + + risk_section = risk_section_match.group(1) + + # Look for the table with risk assessment data + table_match = re.search(r'(.*?)
', risk_section, re.DOTALL) + if not table_match: + logger.warning(f"No risk assessment table found in {md_file_path}") + return risk_data + + table_html = table_match.group(0) + soup = BeautifulSoup(table_html, 'html.parser') + + # Find all rows in the table + rows = soup.find_all('tr') + if len(rows) < 2: # Need at least header and data row + logger.warning(f"Incomplete risk assessment table in {md_file_path}") + return risk_data + + # Extract headers and values + headers = [th.get_text().strip() for th in rows[0].find_all('th')] + values = [td.get_text().strip() for td in rows[1].find_all('td')] + + # Create a dictionary of headers and values + if len(headers) == len(values): + for i, header in enumerate(headers): + risk_data[header] = values[i] + else: + logger.warning(f"Header and value count mismatch in {md_file_path}") + + except Exception as e: + logger.error(f"Error extracting risk assessment from {md_file_path}: {e}") + + return risk_data + +def find_md_file(rule_id, short_name, language): + """Find the markdown file for the given rule ID and short name.""" + repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + md_path = os.path.join(repo_root, language, "cert", "src", "rules", rule_id, f"{short_name}.md") + + if os.path.exists(md_path): + return md_path + else: + # Try without short name (sometimes the file is named after the rule ID) + md_path = os.path.join(repo_root, language, "cert", "src", "rules", rule_id, f"{rule_id}.md") + if os.path.exists(md_path): + return md_path + else: + logger.warning(f"Could not find markdown file for {language} rule {rule_id} ({short_name})") + return None + +def process_rule_package(rule_package_file): + """Process a single rule package JSON file.""" + try: + with open(rule_package_file, 'r', encoding='utf-8') as f: + data = json.load(f) + + modified = False + + # Look for CERT-C and CERT-CPP sections + for cert_key in ["CERT-C", "CERT-C++"]: + if cert_key in data: + language = "c" if cert_key == "CERT-C" else "cpp" + + # Process each rule in the CERT section + for rule_id, rule_data in data[cert_key].items(): + if "queries" in rule_data: + for query in rule_data["queries"]: + if "short_name" in query: + md_file = find_md_file(rule_id, query["short_name"], language) + + if md_file: + risk_data = extract_risk_assessment_from_md(md_file) + + if risk_data: + # Add risk assessment data as tags + if "tags" not in query: + query["tags"] = [] + + # Add each risk assessment property as a tag + for key, value in risk_data.items(): + key_sanitized = key.lower().replace(" ", "-") + if key_sanitized == "rule" or key_sanitized == "recommendation": + # skip rule/recommendation as they just repeat the rule ID + continue + tag = f"external/cert/{key_sanitized}/{value.lower()}" + if tag not in query["tags"]: + query["tags"].append(tag) + modified = True + logger.info(f"Added tag {tag} to {rule_id} ({query['short_name']})") + + # Save the modified data back to the file if any changes were made + if modified: + with open(rule_package_file, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=2) + logger.info(f"Updated {rule_package_file}") + else: + logger.info(f"No changes made to {rule_package_file}") + + except Exception as e: + logger.error(f"Error processing {rule_package_file}: {e}") + +def main(): + """Main function to process all rule packages.""" + logger.info("Starting risk assessment tag addition process") + + rule_packages = find_rule_packages() + logger.info(f"Found {len(rule_packages)} rule package files") + + for rule_package in rule_packages: + logger.info(f"Processing {rule_package}") + process_rule_package(rule_package) + + logger.info("Completed risk assessment tag addition process") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/build_test_database.py b/scripts/build_test_database.py index 3f8acde999..c2c17092a2 100644 --- a/scripts/build_test_database.py +++ b/scripts/build_test_database.py @@ -3,14 +3,42 @@ import os import subprocess import json +from pathlib import Path -if len(sys.argv) < 4: - print ("Usage: build_test_database.py LANGUAGE STANDARD RULE", file=sys.stderr) +if len(sys.argv) != 4 and len(sys.argv) != 2: + print ("Usage: build_test_database.py TEST_FILE | LANGUAGE STANDARD RULE", file=sys.stderr) exit(1) -LANGUAGE=sys.argv[1] -STANDARD=sys.argv[2] -RULE=sys.argv[3] +if len(sys.argv) == 4: + LANGUAGE=sys.argv[1] + STANDARD=sys.argv[2] + RULE=sys.argv[3] + +if len(sys.argv) == 2: + TEST_FILE_PATH=Path(sys.argv[1]) + if not TEST_FILE_PATH.exists(): + print(f"The test file {TEST_FILE_PATH} does not exist!", file=sys.stderr) + exit(1) + RULE_PATH=TEST_FILE_PATH.parent + while True: + if len(list(RULE_PATH.glob("*.expected"))) > 0: + break + if RULE_PATH.parent != RULE_PATH: + RULE_PATH = RULE_PATH.parent + else: + print(f"The test file {TEST_FILE_PATH} is not a test because we couldn't find an expected file!", file=sys.stderr) + exit(1) + RULE=RULE_PATH.name + TESTS_PATH=RULE_PATH.parent.parent + if TESTS_PATH.name != "test": + print(f"The test file {TEST_FILE_PATH} is not in the expected test layout, cannot determine standard or language!", file=sys.stderr) + exit(1) + + STANDARD_PATH=TESTS_PATH.parent + STANDARD=STANDARD_PATH.name + + LANGUAGE_PATH=STANDARD_PATH.parent + LANGUAGE=LANGUAGE_PATH.name if shutil.which("codeql") is None: print ("Please install codeql.", file=sys.stderr) diff --git a/scripts/bump_version.sh b/scripts/bump_version.sh deleted file mode 100644 index ea46f73f72..0000000000 --- a/scripts/bump_version.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - - -if [[ -z $1 ]]; -then - echo "Usage: bump_version.sh " - exit -fi - -echo "Setting Local Branch Version to $1." - -find . -name 'qlpack.yml' | grep -v './codeql_modules' | grep -v './scripts' | xargs sed -i "s/^version.*$/version: ${1}/" - -echo "Done." \ No newline at end of file diff --git a/scripts/deviations/process_coding_standards_config.py b/scripts/configuration/process_coding_standards_config.py similarity index 100% rename from scripts/deviations/process_coding_standards_config.py rename to scripts/configuration/process_coding_standards_config.py diff --git a/scripts/deviations/requirements.txt b/scripts/configuration/requirements.txt similarity index 100% rename from scripts/deviations/requirements.txt rename to scripts/configuration/requirements.txt diff --git a/scripts/generate_metadata/templates/rulemetadata.qll.template b/scripts/generate_metadata/templates/rulemetadata.qll.template index 802fa44641..64b903f5c2 100644 --- a/scripts/generate_metadata/templates/rulemetadata.qll.template +++ b/scripts/generate_metadata/templates/rulemetadata.qll.template @@ -18,9 +18,9 @@ newtype T{{ language_name.upper() }}Query = {% endif %} /** The metadata predicate * */ -predicate isQueryMetadata(Query query, string queryId, string ruleId) { +predicate isQueryMetadata(Query query, string queryId, string ruleId, string category) { {% for package in packages %} - is{{ package }}QueryMetadata(query, queryId, ruleId){% if not loop.last %} or + is{{ package }}QueryMetadata(query, queryId, ruleId, category){% if not loop.last %} or {% endif %}{% endfor %} } diff --git a/scripts/generate_modules/queries/codeql-pack.lock.yml b/scripts/generate_modules/queries/codeql-pack.lock.yml new file mode 100644 index 0000000000..a45ea8f438 --- /dev/null +++ b/scripts/generate_modules/queries/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/scripts/generate_modules/queries/qlpack.yml b/scripts/generate_modules/queries/qlpack.yml index d165177f23..9aabee2562 100644 --- a/scripts/generate_modules/queries/qlpack.yml +++ b/scripts/generate_modules/queries/qlpack.yml @@ -1,3 +1,5 @@ -name: standard-library-extraction-cpp-coding-standards +name: codeql/standard-library-extraction-cpp-coding-standards version: 0.0.0 -libraryPathDependencies: codeql-cpp \ No newline at end of file +license: MIT +dependencies: + codeql/cpp-all: 4.0.3 diff --git a/scripts/generate_rules/coding_standards_utils.py b/scripts/generate_rules/coding_standards_utils.py index ca3410c311..b0bcd48443 100644 --- a/scripts/generate_rules/coding_standards_utils.py +++ b/scripts/generate_rules/coding_standards_utils.py @@ -8,16 +8,21 @@ import tempfile import sys - # Add the shared module to the path script_path = Path(__file__) sys.path.append(str(script_path.parent.parent / 'shared')) +from codeql import CodeQL, CodeQLError from markdown_helpers import HeadingFormatUpdateSpec, update_help_file, HeadingDiffUpdateSpec +# Global holding an instance of CodeQL that can be shared too prevent repeated instantiation costs. +codeql = None + def split_camel_case(short_name : str) -> List[str]: """Split a camel case string to a list.""" + # Edge case, turn FooNaNBar into foo-nan-bar instead of foo-na-n-bar by a preprocessing step. + nan_fixed = short_name.replace("NaN", "Nan") matches = re.finditer( - ".+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)", short_name) + ".+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)", nan_fixed) return [m.group(0) for m in matches] @@ -56,7 +61,6 @@ def render_template(template: Type[Template], args: Dict[str, str], package_name output = template.render(args, package_name=package_name) file.write(output) - def write_exclusion_template(template: Type[Template], args: Dict[str, str], package_name: str, language_name: str, file: TextIO): """Render the template with the given args, and write it to the file using \n newlines.""" output = template.render( @@ -65,7 +69,14 @@ def write_exclusion_template(template: Type[Template], args: Dict[str, str], pac with open(file, "w", newline="\n") as f: f.write(output) -def extract_metadata_from_query(rule_id, title, q, rule_query_tags, language_name, ql_language_name, standard_name, standard_short_name, standard_metadata, anonymise): + global codeql + if codeql == None: + codeql = CodeQL() + # Format the generated exclusion file because we don't want to handle this in the template. + # The format relies on the length of the package name. + codeql.format(file) + +def extract_metadata_from_query(rule_id, title, rule_category, q, rule_query_tags, language_name, ql_language_name, standard_name, standard_short_name, standard_metadata, anonymise): metadata = q.copy() @@ -92,6 +103,7 @@ def extract_metadata_from_query(rule_id, title, q, rule_query_tags, language_nam exclusion_model["queryname"] = metadata["short_name"] exclusion_model["queryname_camelcase"] = metadata["short_name"][0].lower( ) + metadata["short_name"][1:] + exclusion_model["category"] = rule_category if not "kind" in metadata: # default to problem if not specified diff --git a/scripts/generate_rules/generate_package_description.py b/scripts/generate_rules/generate_package_description.py index d3890e230f..843d3bd78f 100644 --- a/scripts/generate_rules/generate_package_description.py +++ b/scripts/generate_rules/generate_package_description.py @@ -96,7 +96,7 @@ def generate_short_name(title): language = rule[0] standard = rule[1] rule_id = rule[2] - queryable = rule[3] + supportable = rule[3] obligation_level = rule[4] enforcement_level = rule[5] allocated_targets = rule[6] @@ -106,10 +106,12 @@ def generate_short_name(title): difficulty = rule[10] # Find all rules in the given language and package if language == language_name and package == package_name: - if not queryable == "Yes": - print("Error: " + standard + " " + rule_id + " is marked as part of package " + package_name + " but is not marked as queryable.") + if not supportable == "Yes": + print("Error: " + standard + " " + rule_id + " is marked as part of package " + package_name + " but is not marked as supportable.") sys.exit(1) + tags = [] + # Add the AUTOSAR obligation, enforcement and allocated target as query properties. properties = {} if obligation_level: @@ -117,7 +119,15 @@ def generate_short_name(title): if enforcement_level: properties["enforcement"] = enforcement_level.lower() if allocated_targets: - properties["allocated-target"] = [target.strip(' ').lower() for target in allocated_targets.split("/")] + if allocated_targets == "Single Translation Unit": + # MISRA C++ 2023 uses the allocated targets field for scope + tags.append("scope/single-translation-unit") + elif allocated_targets == "System": + # MISRA C++ 2023 uses the allocated targets field for scope + tags.append("scope/system") + else: + properties["allocated-target"] = [target.strip(' ').lower() for target in allocated_targets.split("/")] + if difficulty == "Audit": properties["audit"] = "" @@ -164,7 +174,7 @@ def generate_short_name(title): "severity" : severity, "description" : description, "kind" : "problem", - "tags" : [] + "tags" : tags } ] } @@ -187,11 +197,10 @@ def generate_short_name(title): json.dump(package_description, rule_package_file, indent=2, sort_keys=True) print("Rule package file generated at " + str(rule_package_file_path) + ".") print("") - print("A default query has been generated for each for each rule. Please review each rule in the generated JSON file and:") + print("A default query has been generated for each rule. Please review each rule in the generated JSON file and:") print(" (1) Add additional queries as required") print(" (2) Confirm that the following auto-generated properties are appropriate:") - print(" - 'camel_name'.") print(" - 'precision'.") - print(" - 'query_name'.") + print(" - 'short_name'.") print(" - 'severity'.") print(" (3) Add additional 'tags' as required, particularly 'security' or 'correctness'.") diff --git a/scripts/generate_rules/generate_package_files.py b/scripts/generate_rules/generate_package_files.py index 8f2f23f025..862ccfdc1e 100644 --- a/scripts/generate_rules/generate_package_files.py +++ b/scripts/generate_rules/generate_package_files.py @@ -49,7 +49,7 @@ }, "AUTOSAR" : { "standard_title" : "AUTOSAR: Guidelines for the use of the C++14 language in critical and safety-related systems", - "standard_url" : "https://www.autosar.org/fileadmin/user_upload/standards/adaptive/19-11/AUTOSAR_RS_CPP14Guidelines.pdf" + "standard_url" : "https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf" }, "CERT-C" : { "standard_title" : "CERT-C", @@ -58,11 +58,15 @@ "MISRA-C-2012" : { "standard_title" : "MISRA-C:2012 Guidelines for the use of the C language in critical systems", "standard_url" : "https://www.misra.org.uk/" + }, + "MISRA-C++-2023" : { + "standard_title" : "MISRA C++:2023 Guidelines for the use C++:17 in critical systems", + "standard_url" : "https://misra.org.uk/product/misra-cpp2023/" } } # The help files of these standards cannot be distributed in our repository. -external_help_file_standards = ["AUTOSAR", "MISRA-C-2012"] +external_help_file_standards = ["AUTOSAR", "MISRA-C-2012", "MISRA-C++-2023"] # Mapping from the QL language to source file extension used to generate a help example file. ql_language_ext_mappings = { @@ -80,8 +84,8 @@ help="create anonymized versions of the queries, without identifying rule information", ) # Skip the generation of tests. This is useful when creating releases -# wherein we should preserve the author's intention to not provide c-specific -# test cases. +# wherein we should preserve the author's intention to not provide c-specific +# test cases. parser.add_argument( "--skip-shared-test-generation", action="store_true", @@ -99,36 +103,33 @@ help="directory containing external help files" ) parser.add_argument( - "package_name", help="the name of the package to generate query files for") + "package_names", help="the name of the package to generate query files for", metavar='FILE', nargs='+') ######################################################## args = parser.parse_args() language_name = args.language_name.lower() -package_name = args.package_name -# validate language +# validate language if not language_name in ql_language_mappings: exit(f"Unsupported language '{language_name}'") else: ql_language_name = ql_language_mappings[language_name] -# set up some basic paths +# set up some basic paths repo_root = Path(__file__).parent.parent.parent rule_packages_file_path = repo_root.joinpath("rule_packages") -rule_package_file_path = rule_packages_file_path.joinpath( - language_name, package_name + ".json") env = Environment(loader=FileSystemLoader(Path(__file__).parent.joinpath( "templates")), trim_blocks=True, lstrip_blocks=True) -def write_shared_implementation(package_name, rule_id, query, language_name, ql_language_name, common_src_pack_dir, common_test_pack_dir, skip_tests=False): +def write_shared_implementation(package_name, rule_id, query, language_name, ql_language_name, common_src_pack_dir, common_test_pack_dir, test_src_dir, skip_tests=False): shared_impl_dir_name = query["shared_implementation_short_name"].lower() shared_impl_dir = common_src_pack_dir.joinpath( - "codingstandards", - ql_language_name, - "rules", + "codingstandards", + ql_language_name, + "rules", shared_impl_dir_name ) @@ -138,33 +139,33 @@ def write_shared_implementation(package_name, rule_id, query, language_name, ql_ # # Write out the implementation. Implementations are - # always stored in the `ql_language_name` directory. + # always stored in the `ql_language_name` directory. # if not shared_impl_query_library_path.exists(): - + if len(query["short_name"]) > 50: exit(f"Error: {query['short_name']} has more than 50 characters.") - + shared_library_template = env.get_template( "shared_library.ql.template" ) print(f"{rule_id}: Writing out shared implementation file to {str(shared_impl_query_library_path)}") - + write_template( - shared_library_template, - query, - package_name, + shared_library_template, + query, + package_name, shared_impl_query_library_path ) else: print(f"{rule_id}: Skipping writing shared implementation file to {str(shared_impl_query_library_path)}") # Write out the test. Test are always stored under the `language_name` - # directory. + # directory. if not skip_tests: shared_impl_test_dir = common_test_pack_dir.joinpath( - "rules", + "rules", shared_impl_dir_name ) @@ -174,7 +175,7 @@ def write_shared_implementation(package_name, rule_id, query, language_name, ql_ shared_impl_test_query_path = shared_impl_test_dir.joinpath( f"{query['shared_implementation_short_name']}.ql" ) - + with open(shared_impl_test_query_path, "w", newline="\n") as f: f.write("// GENERATED FILE - DO NOT MODIFY\n") f.write( @@ -184,6 +185,22 @@ def write_shared_implementation(package_name, rule_id, query, language_name, ql_ .replace("/", ".") + "\n" ) + f.write("\n") + class_name = str(query["shared_implementation_short_name"]) + "SharedQuery" + f.write("class TestFileQuery extends " + class_name + ",") + # ql formatting of this line depends on the line length + if len(class_name) > 61: + # Line break required after comma + f.write("\n TestQuery\n{ }\n") + elif len(class_name) == 61: + # Line break required before `{` + f.write(" TestQuery\n{ }\n") + elif len(class_name) > 57: + # Line break required after `{` + f.write(" TestQuery {\n}\n") + else: + # Under 100 characters, can be formatted on the same line + f.write(" TestQuery { }\n") # Create an empty test file, if one doesn't already exist shared_impl_test_dir.joinpath( @@ -202,13 +219,13 @@ def write_shared_implementation(package_name, rule_id, query, language_name, ql_ test_ref_file = test_src_dir.joinpath( query["short_name"] + ".testref") - # don't write it if it already exists + # don't write it if it already exists if not test_ref_file.exists(): with open(test_ref_file, "w", newline="\n") as f: f.write(str(shared_impl_test_query_path.relative_to( repo_root)).replace("\\", "/")) -def write_non_shared_testfiles(query, language_name, query_path, test_src_dir, src_pack_dir): +def write_non_shared_testfiles(rule_id, query, language_name, query_path, test_src_dir, src_pack_dir): # Add qlref test file print( rule_id + ": Writing out query test files to " + str(test_src_dir)) @@ -221,181 +238,197 @@ def write_non_shared_testfiles(query, language_name, query_path, test_src_dir, s expected_results_file = test_src_dir.joinpath( f"{query['short_name']}.expected" ) - + if not expected_results_file.exists(): with open(expected_results_file, "w", newline="\n") as f: f.write( "No expected results have yet been specified") -try: - rule_package_file = open(rule_package_file_path, "r") -except PermissionError: - print("Error: No permission to read the rule package file located at '" + - str(rule_package_file_path) + "'") - sys.exit(1) -else: - with rule_package_file: - package_definition = json.load(rule_package_file) - - # Initialize exclusion - exclusion_query = [] - - # Check query standard name is unique before proceeding - query_names = [] - for standard_name, rules in package_definition.items(): - for rule_id, rule_details in rules.items(): - for query in rule_details["queries"]: - query_names.append(query["short_name"]) - if len(query_names) > len(set(query_names)): - print( - "Error: " + "Duplicate query name detected, each query must have a unique query name.") - sys.exit(1) - - for standard_name, rules in package_definition.items(): - - # Identify the short name for the standard, used for directory and tag names - standard_short_name = standard_name.split("-")[0].lower() - # Currently assumes that language_name is also the subdirectory name - standard_dir = repo_root.joinpath( - language_name).joinpath(standard_short_name) - # Identify common src and test packs - common_dir = repo_root.joinpath( - ql_language_name).joinpath("common") - common_src_pack_dir = common_dir.joinpath("src") - # The language specific files always live under the commons for that - # language - common_test_pack_dir = repo_root.joinpath(language_name, "common", "test") - # Identify the source pack for this standard - src_pack_dir = standard_dir.joinpath("src") - for rule_id, rule_details in rules.items(): - # Identify and create the directories required for this rule - rule_src_dir = src_pack_dir.joinpath("rules").joinpath(rule_id) - rule_src_dir.mkdir(exist_ok=True, parents=True) - test_src_dir = standard_dir.joinpath( - "test/rules").joinpath(rule_id) - test_src_dir.mkdir(exist_ok=True, parents=True) - # Build list of tags for this rule to apply to each query - rule_query_tags = [] - for key, value in rule_details["properties"].items(): - if isinstance(value, list): - for v in value: - rule_query_tags.append( - standard_tag(standard_short_name, key, v)) - else: - rule_query_tags.append(standard_tag( - standard_short_name, key, value)) - - for q in rule_details["queries"]: - - # extract metadata and model - query, exclusion_model = extract_metadata_from_query( - rule_id, - rule_details["title"], - q, - rule_query_tags, - language_name, - ql_language_name, - standard_name, - standard_short_name, - standard_metadata, - args.anonymise - ) - # add query to each dict - exclusion_query.append(exclusion_model) - - # Path to query file we want to generate or modify - query_path = rule_src_dir.joinpath( - query["short_name"] + ".ql") - if not query_path.exists(): - # Doesn't already exist, generate full template, including imports and select - if len(query["short_name"]) > 50: - print( - "Error: " + query["short_name"] + " has more than 50 characters. Query name should be less than 50 characters. ") - sys.exit(1) - print(rule_id + ": Writing out query file to " + - str(query_path)) - query_template = env.get_template("query.ql.template") - write_template(query_template, query, - package_name, query_path) - else: - # Query file does already exist, so we only re-write the metadata - print( - rule_id + ": Re-writing metadata for query file at " + str(query_path)) - query_metadata_template = env.get_template( - "query.metadata.template") - # Generate the new metadata - new_metadata = query_metadata_template.render(**query) - with open(query_path, "r+", newline="\n") as query_file: - # Read the existing query file contents - existing_contents = query_file.read() - # Move cursor back to the start of the file, so we can write later - query_file.seek(0) - # Confirm that the query file is valid - if not existing_contents.startswith("/**"): - print("Error: " + " cannot modify the metadata for query file at " + str( - query_path) + " - does not start with /**.") - sys.exit(1) - pos_of_comment_end = existing_contents.find("*/") - if pos_of_comment_end == -1: - print("Error: " + " cannot modify the metadata for query file at " + str( - query_path) + " - does not include a */.") - sys.exit(1) - # Write the new contents to the query file - new_contents = new_metadata + \ - existing_contents[pos_of_comment_end + 2:] - # Write the new contents to the file - query_file.writelines(new_contents) - # Ensure any trailing old data is deleted - query_file.truncate() - - # Add some metadata for each supported standard - if standard_name == "CERT-C++": - query["standard_title"] = "CERT-C++" - query["standard_url"] = "https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046682" - elif standard_name == "AUTOSAR": - query["standard_title"] = "AUTOSAR: Guidelines for the use of the C++14 language in critical and safety-related systems" - query[ - "standard_url" - ] = "https://www.autosar.org/fileadmin/user_upload/standards/adaptive/19-11/AUTOSAR_RS_CPP14Guidelines.pdf" - - help_dir = None - if standard_name in external_help_file_standards: - if args.external_help_dir.is_dir() and args.external_help_dir.exists(): - help_dir = Path(args.external_help_dir).resolve() / (rule_src_dir.relative_to(repo_root)) - help_dir.mkdir(parents=True, exist_ok=True) +def resolve_package(package_name: str) -> Path: + global rule_packages_file_path, language_name + return rule_packages_file_path.joinpath( + language_name, package_name + ".json") + +def generate_package_files(package_name: str) -> None: + global language_name, env + rule_package_file_path = resolve_package(package_name) + print(str(rule_package_file_path)) + try: + rule_package_file = rule_package_file_path.open("r") + except PermissionError: + print("Error: No permission to read the rule package file located at '" + + str(rule_package_file_path) + "'") + sys.exit(1) + else: + with rule_package_file: + package_definition = json.load(rule_package_file) + + # Initialize exclusion + exclusion_query = [] + + # Check query standard name is unique before proceeding + query_names = [] + for standard_name, rules in package_definition.items(): + for rule_id, rule_details in rules.items(): + for query in rule_details["queries"]: + query_names.append(query["short_name"]) + if len(query_names) > len(set(query_names)): + print( + "Error: " + "Duplicate query name detected, each query must have a unique query name.") + sys.exit(1) + + for standard_name, rules in package_definition.items(): + + # Identify the short name for the standard, used for directory and tag names + standard_short_name = standard_name.split("-")[0].lower() + # Currently assumes that language_name is also the subdirectory name + standard_dir = repo_root.joinpath( + language_name).joinpath(standard_short_name) + # Identify common src and test packs + common_dir = repo_root.joinpath( + ql_language_name).joinpath("common") + common_src_pack_dir = common_dir.joinpath("src") + # The language specific files always live under the commons for that + # language + common_test_pack_dir = repo_root.joinpath(language_name, "common", "test") + # Identify the source pack for this standard + src_pack_dir = standard_dir.joinpath("src") + for rule_id, rule_details in rules.items(): + # Identify and create the directories required for this rule + rule_src_dir = src_pack_dir.joinpath("rules").joinpath(rule_id) + rule_src_dir.mkdir(exist_ok=True, parents=True) + test_src_dir = standard_dir.joinpath( + "test/rules").joinpath(rule_id) + test_src_dir.mkdir(exist_ok=True, parents=True) + # Extract the rule category from the obligation property. + assert("properties" in rule_details and "obligation" in rule_details["properties"]) + rule_category = rule_details["properties"]["obligation"] + # Build list of tags for this rule to apply to each query + rule_query_tags = [] + for key, value in rule_details["properties"].items(): + if isinstance(value, list): + for v in value: + rule_query_tags.append( + standard_tag(standard_short_name, key, v)) else: - print(f"{rule_id} : Skipping writing of help file for {query_path} because no existing external help directory is provided!") - else: - help_dir = rule_src_dir - if help_dir: - write_query_help_file(help_dir, env, query, package_name, rule_id, standard_name) - - if "shared_implementation_short_name" in query: - write_shared_implementation(package_name, rule_id, query, language_name, ql_language_name, common_src_pack_dir, common_test_pack_dir, args.skip_shared_test_generation) - else: - write_non_shared_testfiles(query, language_name, query_path, test_src_dir, src_pack_dir) - # Exclusions - exclusions_template = env.get_template("exclusions.qll.template") - common_exclusions_dir = common_src_pack_dir.joinpath( - "codingstandards", - ql_language_name, - "exclusions") - # assign package and sanitize - package_name = package_name.replace("-", "") - package_name = package_name[:1].upper() + package_name[1:] - exclusion_library_file = common_exclusions_dir.joinpath(language_name, - package_name + ".qll") - # write exclusions file - print(package_name + ": Writing out exclusions file to " + - str(exclusion_library_file)) - - os.makedirs(common_exclusions_dir.joinpath( - language_name), exist_ok=True) - - write_exclusion_template(exclusions_template, exclusion_query, - package_name, language_name, exclusion_library_file) + rule_query_tags.append(standard_tag( + standard_short_name, key, value)) + + for q in rule_details["queries"]: + + # extract metadata and model + query, exclusion_model = extract_metadata_from_query( + rule_id, + rule_details["title"], + rule_category, + q, + rule_query_tags, + language_name, + ql_language_name, + standard_name, + standard_short_name, + standard_metadata, + args.anonymise + ) + # add query to each dict + exclusion_query.append(exclusion_model) + + # Path to query file we want to generate or modify + query_path = rule_src_dir.joinpath( + query["short_name"] + ".ql") + if not query_path.exists(): + # Doesn't already exist, generate full template, including imports and select + if len(query["short_name"]) > 50: + print( + "Error: " + query["short_name"] + " has more than 50 characters. Query name should be less than 50 characters. ") + sys.exit(1) + print(rule_id + ": Writing out query file to " + + str(query_path)) + query_template = env.get_template("query.ql.template") + write_template(query_template, query, + package_name, query_path) + else: + # Query file does already exist, so we only re-write the metadata + print( + rule_id + ": Re-writing metadata for query file at " + str(query_path)) + query_metadata_template = env.get_template( + "query.metadata.template") + # Generate the new metadata + new_metadata = query_metadata_template.render(**query) + with open(query_path, "r+", newline="\n") as query_file: + # Read the existing query file contents + existing_contents = query_file.read() + # Move cursor back to the start of the file, so we can write later + query_file.seek(0) + # Confirm that the query file is valid + if not existing_contents.startswith("/**"): + print("Error: " + " cannot modify the metadata for query file at " + str( + query_path) + " - does not start with /**.") + sys.exit(1) + pos_of_comment_end = existing_contents.find("*/") + if pos_of_comment_end == -1: + print("Error: " + " cannot modify the metadata for query file at " + str( + query_path) + " - does not include a */.") + sys.exit(1) + + # Write the new contents to the query file + new_contents = new_metadata + \ + existing_contents[pos_of_comment_end + 2:] + # Write the new contents to the file + query_file.writelines(new_contents) + # Ensure any trailing old data is deleted + query_file.truncate() + + # Add some metadata for each supported standard + if standard_name == "CERT-C++": + query["standard_title"] = "CERT-C++" + query["standard_url"] = "https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046682" + elif standard_name == "AUTOSAR": + query["standard_title"] = "AUTOSAR: Guidelines for the use of the C++14 language in critical and safety-related systems" + query[ + "standard_url" + ] = "https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf" + + help_dir = None + if standard_name in external_help_file_standards: + if args.external_help_dir.is_dir() and args.external_help_dir.exists(): + help_dir = Path(args.external_help_dir).resolve() / (rule_src_dir.relative_to(repo_root)) + help_dir.mkdir(parents=True, exist_ok=True) + else: + print(f"{rule_id} : Skipping writing of help file for {query_path} because no existing external help directory is provided!") + else: + help_dir = rule_src_dir + if help_dir: + write_query_help_file(help_dir, env, query, package_name, rule_id, standard_name) + if "shared_implementation_short_name" in query: + write_shared_implementation(package_name, rule_id, query, language_name, ql_language_name, common_src_pack_dir, common_test_pack_dir, test_src_dir, args.skip_shared_test_generation) + else: + write_non_shared_testfiles(rule_id, query, language_name, query_path, test_src_dir, src_pack_dir) + # Exclusions + exclusions_template = env.get_template("exclusions.qll.template") + common_exclusions_dir = common_src_pack_dir.joinpath( + "codingstandards", + ql_language_name, + "exclusions") + # assign package and sanitize + package_name = package_name.replace("-", "") + package_name = package_name[:1].upper() + package_name[1:] + exclusion_library_file = common_exclusions_dir.joinpath(language_name, + package_name + ".qll") + # write exclusions file + print(package_name + ": Writing out exclusions file to " + + str(exclusion_library_file)) + + os.makedirs(common_exclusions_dir.joinpath( + language_name), exist_ok=True) + + write_exclusion_template(exclusions_template, exclusion_query, + package_name, language_name, exclusion_library_file) + +for package_name in args.package_names: + generate_package_files(package_name) # After updating these files, the metadata should be regenerated print("==========================================================") diff --git a/scripts/generate_rules/templates/exclusions.qll.template b/scripts/generate_rules/templates/exclusions.qll.template index 25d1927722..5d7dc726a3 100644 --- a/scripts/generate_rules/templates/exclusions.qll.template +++ b/scripts/generate_rules/templates/exclusions.qll.template @@ -13,7 +13,7 @@ newtype {{ package_name }}Query = {% endif %} -predicate is{{package_name}}QueryMetadata(Query query, string queryId, string ruleId) { +predicate is{{package_name}}QueryMetadata(Query query, string queryId, string ruleId, string category) { {% for item in data %} query = // `Query` instance for the `{{item['queryname_camelcase']}}` query @@ -21,7 +21,8 @@ predicate is{{package_name}}QueryMetadata(Query query, string queryId, string ru queryId = // `@id` for the `{{item['queryname_camelcase']}}` query "{{ item['queryid'] }}" and - ruleId = "{{ item['ruleid'] }}" + ruleId = "{{ item['ruleid'] }}" and + category = "{{ item['category'] }}" {% if not loop.last %} or {% endif %} diff --git a/scripts/generate_rules/templates/misra-c++-2023-help.md.template b/scripts/generate_rules/templates/misra-c++-2023-help.md.template new file mode 100644 index 0000000000..4fb7b17ee8 --- /dev/null +++ b/scripts/generate_rules/templates/misra-c++-2023-help.md.template @@ -0,0 +1,49 @@ +# {{ rule_id }}: {{ name }} + +This query implements the {{ standard_name | escape }} rule {{ rule_id | escape }}: + +> {{ rule_title }} + +## Classification + +** REPLACE THIS WITH THE CORRECT CLASSIFICATION ** +* required +* implementation +* automated + +## Rationale + +**REPLACE THIS WITH RATIONAL, IF ANY** + +## Exception + +**REPLACE THIS WITH EXCEPTION, IF ANY** + +## Example + +```cpp +// REPLACE THIS WITH C++ EXAMPLE, IF ANY +``` + +## See more + +** REPLACE THIS WITH THE ANY SEE MORE REFERENCES ** + +## Implementation notes + +{% if implementation_scope is defined %} +{{ implementation_scope["description"] }} +{% if implementation_scope["items"] is iterable %} +{% for implementation_scope_entry in implementation_scope["items"] %} +* {{ implementation_scope_entry }} +{% endfor %} +{% endif %} +{% else %} +None +{% endif %} + +## References + +{% if standard_title | length %} +* {{ standard_title | escape }}: [{{ rule_id }}: {{ rule_title }}]({{ standard_url }}) +{% endif %} diff --git a/scripts/generate_rules/templates/shared_library.ql.template b/scripts/generate_rules/templates/shared_library.ql.template index 24431edcc7..93dc503510 100644 --- a/scripts/generate_rules/templates/shared_library.ql.template +++ b/scripts/generate_rules/templates/shared_library.ql.template @@ -1,5 +1,10 @@ +{# + The autogenerated description of the shared query is copied from + the first matching query in `rule_packages`. +#} /** - * Provides a library which includes a `problems` predicate for reporting.... + * Provides a library with a `problems` predicate for the following issue: + * {{ description|join('\n * ') }} */ import cpp diff --git a/scripts/get_workspace_packs.py b/scripts/get_workspace_packs.py new file mode 100644 index 0000000000..fc9054c641 --- /dev/null +++ b/scripts/get_workspace_packs.py @@ -0,0 +1,14 @@ +import glob +import json +import os + +def get_workspace_packs(root): + # Find the packs by globbing using the 'provide' patterns in the manifest. + os.chdir(root) + with open('.codeqlmanifest.json') as manifest_file: + manifest = json.load(manifest_file) + packs = [] + for pattern in manifest['provide']: + packs.extend(glob.glob(pattern, recursive=True)) + + return packs diff --git a/scripts/guideline_recategorization/recategorize.py b/scripts/guideline_recategorization/recategorize.py new file mode 100644 index 0000000000..04d619b2a1 --- /dev/null +++ b/scripts/guideline_recategorization/recategorize.py @@ -0,0 +1,210 @@ +import argparse +import sys +from dataclasses import asdict, dataclass +from typing import Any, Generator, Iterator, List, Mapping, Optional, TextIO, TypedDict, Union, cast +from pathlib import Path +import jsonschema +import json +from jsonpath_ng import jsonpath +import jsonpath_ng.ext +import jsonpointer +import yaml +import yaml.parser +import re +import jsonpatch +from functools import reduce + +CODING_STANDARDS_SCHEMA_ID = 'https://raw.githubusercontent.com/github/codeql-coding-standards/main/schemas/coding-standards-schema-1.0.0.json' +SARIF_SCHEMA_ID = 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json' + +script_path = Path(__file__).resolve() +root_path = script_path.parent.parent.parent + +@dataclass(frozen=True) +class GuidelineRecategorization(): + """ + This is a class to represent a guideline recategorization as specified in a + `coding-standard.yml` configuration. + """ + rule_id: str + category: str + +class JsonPatch(TypedDict): + """ + This is a class that represents a JSON Patch as specified in + https://datatracker.ietf.org/doc/html/rfc6902/. + """ + op: str + path: str + value: str + +def json_path_to_pointer(path: Union[str, jsonpath.JSONPath], subject: Mapping[str, Any]) -> Iterator[jsonpointer.JsonPointer]: + """ Convert a JSON Path to, possible multiple, JSON Pointers""" + if isinstance(path, str): + path = jsonpath_ng.ext.parse(path) + + # Convert a resolved JSON Path to Pointer through the following steps: + # 1. Replace the indexing expression `.[i].` with it's pointer equivalent `/i` with `i` being a positive integer. + translate_indexing = lambda path: re.sub(r'\.\[(\d+)\]', '.\\1', path) + # 2. Split the path in to paths + split_into_parts = lambda path: path.split('.') + # 3. Convert the paths into a JSON pointer. + convert_to_pointer = jsonpointer.JsonPointer.from_parts + + def apply(a, f): + return f(a) + path_to_pointer = lambda p: reduce(apply, [str, translate_indexing, split_into_parts, convert_to_pointer], p) + + return map(path_to_pointer, [match.full_path for match in cast(jsonpath.JSONPath, path).find(subject)]) + +def recategorization_to_json_path_for_rule(recategorization: GuidelineRecategorization) -> str: + """ + Compute a JSON path to the rule specified in the guideline recategorization. + To remain composable the path is returned as a string. + """ + return f'$.runs[?(@.tool.driver.name="CodeQL")].tool.driver.rules[?(@.properties.tags[*]=~"external/[^/]+/id/{recategorization.rule_id.lower()}")]' + +def recategorization_to_json_path_for_category(recategorization: GuidelineRecategorization) -> str: + """ + Compute a JSON path to the rule's category tag specified in the guideline recategorization. + To remain composable the path is returned as a string. + """ + return f'{recategorization_to_json_path_for_rule(recategorization)}.properties.tags[?(@=~"external/[^/]+/obligation/")]' + +def generate_json_patches_for_recategorization(recategorization: GuidelineRecategorization, subject: dict) -> Iterator[JsonPatch]: + """ + Compute as set of JSON patches to apply the recategorization to the subject Sarif file. + """ + def to_jsonpatch(pointer:jsonpointer.JsonPointer) -> Iterator[JsonPatch]: + obligation_tag = cast(str, pointer.get(subject)) + _, standard, _, category = obligation_tag.split('/') + yield JsonPatch( + op = 'replace', + path = pointer.path, + value = f'external/{standard}/obligation/{recategorization.category}') + yield JsonPatch(op = 'add', path = pointer.path, value = f'external/{standard}/original-obligation/{category}') + return (patch for pointer in json_path_to_pointer(recategorization_to_json_path_for_category(recategorization), subject) for patch in to_jsonpatch(pointer)) + + +def get_guideline_recategorizations(coding_standards_config: Mapping[str, Any]) -> Generator[GuidelineRecategorization, None, None]: + """ + Return the guideline recategorizations for a given Coding Standards configuration. + """ + for spec in coding_standards_config['guideline-recategorizations']: + yield GuidelineRecategorization(spec['rule-id'], spec['category']) + +def load_schema(path: Path, defaultname: str) -> Optional[Mapping[str, Any]]: + def resolve_path(path : Path) -> Optional[Path]: + if path.is_file(): + return path + + if path.is_dir(): + if (path / defaultname).is_file(): + return (path / defaultname) + + if (path / 'schemas' / defaultname).is_file(): + return (path / 'schemas' / defaultname) + + if path.parent != path: + return resolve_path(path.parent) + else: + return None + resolved_schema_path = resolve_path(path.resolve()) + if resolved_schema_path: + with resolved_schema_path.open(mode='r') as fp: + try: + return json.load(fp) + except json.decoder.JSONDecodeError as e: + print_failure(f"Failed to load schema with error \"{e.msg}\" at {resolved_schema_path}:{e.lineno}:{e.colno}!") + else: + return None + +def load_config(path: Path) -> Optional[Mapping[str, Any]]: + if path.is_file(): + with path.open('r') as fp: + try: + return yaml.safe_load(fp) + except yaml.parser.ParserError as e: + print_failure(f"Failed to load config with error \"{e.problem}\" at {path}:{e.problem_mark.line}:{e.problem_mark.column}!") + else: + return None + +def validate_against_schema(schema: Mapping[str, Any], instance: Mapping[str, Any]) -> None: + jsonschema.validate(schema=schema, instance=instance) + +def print_warning(*values): + print(*values, file=sys.stderr) + +def print_failure(*values): + print(*values, file=sys.stderr) + exit(1) + +def main(args: argparse.Namespace): + coding_standards_schema = load_schema(args.coding_standards_schema_file, 'coding-standards-schema-1.0.0.json') + if not coding_standards_schema: + print_failure("Failed to load Coding Standards schema!") + + if not '$id' in coding_standards_schema: + print_failure(f"Missing id for Coding Standards schema: '{args.coding_standards_schema_file}'") + + if coding_standards_schema['$id'] != CODING_STANDARDS_SCHEMA_ID: + print_failure(f"Unexpected id for Coding Standards schema, expecting '{CODING_STANDARDS_SCHEMA_ID}'!") + + sarif_schema = load_schema(args.sarif_schema_file, 'sarif-schema-2.1.0.json') + if not sarif_schema: + print(f"Failed to load Sarif schema: '{args.sarif_schema_file}'!", file=sys.stderr) + sys.exit(1) + sarif_schema = cast(Mapping[str, Any], sarif_schema) + + if not '$id' in sarif_schema: + print_failure(f"Missing id for Sarif schema: '{args.sarif_schema_file}'") + + if sarif_schema['$id'] != SARIF_SCHEMA_ID: + print_failure(f"Unexpected id for Sarif schema: '{args.sarif_schema_file}, expecting '{SARIF_SCHEMA_ID}'!") + + coding_standards_config = load_config(args.coding_standards_config_file) + if not coding_standards_schema: + print(f"Failed to load Coding Standards configuration file: {args.coding_standards_config_file}!", file=sys.stderr) + sys.exit(1) + + coding_standards_config = cast(Mapping[str, Any], coding_standards_config) + try: + validate_against_schema(coding_standards_schema, coding_standards_config) + except jsonschema.ValidationError as e: + print(f"Failed to validate the Coding Standards configuration file: {args.coding_standards_config_file} with the message: '{e.message}'!", file=sys.stderr) + sys.exit(1) + + sarif = json.load(args.sarif_in) + try: + validate_against_schema(sarif_schema, sarif) + except jsonschema.ValidationError as e: + print(f"Failed to validate the provided Sarif with the message: '{e.message}'!", file=sys.stderr) + sys.exit(1) + + recategorizations = get_guideline_recategorizations(coding_standards_config) + patch = jsonpatch.JsonPatch([patch for r in recategorizations for patch in generate_json_patches_for_recategorization(r, sarif)]) + if args.dump_json_patch != None: + dump_json_patch = Path(args.dump_json_patch) + if dump_json_patch.is_dir(): + dump_json_patch /= 'json-patch.json' + + if not dump_json_patch.exists(): + dump_json_patch.write_text(patch.to_string()) + else: + print_warning(f"Skipping dumping of JSON patch to file {dump_json_patch} because it already exists!") + + patched_sarif = patch.apply(sarif) + validate_against_schema(sarif_schema, patched_sarif) + + json.dump(patched_sarif, args.sarif_out) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Apply a guideline re-categorization specification to a Sarif results file.') + parser.add_argument('--coding-standards-schema-file', type=Path, default=Path.cwd()) + parser.add_argument('--sarif-schema-file', type=Path, default=Path.cwd()) + parser.add_argument('--dump-json-patch', type=Path) + parser.add_argument('coding_standards_config_file', type=Path) + parser.add_argument('sarif_in', nargs='?', type=argparse.FileType('r'), default=sys.stdin) + parser.add_argument('sarif_out', nargs='?', type=argparse.FileType('w'), default=sys.stdout) + + main(parser.parse_args()) \ No newline at end of file diff --git a/scripts/guideline_recategorization/recategorize_test.py b/scripts/guideline_recategorization/recategorize_test.py new file mode 100644 index 0000000000..f3efc4f337 --- /dev/null +++ b/scripts/guideline_recategorization/recategorize_test.py @@ -0,0 +1,99 @@ +import pytest +import recategorize +from pathlib import Path +import argparse +import sys + +TEST_DATA_DIR = Path(__file__).resolve().parent / 'test-data' + +class TestsInputs: + def test_invalid_codeql_config(self): + with pytest.raises(SystemExit): + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= Path.cwd(), + coding_standards_config_file= TEST_DATA_DIR / 'invalid-coding-standards-config.yml' + )) + + def test_valid_codeql_config(self): + with (TEST_DATA_DIR / 'valid-sarif.json').open(mode='r') as sarif_in: + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= Path.cwd(), + coding_standards_config_file= TEST_DATA_DIR / 'valid-coding-standards-config.yml', + sarif_in=sarif_in, + sarif_out=sys.stdout, + dump_json_patch=None + )) + + def test_invalid_sarif_file(self): + with pytest.raises(SystemExit): + with (TEST_DATA_DIR / 'invalid-sarif.json').open(mode='r') as sarif_in: + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= Path.cwd(), + coding_standards_config_file= TEST_DATA_DIR / 'valid-coding-standards-config.yml', + sarif_in=sarif_in + )) + + def test_valid_sarif_file(self): + with (TEST_DATA_DIR / 'valid-sarif.json').open(mode='r') as sarif_in: + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= Path.cwd(), + coding_standards_config_file= TEST_DATA_DIR / 'valid-coding-standards-config.yml', + sarif_in=sarif_in, + sarif_out=sys.stdout, + dump_json_patch=None + )) + + def test_invalid_yaml(self): + with pytest.raises(SystemExit): + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= Path.cwd(), + coding_standards_config_file= TEST_DATA_DIR / 'invalid-yaml.yml' + )) + + def test_invalid_json_for_schema(self): + with pytest.raises(SystemExit): + recategorize.main(argparse.Namespace( + coding_standards_schema_file= TEST_DATA_DIR / 'invalid-json.json' + )) + +class TestUnsupportedSchemas: + def test_unsupported_sarif_schema(self): + with pytest.raises(SystemExit): + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= TEST_DATA_DIR / 'unsupported-sarif-schema-2.0.0.json', + coding_standards_config_file= Path.cwd() + )) + def test_unsupported_coding_standards_config_schema(self): + with pytest.raises(SystemExit): + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= TEST_DATA_DIR / 'unsupported-coding-standards-schema-0.0.1.json', + coding_standards_config_file= Path.cwd() + )) + +class TestRecategorization: + def test_recategorization(self, tmp_path): + with (TEST_DATA_DIR / 'valid-sarif.json').open(mode='r') as sarif_in: + with (tmp_path / 'sarif.json').open(mode='w') as sarif_out: + recategorize.main(argparse.Namespace( + coding_standards_schema_file= Path.cwd(), + sarif_schema_file= Path.cwd(), + coding_standards_config_file= TEST_DATA_DIR / 'valid-coding-standards-config.yml', + sarif_in=sarif_in, + sarif_out=sarif_out, + dump_json_patch=tmp_path / 'json-patch.json' + )) + + expected_patch = (TEST_DATA_DIR / 'json-patch.expected').read_text() + actual_patch = (tmp_path / 'json-patch.json').read_text() + assert(expected_patch == actual_patch) + + expected_sarif = (TEST_DATA_DIR / 'valid-sarif-recategorized.expected').read_text() + actual_sarif = (tmp_path / 'sarif.json').read_text() + assert(expected_sarif == actual_sarif) \ No newline at end of file diff --git a/scripts/guideline_recategorization/requirements.txt b/scripts/guideline_recategorization/requirements.txt new file mode 100644 index 0000000000..3fa3a6ceae --- /dev/null +++ b/scripts/guideline_recategorization/requirements.txt @@ -0,0 +1,7 @@ +jsonpath-ng==1.5.3 +jsonschema==4.9.1 +jsonpatch==1.32 +jsonpointer==2.3 +PyYAML==5.4 +pytest==7.2.0 + diff --git a/scripts/guideline_recategorization/test-data/empty-coding-standards-config.yml b/scripts/guideline_recategorization/test-data/empty-coding-standards-config.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/guideline_recategorization/test-data/invalid-coding-standards-config.yml b/scripts/guideline_recategorization/test-data/invalid-coding-standards-config.yml new file mode 100644 index 0000000000..16a72ca981 --- /dev/null +++ b/scripts/guideline_recategorization/test-data/invalid-coding-standards-config.yml @@ -0,0 +1,2 @@ +guideline-recategorizations: + - rule-id: "A0-1-1" diff --git a/scripts/guideline_recategorization/test-data/invalid-json.json b/scripts/guideline_recategorization/test-data/invalid-json.json new file mode 100644 index 0000000000..ab61ba76e1 --- /dev/null +++ b/scripts/guideline_recategorization/test-data/invalid-json.json @@ -0,0 +1,3 @@ +{ + "foo": "bar", +} \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/invalid-sarif.json b/scripts/guideline_recategorization/test-data/invalid-sarif.json new file mode 100644 index 0000000000..ed161bd6fd --- /dev/null +++ b/scripts/guideline_recategorization/test-data/invalid-sarif.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0" +} \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/invalid-yaml.yml b/scripts/guideline_recategorization/test-data/invalid-yaml.yml new file mode 100644 index 0000000000..f761ce92ad --- /dev/null +++ b/scripts/guideline_recategorization/test-data/invalid-yaml.yml @@ -0,0 +1,3 @@ +key: + key1: "value" + key2: "value" \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/json-patch.expected b/scripts/guideline_recategorization/test-data/json-patch.expected new file mode 100644 index 0000000000..9ca8544d9f --- /dev/null +++ b/scripts/guideline_recategorization/test-data/json-patch.expected @@ -0,0 +1 @@ +[{"op": "replace", "path": "/runs/0/tool/driver/rules/0/properties/tags/5", "value": "external/autosar/obligation/mandatory"}, {"op": "add", "path": "/runs/0/tool/driver/rules/0/properties/tags/5", "value": "external/autosar/original-obligation/required"}, {"op": "replace", "path": "/runs/0/tool/driver/rules/1/properties/tags/5", "value": "external/autosar/obligation/disapplied"}, {"op": "add", "path": "/runs/0/tool/driver/rules/1/properties/tags/5", "value": "external/autosar/original-obligation/advisory"}, {"op": "replace", "path": "/runs/0/tool/driver/rules/2/properties/tags/4", "value": "external/autosar/obligation/mandatory"}, {"op": "add", "path": "/runs/0/tool/driver/rules/2/properties/tags/4", "value": "external/autosar/original-obligation/advisory"}] \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/unsupported-coding-standards-schema-0.0.1.json b/scripts/guideline_recategorization/test-data/unsupported-coding-standards-schema-0.0.1.json new file mode 100644 index 0000000000..035f218ed7 --- /dev/null +++ b/scripts/guideline_recategorization/test-data/unsupported-coding-standards-schema-0.0.1.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://raw.githubusercontent.com/github/codeql-coding-standards/main/schemas/coding-standards-schema-0.0.1.json", + "additionalProperties": false, + "definitions": { + "guideline-category": { + "enum": [ + "mandatory", + "required", + "advisory", + "disapplied" + ] + }, + "guideline-recategorization": { + "type": "object", + "properties": { + "rule-id": { + "type": "string" + }, + "category": { + "$ref": "#/definitions/guideline-category" + } + }, + "required": [ + "rule-id", + "category" + ] + } + }, + "properties": { + "report-deviated-alerts": { + "description": "When true includes alerts with an applicable deviation. Used for report generation.", + "type": "boolean" + }, + "deviations": { + "description": "A set of deviation records.", + "type": "array" + }, + "deviation-permits": { + "description": "A set of deviation permits.", + "type": "array" + }, + "guideline-recategorizations": { + "type": "array", + "minProperties": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/guideline-recategorization" + } + } + }, + "required": [], + "type": "object" +} \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/unsupported-sarif-schema-2.0.0.json b/scripts/guideline_recategorization/test-data/unsupported-sarif-schema-2.0.0.json new file mode 100644 index 0000000000..10ba2ba3c9 --- /dev/null +++ b/scripts/guideline_recategorization/test-data/unsupported-sarif-schema-2.0.0.json @@ -0,0 +1,1769 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Static Analysis Results Format (SARIF) Version 2.0.0 JSON Schema", + "description": "Static Analysis Results Format (SARIF) Version 2.0.0 JSON Schema: a standard format for the output of static analysis tools.", + "additionalProperties": false, + "type": "object", + "properties": { + "$schema": { + "description": "The URI of the JSON schema corresponding to the version.", + "type": "string", + "format": "uri" + }, + "version": { + "description": "The SARIF format version of this log file.", + "enum": [ + "2.0.0" + ] + }, + "runs": { + "description": "The set of runs contained in this log file.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/run" + } + } + }, + "required": [ + "version", + "runs" + ], + "definitions": { + "attachment": { + "description": "A file relevant to a tool invocation or to a result.", + "type": "object", + "additionalProperties": false, + "properties": { + "description": { + "description": "A message describing the role played by the attachment.", + "$ref": "#/definitions/message" + }, + "fileLocation": { + "description": "The location of the attachment.", + "$ref": "#/definitions/fileLocation" + }, + "regions": { + "description": "An array of regions of interest within the attachment.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/region" + } + }, + "rectangles": { + "description": "An array of rectangles specifying areas of interest within the image.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/rectangle" + } + } + }, + "required": [ + "fileLocation" + ] + }, + "codeFlow": { + "description": "A set of threadFlows which together describe a pattern of code execution relevant to detecting a result.", + "additionalProperties": false, + "type": "object", + "properties": { + "message": { + "description": "A message relevant to the code flow.", + "$ref": "#/definitions/message" + }, + "threadFlows": { + "description": "An array of one or more unique threadFlow objects, each of which describes the progress of a program through a thread of execution.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/threadFlow" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the code flow.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "threadFlows" + ] + }, + "threadFlowLocation": { + "description": "A location visited by an analysis tool in the course of simulating or monitoring the execution of a program.", + "additionalProperties": false, + "type": "object", + "properties": { + "step": { + "description": "The 0-based sequence number of the location in the code flow within which it occurs.", + "type": "integer", + "minimum": 0 + }, + "location": { + "description": "The code location.", + "$ref": "#/definitions/location" + }, + "stack": { + "description": "The call stack leading to this location.", + "$ref": "#/definitions/stack" + }, + "kind": { + "description": "A string describing the type of this location.", + "type": "string" + }, + "module": { + "description": "The name of the module that contains the code that is executing.", + "type": "string" + }, + "state": { + "description": "A dictionary, each of whose keys specifies a variable or expression, the associated value of which represents the variable or expression value. For an annotation of kind 'continuation', for example, this dictionary might hold the current assumed values of a set of global variables.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nestingLevel": { + "description": "An integer representing a containment hierarchy within the thread flow", + "type": "integer" + }, + "executionOrder": { + "description": "An integer representing the temporal order in which execution reached this location.", + "type": "integer" + }, + "timestamp": { + "description": "The time at which this location was executed.", + "type": "string", + "format": "date-time" + }, + "importance": { + "description": "Specifies the importance of this location in understanding the code flow in which it occurs. The order from most to least important is \"essential\", \"important\", \"unimportant\". Default: \"important\".", + "enum": [ + "important", + "essential", + "unimportant" + ] + }, + "properties": { + "description": "Key/value pairs that provide additional information about the code location.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + } + }, + "conversion": { + "description": "Describes how a converter transformed the output of a static analysis tool from the analysis tool's native output format into the SARIF format.", + "additionalProperties": false, + "type": "object", + "properties": { + "tool": { + "description": "A tool object that describes the converter.", + "$ref": "#/definitions/tool" + }, + "invocation": { + "description": "An invocation object that describes the invocation of the converter.", + "$ref": "#/definitions/invocation" + }, + "analysisToolLogFiles": { + "description": "The locations of the analysis tool's per-run log files.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/fileLocation" + } + } + }, + "required": [ + "tool" + ] + }, + "edge": { + "description": "Represents a directed edge in a graph.", + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "description": "A string that uniquely identifies the edge within its graph.", + "type": "string" + }, + "label": { + "description": "A short description of the edge.", + "$ref": "#/definitions/message" + }, + "sourceNodeId": { + "description": "Identifies the source node (the node at which the edge starts).", + "type": "string" + }, + "targetNodeId": { + "description": "Identifies the target node (the node at which the edge ends).", + "type": "string" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the edge.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "id", + "sourceNodeId", + "targetNodeId" + ] + }, + "edgeTraversal": { + "description": "Represents the traversal of a single edge in the course of a graph traversal.", + "type": "object", + "additionalProperties": false, + "properties": { + "edgeId": { + "description": "Identifies the edge being traversed.", + "type": "string" + }, + "message": { + "description": "A message to display to the user as the edge is traversed.", + "$ref": "#/definitions/message" + }, + "finalState": { + "description": "The values of relevant expressions after the edge has been traversed.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "stepOverEdgeCount": { + "description": "The number of edge traversals necessary to return from a nested graph.", + "type": "integer" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the edge traversal.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "edgeId" + ] + }, + "exception": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "A string that identifies the kind of exception, for example, the fully qualified type name of an object that was thrown, or the symbolic name of a signal." + }, + "message": { + "type": "string", + "description": "A plain text message that describes the exception." + }, + "stack": { + "description": "The sequence of function calls leading to the exception.", + "$ref": "#/definitions/stack" + }, + "innerExceptions": { + "type": "array", + "description": "An array of exception objects each of which is considered a cause of this exception.", + "items": { + "$ref": "#/definitions/exception" + } + } + } + }, + "file": { + "description": "A single file. In some cases, this file might be nested within another file.", + "additionalProperties": false, + "type": "object", + "properties": { + "fileLocation": { + "description": "The location of the file.", + "$ref": "#/definitions/fileLocation" + }, + "parentKey": { + "description": "Identifies the key of the immediate parent of the file, if this file is nested.", + "type": "string" + }, + "offset": { + "description": "The offset in bytes of the file within its containing file.", + "type": "integer" + }, + "length": { + "description": "The length of the file in bytes.", + "type": "integer" + }, + "roles": { + "description": "The role or roles played by the file in the analysis.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "analysisTarget", + "attachment", + "responseFile", + "resultFile", + "standardStream", + "traceFile", + "unmodifiedFile", + "modifiedFile", + "addedFile", + "deletedFile", + "renamedFile", + "uncontrolledFile" + ] + } + }, + "mimeType": { + "description": "The MIME type (RFC 2045) of the file.", + "type": "string", + "pattern": "[^/]+/.+" + }, + "contents": { + "description": "The contents of the file.", + "$ref": "#/definitions/fileContent" + }, + "encoding": { + "description": "Specifies the encoding for a file object that refers to a text file.", + "type": "string" + }, + "hashes": { + "description": "An array of hash objects, each of which specifies a hashed value for the file, along with the name of the hash function used to compute the hash.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/hash" + } + }, + "lastModifiedTime": { + "description": "The date and time at which the file was most recently modified. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the file.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + } + }, + "fileChange": { + "description": "A change to a single file.", + "additionalProperties": false, + "type": "object", + "properties": { + "fileLocation": { + "description": "The location of the file to change.", + "$ref": "#/definitions/fileLocation" + }, + "replacements": { + "description": "An array of replacement objects, each of which represents the replacement of a single range of bytes in a single file specified by 'uri'.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/replacement" + } + } + }, + "required": [ + "fileLocation", + "replacements" + ] + }, + "fileContent": { + "description": "Represents content from an external file.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "description": "UTF-8-encoded content from a text file.", + "type": "string" + }, + "binary": { + "description": "MIME Base64-encoded content from a binary file, or from a text file in its original encoding.", + "type": "string" + } + } + }, + "fileLocation": { + "description": "Specifies the location of a file.", + "additionalProperties": false, + "type": "object", + "properties": { + "uri": { + "description": "A string containing a valid relative or absolute URI.", + "type": "string", + "format": "uri-reference" + }, + "uriBaseId": { + "description": "A string which indirectly specifies the absolute URI with respect to which a relative URI in the \"uri\" property is interpreted.", + "type": "string" + } + }, + "required": [ + "uri" + ] + }, + "fix": { + "description": "A proposed fix for the problem represented by a result object. A fix specifies a set of file to modify. For each file, it specifies a set of bytes to remove, and provides a set of new bytes to replace them.", + "additionalProperties": false, + "type": "object", + "properties": { + "description": { + "description": "A plain text message that describes the proposed fix, enabling viewers to present the proposed change to an end user.", + "$ref": "#/definitions/message" + }, + "fileChanges": { + "description": "One or more file changes that comprise a fix for a result.", + "type": "array", + "items": { + "$ref": "#/definitions/fileChange" + } + } + }, + "required": [ + "fileChanges" + ] + }, + "graph": { + "description": "A network of nodes and directed edges that describes some aspect of the structure of the code (for example, a call graph).", + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "description": "A string that uniquely identifies the graph within a run.graphs or result.graphs array.", + "type": "string" + }, + "description": { + "description": "A description of the graph.", + "$ref": "#/definitions/message" + }, + "nodes": { + "description": "An array of node objects representing the nodes of the graph.", + "type": "array", + "items": { + "$ref": "#/definitions/node" + } + }, + "edges": { + "description": "An array of edge objects representing the edges of the graph.", + "type": "array", + "items": { + "$ref": "#/definitions/edge" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the graph.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "id", + "nodes", + "edges" + ] + }, + "graphTraversal": { + "description": "Represents a path through a graph.", + "type": "object", + "additionalProperties": false, + "properties": { + "graphId": { + "description": "A string that uniquely identifies that graph being traversed.", + "type": "string" + }, + "description": { + "description": "A description of this graph traversal.", + "$ref": "#/definitions/message" + }, + "initialState": { + "description": "Values of relevant expressions at the start of the graph traversal.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "edgeTraversals": { + "description": "The sequences of edges traversed by this graph traversal.", + "type": "array", + "items": { + "$ref": "#/definitions/edgeTraversal" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the graph traversal.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "graphId", + "edgeTraversals" + ] + }, + "hash": { + "description": "A hash value of some file or collection of files, together with the hash function used to compute the hash.", + "additionalProperties": false, + "type": "object", + "properties": { + "value": { + "description": "The hash value of some file or collection of files, computed by the hash function named in the 'algorithm' property.", + "type": "string" + }, + "algorithm": { + "description": "The name of the hash function used to compute the hash value specified in the 'value' property.", + "type": "string" + } + }, + "required": [ + "value", + "algorithm" + ] + }, + "invocation": { + "description": "The runtime environment of the analysis tool run.", + "additionalProperties": false, + "type": "object", + "properties": { + "commandLine": { + "description": "The command line used to invoke the tool.", + "type": "string" + }, + "arguments": { + "description": "An array of strings, containing in order the command line arguments passed to the tool from the operating system.", + "type": "array", + "items": { + "type": "string" + } + }, + "responseFiles": { + "description": "The locations of any response files specified on the tool's command line.", + "type": "array", + "items": { + "$ref": "#/definitions/fileLocation" + } + }, + "attachments": { + "description": "A set of files relevant to the invocation of the tool.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/attachment" + } + }, + "startTime": { + "description": "The date and time at which the run started. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + "endTime": { + "description": "The date and time at which the run ended. See \"Date/time properties\" in the SARIF spec for the required format.", + "type": "string", + "format": "date-time" + }, + "exitCode": { + "description": "The process exit code.", + "type": "integer" + }, + "toolNotifications": { + "description": "A list of runtime conditions detected by the tool in the course of the analysis.", + "type": "array", + "items": { + "$ref": "#/definitions/notification" + } + }, + "configurationNotifications": { + "description": "A list of conditions detected by the tool that are relevant to the tool's configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/notification" + } + }, + "exitCodeDescription": { + "description": "The reason for the process exit.", + "type": "string" + }, + "exitSignalName": { + "description": "The name of the signal that caused the process to exit.", + "type": "string" + }, + "exitSignalNumber": { + "description": "The numeric value of the signal that caused the process to exit.", + "type": "integer" + }, + "processStartFailureMessage": { + "description": "The reason given by the operating system that the process failed to start.", + "type": "string" + }, + "toolExecutionSuccessful": { + "description": "A value indicating whether the tool's execution completed successfully.", + "type": "boolean" + }, + "machine": { + "description": "The machine that hosted the analysis tool run.", + "type": "string" + }, + "account": { + "description": "The account that ran the analysis tool.", + "type": "string" + }, + "processId": { + "description": "The process id for the analysis tool run.", + "type": "integer" + }, + "executableLocation": { + "description": "An absolute URI specifying the location of the analysis tool's executable.", + "$ref": "#/definitions/fileLocation" + }, + "workingDirectory": { + "description": "The working directory for the analysis rool run.", + "type": "string" + }, + "environmentVariables": { + "description": "The environment variables associated with the analysis tool process, expressed as key/value pairs.", + "type": "object", + "additionalProperties": true, + "default": {} + }, + "stdin": { + "description": "A file containing the standard input stream to the process that was invoked.", + "$ref": "#/definitions/fileLocation" + }, + "stdout": { + "description": "A file containing the standard output stream from the process that was invoked.", + "$ref": "#/definitions/fileLocation" + }, + "stderr": { + "description": "A file containing the standard error stream from the process that was invoked.", + "$ref": "#/definitions/fileLocation" + }, + "stdoutStderr": { + "description": "A file containing the interleaved standard output and standard error stream from the process that was invoked.", + "$ref": "#/definitions/fileLocation" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the invocation.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + } + }, + "location": { + "description": "A location within a programming artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + "physicalLocation": { + "description": "Identifies the file and region.", + "$ref": "#/definitions/physicalLocation" + }, + "fullyQualifiedLogicalName": { + "description": "The human-readable fully qualified name of the logical location. If run.logicalLocations is present, this value matches a property name within that object, from which further information about the logical location can be obtained.", + "type": "string" + }, + "message": { + "description": "A message relevant to the location.", + "$ref": "#/definitions/message" + }, + "annotations": { + "description": "A set of regions relevant to the location.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/region" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the location.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + } + }, + "logicalLocation": { + "description": "A logical location of a construct that produced a result.", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "Identifies the construct in which the result occurred. For example, this property might contain the name of a class or a method.", + "type": "string" + }, + "fullyQualifiedName": { + "description": "The human-readable fully qualified name of the logical location.", + "type": "string" + }, + "decoratedName": { + "description": "The machine-readable name for the logical location, such as a mangled function name provided by a C++ compiler that encodes calling convention, return type and other details along with the function name.", + "type": "string" + }, + "parentKey": { + "description": "Identifies the key of the immediate parent of the construct in which the result was detected. For example, this property might point to a logical location that represents the namespace that holds a type.", + "type": "string" + }, + "kind": { + "description": "The type of construct this logicalLocationComponent refers to. Should be one of 'function', 'member', 'module', 'namespace', 'package', 'parameter', 'resource', 'returnType', 'type', or 'variable', if any of those accurately describe the construct.", + "type": "string" + } + } + }, + "message": { + "description": "Encapsulates a message intended to be read by the end user.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "description": "A plain text message string.", + "type": "string" + }, + "messageId": { + "description": "The resource id for a plain text message string.", + "type": "string" + }, + "richText": { + "description": "A rich text message string.", + "type": "string" + }, + "richMessageId": { + "description": "The resource id for a rich text message string.", + "type": "string" + }, + "arguments": { + "description": "An array of strings to substitute into the message string.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "node": { + "description": "Represents a node in a graph.", + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "description": "A string that uniquely identifies the node within its graph.", + "type": "string" + }, + "label": { + "description": "A short description of the node.", + "$ref": "#/definitions/message" + }, + "location": { + "description": "A code location associated with the node.", + "$ref": "#/definitions/location" + }, + "children": { + "description": "Array of child nodes.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/node" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the node.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "id" + ] + }, + "notification": { + "description": "Describes a condition relevant to the tool itself, as opposed to being relevant to a target being analyzed by the tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "description": "An identifier for the condition that was encountered.", + "type": "string" + }, + "ruleId": { + "description": "The stable, unique identifier of the rule (if any) to which this notification is relevant. This member can be used to retrieve rule metadata from the rules dictionary, if it exists.", + "type": "string" + }, + "physicalLocation": { + "description": "The file and region relevant to this notification.", + "$ref": "#/definitions/physicalLocation" + }, + "message": { + "description": "A message that describes the condition that was encountered.", + "$ref": "#/definitions/message" + }, + "level": { + "description": "A value specifying the severity level of the notification.", + "default": "warning", + "enum": [ + "note", + "warning", + "error" + ] + }, + "threadId": { + "description": "The thread identifier of the code that generated the notification.", + "type": "integer" + }, + "time": { + "description": "The date and time at which the analysis tool generated the notification.", + "type": "string", + "format": "date-time" + }, + "exception": { + "description": "The runtime exception, if any, relevant to this notification.", + "$ref": "#/definitions/exception" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the notification.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "message" + ] + }, + "physicalLocation": { + "description": "A physical location relevant to a result. Specifies a reference to a programming artifact together with a range of bytes or characters within that artifact.", + "additionalProperties": false, + "type": "object", + "properties": { + "id": { + "description": "Value that distinguishes this physical location from all other physical locations in this run object.", + "type": "integer" + }, + "fileLocation": { + "description": "The location of the file.", + "$ref": "#/definitions/fileLocation" + }, + "region": { + "description": "Specifies a portion of the file.", + "$ref": "#/definitions/region" + }, + "contextRegion": { + "description": "Specifies a portion of the file that encloses the region. Allows a viewer to display additional context around the region.", + "$ref": "#/definitions/region" + } + }, + "required": [ + "fileLocation" + ] + }, + "rectangle": { + "description": "An area within an image.", + "additionalProperties": false, + "type": "object", + "properties": { + "top": { + "description": "The Y coordinate of the top edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + "left": { + "description": "The X coordinate of the left edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + "bottom": { + "description": "The Y coordinate of the bottom edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + "right": { + "description": "The X coordinate of the right edge of the rectangle, measured in the image's natural units.", + "type": "number" + }, + "message": { + "description": "A message relevant to the rectangle.", + "$ref": "#/definitions/message" + } + } + }, + "region": { + "description": "A region within a file where a result was detected.", + "additionalProperties": false, + "type": "object", + "properties": { + "startLine": { + "description": "The line number of the first character in the region.", + "type": "integer", + "minimum": 1 + }, + "startColumn": { + "description": "The column number of the first character in the region.", + "type": "integer", + "minimum": 1 + }, + "endLine": { + "description": "The line number of the last character in the region.", + "type": "integer", + "minimum": 1 + }, + "endColumn": { + "description": "The column number of the character following the end of the region.", + "type": "integer", + "minimum": 1 + }, + "charOffset": { + "description": "The zero-based offset from the beginning of the file of the first character in the region.", + "type": "integer", + "minimum": 0 + }, + "charLength": { + "description": "The length of the region in characters.", + "type": "integer", + "minimum": 0 + }, + "byteOffset": { + "description": "The zero-based offset from the beginning of the file of the first byte in the region.", + "type": "integer", + "minimum": 0 + }, + "byteLength": { + "description": "The length of the region in bytes.", + "type": "integer", + "minimum": 0 + }, + "snippet": { + "description": "The portion of the file contents within the specified region.", + "$ref": "#/definitions/fileContent" + }, + "message": { + "description": "A message relevant to the region.", + "$ref": "#/definitions/message" + } + } + }, + "replacement": { + "description": "The replacement of a single region of a file.", + "additionalProperties": false, + "type": "object", + "properties": { + "deletedRegion": { + "description": "The region of the file to delete.", + "$ref": "#/definitions/region" + }, + "insertedContent": { + "description": "The content to insert at the location specified by the 'deletedRegion' property.", + "$ref": "#/definitions/fileContent" + } + }, + "required": [ + "deletedRegion" + ] + }, + "resources": { + "description": "Container for items that require localization.", + "type": "object", + "properties": { + "messageStrings": { + "description": "A dictionary, each of whose keys is a resource identifier and each of whose values is a localized string.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "rules": { + "description": "A dictionary, each of whose keys is a string and each of whose values is a 'rule' object, that describe all rules associated with an analysis tool or a specific run of an analysis tool.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/rule" + } + } + } + }, + "result": { + "description": "A result produced by an analysis tool.", + "additionalProperties": false, + "type": "object", + "properties": { + "ruleId": { + "description": "The stable, unique identifier of the rule (if any) to which this notification is relevant. This member can be used to retrieve rule metadata from the rules dictionary, if it exists.", + "type": "string" + }, + "level": { + "description": "A value specifying the severity level of the result.", + "enum": [ + "notApplicable", + "pass", + "note", + "warning", + "error", + "open" + ] + }, + "message": { + "description": "A message that describes the result. The first sentence of the message only will be displayed when visible space is limited.", + "$ref": "#/definitions/message" + }, + "ruleMessageId": { + "description": "A string that identifies the message within the metadata for the rule used in this result.", + "type": "string" + }, + "locations": { + "description": "One or more locations where the result occurred. Specify only one location unless the problem indicated by the result can only be corrected by making a change at every specified location.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/location" + } + }, + "analysisTarget": { + "description": "Identifies the file that the analysis tool was instructed to scan. This need not be the same as the file where the result actually occurred.", + "$ref": "#/definitions/fileLocation" + }, + "instanceGuid": { + "description": "A stable, unique identifer for the result in the form of a GUID.", + "type": "string" + }, + "partialFingerprints": { + "description": "A set of strings that contribute to the stable, unique identity of the result.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "fingerprints": { + "description": "A set of strings each of which individually defines a stable, unique identity for the result.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "stacks": { + "description": "An array of 'stack' objects relevant to the result.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/stack" + } + }, + "codeFlows": { + "description": "An array of 'codeFlow' objects relevant to the result.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/codeFlow" + } + }, + "graphs": { + "description": "An array of one or more unique 'graph' objects.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/graph" + } + }, + "graphTraversals": { + "description": "An array of one or more unique 'graphTraversal' objects.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/graphTraversal" + } + }, + "relatedLocations": { + "description": "A set of locations relevant to this result.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/location" + } + }, + "suppressionStates": { + "description": "A set of flags indicating one or more suppression conditions.", + "type": "array", + "items": { + "enum": [ + "suppressedInSource", + "suppressedExternally" + ] + } + }, + "baselineState": { + "description": "The state of a result relative to a baseline of a previous run.", + "enum": [ + "new", + "existing", + "absent" + ] + }, + "attachments": { + "description": "A set of files relevant to the result.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/attachment" + } + }, + "workItemUris": { + "description": "The URIs of the work items associated with this result", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "format": "uri" + } + }, + "conversionProvenance": { + "description": "An array of physicalLocation objects which specify the portions of an analysis tool's output that a converter transformed into the result object.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/physicalLocation" + } + }, + "fixes": { + "description": "An array of 'fix' objects, each of which represents a proposed fix to the problem indicated by the result.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/fix" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the result.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + } + }, + "rule": { + "description": "Describes an analysis rule.", + "additionalProperties": false, + "type": "object", + "properties": { + "id": { + "description": "A stable, opaque identifier for the rule.", + "type": "string" + }, + "name": { + "description": "A rule identifier that is understandable to an end user.", + "$ref": "#/definitions/message" + }, + "shortDescription": { + "description": "A concise description of the rule. Should be a single sentence that is understandable when visible space is limited to a single line of text.", + "$ref": "#/definitions/message" + }, + "fullDescription": { + "description": "A description of the rule. Should, as far as possible, provide details sufficient to enable resolution of any problem indicated by the result.", + "$ref": "#/definitions/message" + }, + "messageStrings": { + "description": "A set of name/value pairs with arbitrary names. The value within each name/value pair consists of plain text interspersed with placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "richMessageStrings": { + "description": "A set of name/value pairs with arbitrary names. The value within each name/value pair consists of rich text interspersed with placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "configuration": { + "description": "Information about the rule that can be configured at runtime.", + "$ref": "#/definitions/ruleConfiguration" + }, + "helpUri": { + "description": "A URI where the primary documentation for the rule can be found.", + "type": "string", + "format": "uri" + }, + "help": { + "description": "Provides the primary documentation for the rule, useful when there is no online documentation.", + "$ref": "#/definitions/message" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the rule.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "id" + ] + }, + "ruleConfiguration": { + "description": "Information about a rule that can be configured at runtime.", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "description": "Specifies whether the rule will be evaluated during the scan.", + "type": "boolean" + }, + "defaultLevel": { + "description": "Specifies the default severity level of the result.", + "enum": [ + "note", + "warning", + "error", + "open" + ] + }, + "parameters": { + "description": "Contains configuration information specific to this rule.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional configuration information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + } + }, + "run": { + "description": "Describes a single run of an analysis tool, and contains the output of that run.", + "additionalProperties": false, + "type": "object", + "properties": { + "tool": { + "description": "Information about the tool or tool pipeline that generated the results in this run. A run can only contain results produced by a single tool or tool pipeline. A run can aggregate results from multiple log files, as long as context around the tool run (tool command-line arguments and the like) is identical for all aggregated files.", + "$ref": "#/definitions/tool" + }, + "invocations": { + "description": "Describes the invocation of the analysis tool.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/invocation" + } + }, + "conversion": { + "description": "A conversion object that describes how a converter transformed an analysis tool's native output format into the SARIF format.", + "$ref": "#/definitions/conversion" + }, + "versionControlProvenance": { + "description": "Specifies the revision in version control of the files that were scanned.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/versionControlDetails" + } + }, + "originalUriBaseIds": { + "description": "The absolute URI specified by each uriBaseId symbol on the machine where the tool originally ran.", + "type": "object", + "additionalProperties": { + "type": "string", + "format": "uri" + } + }, + "files": { + "description": "A dictionary each of whose keys is a URI and each of whose values is a file object.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/file" + } + }, + "logicalLocations": { + "description": "A dictionary each of whose keys specifies a logical location such as a namespace, type or function.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/logicalLocation" + } + }, + "graphs": { + "description": "An array of one or more unique 'graph' objects.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/graph" + } + }, + "results": { + "description": "The set of results contained in an SARIF log. The results array can be omitted when a run is solely exporting rules metadata. It must be present (but may be empty) in the event that a log file represents an actual scan.", + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/definitions/result" + } + }, + "resources": { + "description": "Items that can be localized, such as message strings and rule metadata.", + "$ref": "#/definitions/resources" + }, + "instanceGuid": { + "description": "A stable, unique identifier for the run, in the form of a GUID.", + "type": "string" + }, + "correlationGuid": { + "description": "A stable, unique identifier for the equivalence class of logically identical results to which this result belongs, in the form of a GUID.", + "type": "string" + }, + "logicalId": { + "description": "A logical identifier for a run, for example, 'nightly Clang analyzer run'. Multiple runs of the same type can have the same stableId.", + "type": "string" + }, + "description": { + "description": "A description of the run.", + "$ref": "#/definitions/message" + }, + "automationLogicalId": { + "description": "A global identifier that allows the run to be correlated with other artifacts produced by a larger automation process.", + "type": "string" + }, + "baselineInstanceGuid": { + "description": "The 'instanceGuid' property of a previous SARIF 'run' that comprises the baseline that was used to compute result 'baselineState' properties for the run.", + "type": "string" + }, + "architecture": { + "description": "The hardware architecture for which the run was targeted.", + "type": "string" + }, + "richMessageMimeType": { + "description": "The MIME type of all rich text message properties in the run. Default: \"text/markdown;variant=GFM\"", + "type": "string", + "default": "text/markdown;variant=GFM" + }, + "redactionToken": { + "description": "The string used to replace sensitive information in a redaction-aware property.", + "type": "string" + }, + "defaultFileEncoding": { + "description": "Specifies the default encoding for any file object that refers to a text file.", + "type": "string" + }, + "columnKind": { + "description": "Specifies the unit in which the tool measures columns.", + "enum": [ + "utf16CodeUnits", + "unicodeCodePoints" + ] + }, + "properties": { + "description": "Key/value pairs that provide additional information about the run.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information about the run.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "tool", + "results" + ] + }, + "stack": { + "description": "A call stack that is relevant to a result.", + "additionalProperties": false, + "type": "object", + "properties": { + "message": { + "description": "A message relevant to this call stack.", + "$ref": "#/definitions/message" + }, + "frames": { + "description": "An array of stack frames that represent a sequence of calls, rendered in reverse chronological order, that comprise the call stack.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/stackFrame" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the stack.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "frames" + ] + }, + "stackFrame": { + "description": "A function call within a stack trace.", + "additionalProperties": false, + "type": "object", + "properties": { + "location": { + "description": "The location to which this stack frame refers.", + "$ref": "#/definitions/location" + }, + "module": { + "description": "The name of the module that contains the code of this stack frame.", + "type": "string" + }, + "threadId": { + "description": "The thread identifier of the stack frame.", + "type": "integer" + }, + "address": { + "description": "The address of the method or function that is executing.", + "type": "integer" + }, + "offset": { + "description": "The offset from the method or function that is executing.", + "type": "integer" + }, + "parameters": { + "description": "The parameters of the call that is executing.", + "type": "array", + "items": { + "type": "string", + "default": [] + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the stack frame.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "fullyQualifiedLogicalName" + ] + }, + "threadFlow": { + "type": "object", + "properties": { + "id": { + "description": "An string that uniquely identifies the threadFlow within the codeFlow in which it occurs.", + "type": "string" + }, + "message": { + "description": "A message relevant to the thread flow.", + "$ref": "#/definitions/message" + }, + "locations": { + "description": "A temporally ordered array of 'threadFlowLocation' objects, each of which describes a location visited by the tool in the course of producing the result.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/threadFlowLocation" + } + }, + "properties": { + "description": "Key/value pairs that provide additional information about the thread flow.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "locations" + ] + }, + "tool": { + "description": "The analysis tool that was run.", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the tool.", + "type": "string" + }, + "fullName": { + "description": "The name of the tool along with its version and any other useful identifying information, such as its locale.", + "type": "string" + }, + "version": { + "description": "The tool version, in whatever format the tool natively provides.", + "type": "string" + }, + "semanticVersion": { + "description": "The tool version in the format specified by Semantic Versioning 2.0.", + "type": "string" + }, + "fileVersion": { + "description": "The binary version of the tool's primary executable file (for operating systems such as Windows that provide that information).", + "type": "string", + "pattern": "[0-9]+(\\.[0-9]+){3}" + }, + "downloadUri": { + "description": "The absolute URI from which the tool can be downloaded.", + "type": "string", + "format": "uri" + }, + "sarifLoggerVersion": { + "description": "A version that uniquely identifies the SARIF logging component that generated this file, if it is versioned separately from the tool.", + "type": "string" + }, + "language": { + "description": "The tool language (expressed as an ISO 649 two-letter lowercase culture code) and region (expressed as an ISO 3166 two-letter uppercase subculture code associated with a country or region).", + "type": "string", + "default": "en-US" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the tool.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "name" + ] + }, + "versionControlDetails": { + "descriptipn": "Specifies the information necessary to retrieve a desired revision from a version control system.", + "type": "object", + "additionalProperties": false, + "properties": { + "uri": { + "description": "The absolute URI of the repository.", + "type": "string", + "format": "uri" + }, + "revisionId": { + "description": "A string that uniquely and permanently identifies the revision within the repository.", + "type": "string" + }, + "branch": { + "description": "The name of a branch containing the revision.", + "type": "string" + }, + "tag": { + "description": "A tag that has been applied to the revision.", + "type": "string" + }, + "timestamp": { + "description": "The date and time at which the revision was created.", + "type": "string", + "format": "date-time" + }, + "properties": { + "description": "Key/value pairs that provide additional information about the revision.", + "type": "object", + "additionalProperties": true, + "properties": { + "tags": { + "description": "A set of distinct strings that provide additional information.", + "type": "array", + "uniqueItems": true, + "default": [], + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "uri" + ] + } + } +} \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/valid-coding-standards-config.yml b/scripts/guideline_recategorization/test-data/valid-coding-standards-config.yml new file mode 100644 index 0000000000..6576920696 --- /dev/null +++ b/scripts/guideline_recategorization/test-data/valid-coding-standards-config.yml @@ -0,0 +1,7 @@ +guideline-recategorizations: + - rule-id: "A0-1-1" + category: "mandatory" + - rule-id: "A0-1-6" + category: "disapplied" + - rule-id: "A11-0-1" + category: "mandatory" diff --git a/scripts/guideline_recategorization/test-data/valid-sarif-recategorized.expected b/scripts/guideline_recategorization/test-data/valid-sarif-recategorized.expected new file mode 100644 index 0000000000..6de1af0fbf --- /dev/null +++ b/scripts/guideline_recategorization/test-data/valid-sarif-recategorized.expected @@ -0,0 +1 @@ +{"runs": [{"automationDetails": {"id": ".github/workflows/codeql-analysis.yml:analyze/language:python/"}, "conversion": {"tool": {"driver": {"name": "GitHub Code Scanning"}}}, "tool": {"driver": {"name": "CodeQL", "semanticVersion": "2.10.1", "rules": [{"id": "cpp/autosar/useless-assignment", "name": "cpp/autosar/useless-assignment", "shortDescription": {"text": "A0-1-1: Non-volatile variable assigned a value which is never used"}, "fullDescription": {"text": "A project shall not contain instances of non-volatile variables being given values that are not subsequently used."}, "defaultConfiguration": {"enabled": true, "level": "warning"}, "properties": {"tags": ["external/autosar/id/a0-1-1", "readability", "maintainability", "external/autosar/allocated-target/implementation", "external/autosar/enforcement/automated", "external/autosar/original-obligation/required", "external/autosar/obligation/mandatory"], "description": "A project shall not contain instances of non-volatile variables being given values\n that are not subsequently used.", "id": "cpp/autosar/useless-assignment", "kind": "problem", "name": "A0-1-1: Non-volatile variable assigned a value which is never used", "precision": "very-high", "problem.severity": "warning"}}, {"id": "cpp/autosar/unused-type-declarations", "name": "cpp/autosar/unused-type-declarations", "shortDescription": {"text": "A0-1-6: There should be no unused type declarations"}, "fullDescription": {"text": "Unused type declarations are either redundant or indicate a possible mistake on the part of the programmer."}, "defaultConfiguration": {"enabled": true, "level": "warning"}, "properties": {"tags": ["external/autosar/id/a0-1-6", "readability", "maintainability", "external/autosar/allocated-target/implementation", "external/autosar/enforcement/automated", "external/autosar/original-obligation/advisory", "external/autosar/obligation/disapplied"], "description": "Unused type declarations are either redundant or indicate a possible mistake on the\n part of the programmer.", "id": "cpp/autosar/unused-type-declarations", "kind": "problem", "name": "A0-1-6: There should be no unused type declarations", "precision": "very-high", "problem.severity": "warning"}}, {"id": "cpp/autosar/non-pod-type-should-be-defined-as-class", "name": "cpp/autosar/non-pod-type-should-be-defined-as-class", "shortDescription": {"text": "A11-0-1: A non-POD type should be defined as class"}, "fullDescription": {"text": "The class specifier forces the type to provide private access control for all its members by default. This is consistent with developer expectations, because it is expected that a class has its invariant, interface and could provide custom-defined constructors."}, "defaultConfiguration": {"enabled": true, "level": "note"}, "properties": {"tags": ["external/autosar/id/a11-0-1", "maintainability", "external/autosar/allocated-target/implementation", "external/autosar/enforcement/automated", "external/autosar/original-obligation/advisory", "external/autosar/obligation/mandatory"], "description": "The class specifier forces the type to provide private access control for all its\n members by default. This is consistent with developer expectations, because it is\n expected that a class has its invariant, interface and could provide custom-defined\n constructors.", "id": "cpp/autosar/non-pod-type-should-be-defined-as-class", "kind": "problem", "name": "A11-0-1: A non-POD type should be defined as class", "precision": "very-high", "problem.severity": "recommendation"}}]}}}], "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", "version": "2.1.0"} \ No newline at end of file diff --git a/scripts/guideline_recategorization/test-data/valid-sarif.json b/scripts/guideline_recategorization/test-data/valid-sarif.json new file mode 100644 index 0000000000..d9df2d2c19 --- /dev/null +++ b/scripts/guideline_recategorization/test-data/valid-sarif.json @@ -0,0 +1,115 @@ +{ + "runs": [ + { + "automationDetails": { + "id": ".github/workflows/codeql-analysis.yml:analyze/language:python/" + }, + "conversion": { + "tool": { + "driver": { + "name": "GitHub Code Scanning" + } + } + }, + "tool": { + "driver": { + "name": "CodeQL", + "semanticVersion": "2.10.1", + "rules": [ + { + "id": "cpp/autosar/useless-assignment", + "name": "cpp/autosar/useless-assignment", + "shortDescription": { + "text": "A0-1-1: Non-volatile variable assigned a value which is never used" + }, + "fullDescription": { + "text": "A project shall not contain instances of non-volatile variables being given values that are not subsequently used." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "external/autosar/id/a0-1-1", + "readability", + "maintainability", + "external/autosar/allocated-target/implementation", + "external/autosar/enforcement/automated", + "external/autosar/obligation/required" + ], + "description": "A project shall not contain instances of non-volatile variables being given values\n that are not subsequently used.", + "id": "cpp/autosar/useless-assignment", + "kind": "problem", + "name": "A0-1-1: Non-volatile variable assigned a value which is never used", + "precision": "very-high", + "problem.severity": "warning" + } + }, + { + "id": "cpp/autosar/unused-type-declarations", + "name": "cpp/autosar/unused-type-declarations", + "shortDescription": { + "text": "A0-1-6: There should be no unused type declarations" + }, + "fullDescription": { + "text": "Unused type declarations are either redundant or indicate a possible mistake on the part of the programmer." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "external/autosar/id/a0-1-6", + "readability", + "maintainability", + "external/autosar/allocated-target/implementation", + "external/autosar/enforcement/automated", + "external/autosar/obligation/advisory" + ], + "description": "Unused type declarations are either redundant or indicate a possible mistake on the\n part of the programmer.", + "id": "cpp/autosar/unused-type-declarations", + "kind": "problem", + "name": "A0-1-6: There should be no unused type declarations", + "precision": "very-high", + "problem.severity": "warning" + } + }, + { + "id": "cpp/autosar/non-pod-type-should-be-defined-as-class", + "name": "cpp/autosar/non-pod-type-should-be-defined-as-class", + "shortDescription": { + "text": "A11-0-1: A non-POD type should be defined as class" + }, + "fullDescription": { + "text": "The class specifier forces the type to provide private access control for all its members by default. This is consistent with developer expectations, because it is expected that a class has its invariant, interface and could provide custom-defined constructors." + }, + "defaultConfiguration": { + "enabled": true, + "level": "note" + }, + "properties": { + "tags": [ + "external/autosar/id/a11-0-1", + "maintainability", + "external/autosar/allocated-target/implementation", + "external/autosar/enforcement/automated", + "external/autosar/obligation/advisory" + ], + "description": "The class specifier forces the type to provide private access control for all its\n members by default. This is consistent with developer expectations, because it is\n expected that a class has its invariant, interface and could provide custom-defined\n constructors.", + "id": "cpp/autosar/non-pod-type-should-be-defined-as-class", + "kind": "problem", + "name": "A11-0-1: A non-POD type should be defined as class", + "precision": "very-high", + "problem.severity": "recommendation" + } + } + ] + } + } + } + ], + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0" +} \ No newline at end of file diff --git a/scripts/help/cert-help-extraction.py b/scripts/help/cert-help-extraction.py index 6bd1abccd5..f785b0955f 100755 --- a/scripts/help/cert-help-extraction.py +++ b/scripts/help/cert-help-extraction.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 from argparse import ArgumentParser +from typing import Generator import tempfile import re import urllib.request @@ -23,6 +24,7 @@ CERT_WIKI = "https://wiki.sei.cmu.edu" RULES_LIST_C = "/confluence/display/c/2+Rules" +RECOMMENDED_LIST_C = "/confluence/display/c/3+Recommendations" RULES_LIST_CPP = "/confluence/display/cplusplus/2+Rules" cache_path = script_path.parent / '.cache' @@ -47,16 +49,22 @@ def soupify(url: str) -> BeautifulSoup: return BeautifulSoup(content, 'html.parser') - -def get_rules(): - rules = [] - for soup in [soupify(f"{CERT_WIKI}{RULES_LIST_C}"), soupify(f"{CERT_WIKI}{RULES_LIST_CPP}")]: +def get_rule_listings() -> Generator[Tag, None, None]: + for rule_list_id in [RULES_LIST_C, RULES_LIST_CPP]: + soup = soupify(f"{CERT_WIKI}{rule_list_id}") if soup == None: - return None - - rule_listing_start = soup.find( + continue + + yield soup.find( "h1", string="Rule Listing") + soup = soupify(f"{CERT_WIKI}{RECOMMENDED_LIST_C}") + if soup != None: + yield soup.find("h1", string="Recommendation Listing") + +def get_rules(): + rules = [] + for rule_listing_start in get_rule_listings(): for link in rule_listing_start.next_element.next_element.find_all('a'): if '-C' in link.string: rule, title = map(str.strip, link.string.split('.', 1)) @@ -214,6 +222,8 @@ def helper(node): # Fix a broken url present in many CERT-C pages if node.name == 'a' and 'href' in node.attrs and node['href'] == "http://BB. Definitions#vulnerability": node['href'] = "https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability" + elif node.name == 'a' and 'href' in node.attrs and node['href'] == "http://BB. Definitions#unexpected behavior": + node['href'] = "https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior" # Turn relative URLs into absolute URLS elif node.name == 'a' and 'href' in node.attrs and node['href'].startswith("/confluence"): node['href'] = f"{CERT_WIKI}{node['href']}" diff --git a/scripts/install-packs.py b/scripts/install-packs.py new file mode 100644 index 0000000000..ab45c32818 --- /dev/null +++ b/scripts/install-packs.py @@ -0,0 +1,23 @@ +import argparse +import os +import subprocess +import get_workspace_packs + +parser = argparse.ArgumentParser(description="Install CodeQL library pack dependencies.") +parser.add_argument('--mode', required=False, choices=['use-lock', 'update', 'verify', 'no-lock'], default="use-lock", help="Installation mode, identical to the `--mode` argument to `codeql pack install`") +parser.add_argument('--codeql', required=False, default='codeql', help="Path to the `codeql` executable.") +args = parser.parse_args() + +# Find the root of the repo +root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +packs = get_workspace_packs.get_workspace_packs(root) + +# Find the CodeQL packs in the repo. This can also return packs outside of the repo, if those packs +# are installed in a sibling directory to the CLI. +for pack in packs: + pack_path = os.path.join(root, pack) + # Run `codeql pack install` to install dependencies. + command = [args.codeql, 'pack', 'install', '--allow-prerelease', '--mode', args.mode, pack_path] + print(f'Running `{" ".join(command)}`') + subprocess.check_call(command) diff --git a/scripts/matrix_testing/CompileFixTool.ps1 b/scripts/matrix_testing/CompileFixTool.ps1 index ed4db02840..86a6481b8e 100755 --- a/scripts/matrix_testing/CompileFixTool.ps1 +++ b/scripts/matrix_testing/CompileFixTool.ps1 @@ -14,18 +14,18 @@ param( [string] $Language, - # The compiler to use. Valid values are 'clang' and 'arm-clang'. + # The compiler to use. [Parameter(Mandatory)] - [ValidateSet('clang', 'armclang', 'tiarmclang')] + [ValidateSet('clang', 'armclang', 'tiarmclang', 'gcc', 'qcc')] [string] $Configuration ) -Import-Module -Name "$PSScriptRoot\..\PSCodingStandards\CodingStandards" +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" -. "$PSScriptRoot\NewDatabaseForRule.ps1" -. "$PSScriptRoot\Config.ps1" -. "$PSScriptRoot\Get-CompilerExecutable.ps1" +. "$PSScriptRoot/NewDatabaseForRule.ps1" +. "$PSScriptRoot/Config.ps1" +. "$PSScriptRoot/Get-CompilerExecutable.ps1" # # Verify All the Required CLI Tools are Installed @@ -37,7 +37,7 @@ Write-Host -ForegroundColor ([ConsoleColor]2) "OK" $CODEQL_VERSION = (codeql version --format json | ConvertFrom-Json).version Write-Host "Checking 'codeql' version = $REQUIRED_CODEQL_VERSION...." -NoNewline -if (-Not $CODEQL_VERSION -eq $REQUIRED_CODEQL_VERSION) { +if (-Not ($CODEQL_VERSION -eq $REQUIRED_CODEQL_VERSION)) { throw "Invalid CodeQL version $CODEQL_VERSION. Please install $REQUIRED_CODEQL_VERSION." } Write-Host -ForegroundColor ([ConsoleColor]2) "OK" diff --git a/scripts/matrix_testing/Config.ps1 b/scripts/matrix_testing/Config.ps1 index 9d64e20452..c904a06863 100644 --- a/scripts/matrix_testing/Config.ps1 +++ b/scripts/matrix_testing/Config.ps1 @@ -1,30 +1,39 @@ +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" + $COMPILER_MAPPINGS = @{ "cpp" = @{ "clang" = "clang++"; + "gcc" = "g++"; "armclang" = "armclang"; "tiarmclang" = "tiarmclang"; + "qcc" = "qcc"; }; "c" = @{ "clang" = "clang"; + "gcc" = "gcc"; + "qcc" = "qcc"; }; } $COMPILER_ARGS = @{ "cpp" = @{ "clang" = "-std=c++14 -fsyntax-only"; + "gcc" = "-std=c++14 -fsyntax-only"; "armclang" = "-std=c++14 -fsyntax-only --target=arm-arm-none-eabi"; "tiarmclang" = "-std=c++14 -fsyntax-only --target=arm-arm-none-eabi"; + "qcc" = "-lang-c++ -V8.3.0 -Wc,-fsyntax-only -c -nopipe -std=c++14 -D_QNX_SOURCE -Vgcc_ntoaarch64le_cxx"; }; "c" = @{ - "clang" = "-fsyntax-only"; + "gcc" = "-fsyntax-only -std=c11"; + "clang" = "-fsyntax-only -std=c11"; + "qcc" = "-V8.3.0 -Wc,-fsyntax-only -c -nopipe -std=c11 -Vgcc_ntoaarch64le"; }; } -$AVAILABLE_SUITES = @("CERT-C++", "AUTOSAR", "MISRA-C-2012", "CERT-C") -$REQUIRED_CODEQL_VERSION = "2.6.3" +$REQUIRED_CODEQL_VERSION = (Get-Content (Join-Path (Get-RepositoryRoot) "supported_codeql_configs.json") | ConvertFrom-Json).supported_environment.codeql_cli $REPORT_QUERY = @" diff --git a/scripts/matrix_testing/CreateMatrixTestReport.ps1 b/scripts/matrix_testing/CreateMatrixTestReport.ps1 index 3af69e6353..a8de5034ce 100644 --- a/scripts/matrix_testing/CreateMatrixTestReport.ps1 +++ b/scripts/matrix_testing/CreateMatrixTestReport.ps1 @@ -53,7 +53,7 @@ All Parameters: Accept wildcard characters? false -Configuration - The compiler to use. Valid values are 'clang' and 'arm-clang'. + The compiler to use. Required? true Position? named @@ -123,6 +123,12 @@ param( [string] $ReportDir = (Get-Location), + # Skip summary report -- used for Linux hosts that don't support + # the OLE database stuff. + [Parameter(Mandatory = $false)] + [switch] + $SkipSummaryReport, + # Tells the script to use the sytem tmp directory instead of the rule # directory. [Parameter(Mandatory = $false)] @@ -134,16 +140,16 @@ param( [string] $NumThreads = 10, - # The compiler to use. Valid values are 'clang' and 'arm-clang'. + # The compiler to use. [Parameter(Mandatory)] - [ValidateSet('clang', 'armclang', 'tiarmclang')] + [ValidateSet('clang', 'armclang', 'tiarmclang', 'gcc', 'qcc')] [string] $Configuration, # For a suite, the suites we support. Valid values are 'CERT-C++' and - # 'AUTOSAR' and MISRA-C-2012 and CERT-C + # 'AUTOSAR' and MISRA-C-2012, MISRA-C++-2023 and CERT-C [Parameter(Mandatory, ParameterSetName = 'Suite')] - [ValidateSet("CERT-C++", "AUTOSAR", "MISRA-C-2012", "CERT-C")] + [ValidateSet("CERT-C++", "AUTOSAR", "MISRA-C-2012", "CERT-C", "MISRA-C++-2023")] [string] $SuiteName, @@ -202,11 +208,11 @@ param( $PackageName ) -Import-Module -Name "$PSScriptRoot\..\PSCodingStandards\CodingStandards" +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" -. "$PSScriptRoot\CreateSummaryReport.ps1" -. "$PSScriptRoot\Get-CompilerExecutable.ps1" -. "$PSScriptRoot\Config.ps1" +. "$PSScriptRoot/CreateSummaryReport.ps1" +. "$PSScriptRoot/Get-CompilerExecutable.ps1" +. "$PSScriptRoot/Config.ps1" $REPORT = @() $queriesToCheck = @() @@ -245,6 +251,7 @@ else { Write-Host "Loaded $($queriesToCheck.Count) Queries." } + # # Step 2: Verify All the Required CLI Tools are Installed # @@ -255,7 +262,7 @@ Write-Host -ForegroundColor ([ConsoleColor]2) "OK" $CODEQL_VERSION = (codeql version --format json | ConvertFrom-Json).version Write-Host "Checking 'codeql' version = $REQUIRED_CODEQL_VERSION...." -NoNewline -if (-Not $CODEQL_VERSION -eq $REQUIRED_CODEQL_VERSION) { +if (-Not ($CODEQL_VERSION -eq $REQUIRED_CODEQL_VERSION)) { throw "Invalid CodeQL version $CODEQL_VERSION. Please install $REQUIRED_CODEQL_VERSION." } Write-Host -ForegroundColor ([ConsoleColor]2) "OK" @@ -271,11 +278,13 @@ Write-Host -ForegroundColor ([ConsoleColor]2) "OK" # $jobRows = $queriesToCheck | ForEach-Object -ThrottleLimit $NumThreads -Parallel { - Import-Module -Name "$using:PSScriptRoot\..\PSCodingStandards\CodingStandards" + Import-Module -Name "$using:PSScriptRoot/../PSCodingStandards/CodingStandards" - #. "$using:PSScriptRoot\GetTestDirectory.ps1" - . "$using:PSScriptRoot\NewDatabaseForRule.ps1" - . "$using:PSScriptRoot\ExecuteQueryAndDecodeAsJson.ps1" + . "$using:PSScriptRoot/NewDatabaseForRule.ps1" + . "$using:PSScriptRoot/ExecuteQueryAndDecodeAsJson.ps1" + . "$using:PSScriptRoot/Get-CompilerSpecificFiles.ps1" + . "$using:PSScriptRoot/Pop-CompilerSpecificFiles.ps1" + . "$using:PSScriptRoot/Push-CompilerSpecificFiles.ps1" $q = $_ @@ -283,121 +292,146 @@ $jobRows = $queriesToCheck | ForEach-Object -ThrottleLimit $NumThreads -Parallel $CurrentRuleName = $q.__memberof_rule $CurrentQueryName = $q.short_name $CurrentPackageName = $q.__memberof_package - # for the report - $row = @{ - "SUITE" = $CurrentSuiteName; - "PACKAGE" = $CurrentPackageName; - "RULE" = $CurrentRuleName; - "QUERY" = $CurrentQueryName; - "COMPILE_PASS" = $false; - "EXTRACTOR_PASS" = $false; - "EXTRACTOR_ERRORS" = ""; - "TEST_PASS" = $false ; - "TEST_DIFFERENCE" = ""; - } - - Write-Host "Resolving pack 'codeql/cpp-queries'...." -NoNewline - $CODEQL_CPP_QUERIES_PATH = (codeql resolve qlpacks --format json | ConvertFrom-Json)."codeql/cpp-queries" - if ( -Not (Test-Path -Path $CODEQL_CPP_QUERIES_PATH -PathType Container) ) { - Write-Host "Could not resolve pack 'codeql/cpp-queries'. Please install the pack 'codeql/cpp-queries'." - return $row - } - Write-Host -ForegroundColor ([ConsoleColor]2) "OK" - - Write-Host "====================[Rule=$CurrentRuleName,Suite=$CurrentSuiteName/Query=$CurrentQueryName]====================" - - $testDirectory = (Get-TestDirectory -RuleObject $q -Language $using:Language) - Write-Host "Compiling database in $testDirectory..." -NoNewline - try { - $db = New-Database-For-Rule -RuleName $CurrentRuleName -RuleTestDir $testDirectory -Configuration $using:Configuration -Language $using:Language - Write-Host -ForegroundColor ([ConsoleColor]2) "OK" + # all the test directories -- there may be more than one for a given rule + $testDirs = (Get-ATestDirectory -RuleObject $q -Language $using:Language) + + foreach($testDirectory in $testDirs){ + + Write-Host "Acquiring lock for $testDirectory" + $Mutex = New-Object -TypeName System.Threading.Mutex -ArgumentList $false, ("__Matrix_" + $testDirectory.Replace([IO.Path]::DirectorySeparatorChar,"_")); + $Mutex.WaitOne() | Out-Null; + Write-Host "Locked $testDirectory" + + # for the report + $row = @{ + "SUITE" = $CurrentSuiteName; + "PACKAGE" = $CurrentPackageName; + "RULE" = $CurrentRuleName; + "QUERY" = $CurrentQueryName; + "COMPILE_PASS" = $false; + "COMPILE_ERROR_OUTPUT" = ""; + "TEST_PASS" = $false ; + "TEST_DIFFERENCE" = ""; + } + + + + Write-Host "====================[Rule=$CurrentRuleName,Suite=$CurrentSuiteName/Query=$CurrentQueryName]====================" + + + try { + ########################################################### + ########################################################### + # Push context + ########################################################### + + if ($q.shared_implementation_short_name) { + $fileSet = (Get-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -TestDirectory $testDirectory -Query $q.shared_implementation_short_name) + } + else { + $fileSet = (Get-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -TestDirectory $testDirectory -Query $CurrentQueryName) + } + + if($fileSet){ + $context = Push-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -FileSet $fileSet + } + + Write-Host "Compiling database in $testDirectory..." -NoNewline + + try { + $db = New-Database-For-Rule -RuleName $CurrentRuleName -RuleTestDir $testDirectory -Configuration $using:Configuration -Language $using:Language + Write-Host -ForegroundColor ([ConsoleColor]2) "OK" + } + catch { + Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" + $row["COMPILE_ERROR_OUTPUT"] = $_ + + continue # although it is unlikely to succeed with the next rule skipping to the next rule + # ensures all of the rules will be reported in the + # output. + } + + $row["COMPILE_PASS"] = $true + + Write-Host "Checking expected output..." + + # Dragons below 🐉🐉🐉 + # + # Note this technique uses so-called "wizard" settings to make it possible + # to compare hand compiled databases using qltest. The relative paths and + # other options are required to be set as below (especially the detail about + # the relative path of the dataset and the test). + + # the "dataset" should be the `db-cpp` directory inside the database + # directory. HOWEVER. It should be the path relative to the test directory. + + $rulePath = Resolve-Path $testDirectory + $dbPath = Resolve-Path $db + + Write-Host "Resolving database $dbPath relative to test directory $rulePath" + $dataset = Resolve-Path (Join-Path $dbPath "db-cpp") + + Push-Location $rulePath + $datasetRelPath = Resolve-Path -Relative $dataset + Pop-Location + + Write-Host "Using relative path: $datasetRelPath" + + # Actually do the qltest run. + # codeql test run --dataset "relpath" + + if ($q.shared_implementation_short_name) { + $qlRefFile = Join-Path $rulePath "$($q.shared_implementation_short_name).ql" + } + else { + $qlRefFile = Join-Path $rulePath "$CurrentQueryName.qlref" + } + + Write-Host "codeql test run $qlRefFile --search-path . --dataset=`"$datasetRelPath`"" + + $stdOut = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) + $stdErr = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) + + + Write-Host "Standard Out Buffered to: $stdOut" + Write-Host "Standard Error Buffered to: $stdErr" + + $procDetails = Start-Process -FilePath "codeql" -PassThru -NoNewWindow -Wait -ArgumentList "test run $qlRefFile --search-path . --dataset=`"$datasetRelPath`"" -RedirectStandardOutput $stdOut -RedirectStandardError $stdErr + + if (-Not $procDetails.ExitCode -eq 0) { + + Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" + Get-Content $stdOut | Out-String | Write-Host + + $row["TEST_DIFFERENCE"] = Get-Content $stdOut | Out-String + + } + else { + $row["TEST_PASS"] = $true + Write-Host -ForegroundColor ([ConsoleColor]2) "OK" + } + }finally { + + # output current row state + $row + + + ########################################################### + ########################################################### + # Context is restored here + ########################################################### + if($context){ + Pop-CompilerSpecificFiles -Context $context + } + + # release any held mutexes + $Mutex.ReleaseMutex(); + Write-Host "Released $testDirectory" + } } - catch { - Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" - - return $row # although it is unlikely to succeed with the next rule skipping to the next rule - # ensures all of the rules will be reported in the - # output. - } - - $row["COMPILE_PASS"] = $true - Write-Host "Validating extractor results..." -NoNewline - - try { - $diagnostics = Execute-QueryAndDecodeAsJson -DatabasePath $db -QueryPath $diagnostic_query - }catch { - Write-Host -ForegroundColor ([ConsoleColor]4) $_Exception.Message - return $row - } - - if ( $diagnostics.'#select'.tuples.Length -eq 0 ) { - $row["EXTRACTOR_PASS"] = $true - Write-Host -ForegroundColor ([ConsoleColor]2) "OK" - } else { - Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" - $row["EXTRACTOR_ERRORS"] = $diagnostics | ConvertTo-Json -Depth 100 - } - - Write-Host "Checking expected output..." - - # Note this technique uses so-called "wizard" settings to make it possible - # to compare hand compiled databases using qltest. The relative paths and - # other options are required to be set as below (especially the detail about - # the relative path of the dataset and the test). - - # the "dataset" should be the `db-cpp` directory inside the database - # directory. HOWEVER. It should be the path relative to the test directory. - - $rulePath = Resolve-Path $testDirectory - $dbPath = Resolve-Path $db - - Write-Host "Resolving database $dbPath relative to test directory $rulePath" - $dataset = Resolve-Path (Join-Path $dbPath "db-cpp") - - Push-Location $rulePath - $datasetRelPath = Resolve-Path -Relative $dataset - Pop-Location - - Write-Host "Using relative path: $datasetRelPath" - - # Actually do the qltest run. - # codeql test run --dataset "relpath" - - if ($q.shared_implementation_short_name) { - $qlRefFile = Join-Path $rulePath "$($q.shared_implementation_short_name).ql" - } - else { - $qlRefFile = Join-Path $rulePath "$CurrentQueryName.qlref" - } - - Write-Host "codeql test run $qlRefFile --dataset=`"$datasetRelPath`"" - - $stdOut = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) - $stdErr = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) - - - Write-Host "Standard Out Buffered to: $stdOut" - Write-Host "Standard Error Buffered to: $stdErr" - - - $procDetails = Start-Process -FilePath "codeql" -PassThru -NoNewWindow -Wait -ArgumentList "test run $qlRefFile --dataset=`"$datasetRelPath`"" -RedirectStandardOutput $stdOut -RedirectStandardError $stdErr - - if (-Not $procDetails.ExitCode -eq 0) { - - Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" - Get-Content $stdOut | Out-String | Write-Host - - $row["TEST_DIFFERENCE"] = Get-Content $stdOut | Out-String - - } - else { - $row["TEST_PASS"] = $true - Write-Host -ForegroundColor ([ConsoleColor]2) "OK" - } - - return $row + # go to next row } # combine the outputs @@ -420,6 +454,8 @@ foreach ($r in $REPORT) { [PSCustomObject]$r | Export-CSV -Path $reportOutputFile -Append -NoTypeInformation } -# write out a summary -Write-Host "Writing summary report to $summaryReportOutputFile" -Create-Summary-Report -DataFile $reportOutputFile -OutputFile $summaryReportOutputFile \ No newline at end of file +if (-not $SkipSummaryReport){ + # write out a summary + Write-Host "Writing summary report to $summaryReportOutputFile" + Create-Summary-Report -DataFile $reportOutputFile -OutputFile $summaryReportOutputFile +} diff --git a/scripts/matrix_testing/CreateSummaryReport.ps1 b/scripts/matrix_testing/CreateSummaryReport.ps1 index 0106848f13..0aec3eb0d0 100644 --- a/scripts/matrix_testing/CreateSummaryReport.ps1 +++ b/scripts/matrix_testing/CreateSummaryReport.ps1 @@ -1,4 +1,4 @@ -. "$PSScriptRoot\Config.ps1" +. "$PSScriptRoot/Config.ps1" function Create-Summary-Report { param([Parameter(Mandatory)] [string] diff --git a/scripts/matrix_testing/Get-CompilerArgs.ps1 b/scripts/matrix_testing/Get-CompilerArgs.ps1 index 5ce7b52ab8..2b970120c2 100644 --- a/scripts/matrix_testing/Get-CompilerArgs.ps1 +++ b/scripts/matrix_testing/Get-CompilerArgs.ps1 @@ -1,12 +1,25 @@ -. "$PSScriptRoot\Config.ps1" +. "$PSScriptRoot/Config.ps1" function Get-CompilerArgs { - param([Parameter(Mandatory)] + param( + [Parameter(Mandatory)] [string] $Configuration, [Parameter(Mandatory)] + [string] + $TestDirectory, + [Parameter(Mandatory)] [ValidateSet('c', 'cpp')] [string] $Language ) - return $COMPILER_ARGS[$Language][$Configuration] + $baseArgs = $COMPILER_ARGS[$Language][$Configuration] + + $optionsFile = (Join-Path $TestDirectory "options.$Configuration") + + # perhaps there is an options file? + if(Test-Path $optionsFile){ + return $baseArgs + " " + (Get-Content $optionsFile) + } + + return $baseArgs } \ No newline at end of file diff --git a/scripts/matrix_testing/Get-CompilerExecutable.ps1 b/scripts/matrix_testing/Get-CompilerExecutable.ps1 index 9ca9bce889..45b83ea1fa 100644 --- a/scripts/matrix_testing/Get-CompilerExecutable.ps1 +++ b/scripts/matrix_testing/Get-CompilerExecutable.ps1 @@ -1,4 +1,4 @@ -. "$PSScriptRoot\Config.ps1" +. "$PSScriptRoot/Config.ps1" function Get-CompilerExecutable { param([Parameter(Mandatory)] [string] diff --git a/scripts/matrix_testing/Get-CompilerSpecificFiles.ps1 b/scripts/matrix_testing/Get-CompilerSpecificFiles.ps1 new file mode 100644 index 0000000000..6197a05650 --- /dev/null +++ b/scripts/matrix_testing/Get-CompilerSpecificFiles.ps1 @@ -0,0 +1,39 @@ +. "$PSScriptRoot/Config.ps1" +function Get-CompilerSpecificFiles { + param([Parameter(Mandatory)] + [string] + $Configuration, + [Parameter(Mandatory)] + [ValidateSet('c', 'cpp')] + [string] + $Language, + [Parameter(Mandatory)] + [string] + $TestDirectory, + [Parameter(Mandatory)] + [string] + $Query + ) + # + # Convention is as follows: + # + # For test files: + # + # file.c/cpp is used for ALL compilers + # file.c./file.cpp. is used for + # + # file.expected is used for all compilers + # file.expected. is used for + Write-Host "Scanning for compiler specific files in $TestDirectory" + + foreach($f in (Get-ChildItem -Filter "*.$Language.$Configuration" $TestDirectory)){ + Write-Host "Found file $f..." + $f + } + + foreach($f in (Get-ChildItem -Filter "$Query.expected.$Configuration" $TestDirectory)){ + Write-Host "Found file $f..." + $f + } + +} \ No newline at end of file diff --git a/scripts/matrix_testing/NewDatabaseForRule.ps1 b/scripts/matrix_testing/NewDatabaseForRule.ps1 index 6dfd6fa86d..c7b44a5b0f 100644 --- a/scripts/matrix_testing/NewDatabaseForRule.ps1 +++ b/scripts/matrix_testing/NewDatabaseForRule.ps1 @@ -1,6 +1,6 @@ -. "$PSScriptRoot\Get-CompilerExecutable.ps1" -. "$PSScriptRoot\Get-CompilerArgs.ps1" -. "$PSScriptRoot\GetNewDBName.ps1" +. "$PSScriptRoot/Get-CompilerExecutable.ps1" +. "$PSScriptRoot/Get-CompilerArgs.ps1" +. "$PSScriptRoot/GetNewDBName.ps1" function New-Database-For-Rule { @@ -21,13 +21,12 @@ function New-Database-For-Rule { Write-Host "Creating Database for Rule $RuleName..." - $cppFiles = Get-ChildItem $RuleTestDir/*.c* + $cppFiles = Get-ChildItem $RuleTestDir/*.$Language $cppFilesString = ([String]::Join(' ', $cppFiles)) Write-Host "Found '.cpp' files $cppFilesString." $CompilerExecutable = Get-CompilerExecutable -Configuration $Configuration -Language $Language - $CompilerArgs = Get-CompilerArgs -Configuration $Configuration -Language $Language - + $CompilerArgs = Get-CompilerArgs -Configuration $Configuration -Language $Language -TestDirectory $RuleTestDir $BUILD_COMMAND = "$CompilerExecutable $CompilerArgs $cppFilesString" if ($UseTmpDir) { @@ -39,10 +38,14 @@ function New-Database-For-Rule { Write-Host "codeql database create -l cpp -s $RuleTestDir --command='$BUILD_COMMAND' $DB_PATH" - $procDetails = Start-Process -FilePath "codeql" -PassThru -NoNewWindow -Wait -ArgumentList "database create -l cpp -s $RuleTestDir --command=`"$BUILD_COMMAND`" $DB_PATH" + $stdOut = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) + + $procDetails = Start-Process -FilePath "codeql" -RedirectStandardOutput $stdOut -PassThru -NoNewWindow -Wait -ArgumentList "database create -l cpp -s $RuleTestDir --command=`"$BUILD_COMMAND`" $DB_PATH" + + Get-Content $stdOut | Out-String | Write-Host if (-Not $procDetails.ExitCode -eq 0) { - throw "Database creation failed." + throw Get-Content $stdOut | Out-String } return $DB_PATH diff --git a/scripts/matrix_testing/Pop-CompilerSpecificFiles.ps1 b/scripts/matrix_testing/Pop-CompilerSpecificFiles.ps1 new file mode 100644 index 0000000000..9f78aac381 --- /dev/null +++ b/scripts/matrix_testing/Pop-CompilerSpecificFiles.ps1 @@ -0,0 +1,23 @@ +. "$PSScriptRoot/Config.ps1" +function Pop-CompilerSpecificFiles { + param([Parameter(Mandatory)] + [hashtable[]] + $Context + ) + + foreach($c in $Context){ + + $origin = $c.origin + $temp = $c.temp + + if($temp){ + Write-Host "Restoring $temp -> $origin" + Copy-Item -Force -Path $temp -Destination $origin + }else { + # otherwise we just need to delete the origin + Write-Host "Removing unneeded context item $origin" + Remove-Item -Force $origin + } + } + +} \ No newline at end of file diff --git a/scripts/matrix_testing/Push-CompilerSpecificFiles.ps1 b/scripts/matrix_testing/Push-CompilerSpecificFiles.ps1 new file mode 100644 index 0000000000..2265dfc2db --- /dev/null +++ b/scripts/matrix_testing/Push-CompilerSpecificFiles.ps1 @@ -0,0 +1,60 @@ +. "$PSScriptRoot/Config.ps1" +function Push-CompilerSpecificFiles { + param([Parameter(Mandatory)] + [System.IO.FileSystemInfo[]] + $FileSet, + [string] + $Configuration, + [Parameter(Mandatory)] + [ValidateSet('c', 'cpp')] + $Language + ) + + # for each file, move it to a temporary location + foreach($f in $FileSet){ + # + # Convention is as follows: + # + # For test files: + # + # file.c/cpp is used for ALL compilers + # file.c./file.cpp. is used for + # + # file.expected is used for all compilers + # file.expected. is used for + + $tmp = New-TemporaryFile + + # + # Note -- it is not necessary for the file we are going to replace + # to exist. If it DOES NOT exist, we simply delete the compiler specific + # file afterwards. + + # transform the compiler specific file to the generic one + $originFilePath = $f.FullName.Replace(".$Configuration", "") + + # IF it exists, copy the originFile to a temp location and replace it + # with the specific file. + if(Test-Path $originFilePath){ + + $originFile = Get-Item $originFilePath + + Write-Host "Moving generic file $originFile to $tmp..." + Move-Item -Force -Path $originFile -Destination $tmp + Write-Host "Copying $f to generic file $originFile" + Copy-Item -Path $f -Destination $originFile + + @{"origin"=$originFile; "temp"=$tmp;} + }else{ + + $originFile = New-Item $originFilePath + + Write-Host "Copying $f to generic file $originFile" + Copy-Item -Path $f -Destination $originFile + + #we set $temp to $null since we don't want to copy anything + # back + @{"origin"=$originFile; "temp"=$null;} + } + } +} \ No newline at end of file diff --git a/scripts/performance_testing/Config.ps1 b/scripts/performance_testing/Config.ps1 new file mode 100644 index 0000000000..4ba1db78fd --- /dev/null +++ b/scripts/performance_testing/Config.ps1 @@ -0,0 +1,4 @@ +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" + +$REQUIRED_CODEQL_VERSION = (Get-Content (Join-Path (Get-RepositoryRoot) "supported_codeql_configs.json") | ConvertFrom-Json).supported_environment.codeql_cli + diff --git a/scripts/performance_testing/Convert-DurationStringToMs.ps1 b/scripts/performance_testing/Convert-DurationStringToMs.ps1 new file mode 100644 index 0000000000..043290deab --- /dev/null +++ b/scripts/performance_testing/Convert-DurationStringToMs.ps1 @@ -0,0 +1,77 @@ +function Convert-DurationStringToMs { + param( + [Parameter(Mandatory)] + [string] + $DurationString + ) + + $durationStack = @() + $unitStack = @() + + + $durationBuff = $false + $unitBuff = $false + + for($i=0; $i -le $DurationString.Length; $i++){ + $s = $DurationString[$i] + #Write-Host $s + if($s -match "\d|\."){ # consume if it is a number or a decimal + + # init buffer + if($durationBuff -eq $false){ + $durationBuff = "" + } + + # accept last unit + if(-Not $unitBuff -eq $false){ + $unitStack += $unitBuff + $unitBuff = $false + } + + $durationBuff += $s + }else{ # otherwise it is a unit -- multiply by it to get the ms. + + # init buffer + if($unitBuff -eq $false){ + $unitBuff = "" + } + + # accept last digit buffer + if(-Not $durationBuff -eq $false){ + $durationStack += $durationBuff + $durationBuff = $false + } + + $unitBuff += $s + } + } + + # should always end with accepting the last one (because it will be a + # unit) + $unitStack += $unitBuff + + $totalMs = 0 + + for($i=0; $i -le $unitStack.Length; $i++){ + + $time = [System.Convert]::ToDecimal($durationStack[$i]) + $unit = $unitStack[$i] + + if($unit -eq 'h'){ + $time = $time * (60*60*1000) + } + if($unit -eq 'm'){ + $time = $time * (60*1000) + } + if($unit -eq 's'){ + $time = $time * (1000) + } + if($unit -eq 'ms'){ + $time = $time + } + + $totalMs += $time + } + + return $totalMs +} \ No newline at end of file diff --git a/scripts/performance_testing/Get-DurationString.ps1 b/scripts/performance_testing/Get-DurationString.ps1 new file mode 100644 index 0000000000..cb38133427 --- /dev/null +++ b/scripts/performance_testing/Get-DurationString.ps1 @@ -0,0 +1,12 @@ +function Get-DurationString { + param( + [Parameter(Mandatory)] + [string] + $LogLine + ) + $In = $LogLine.IndexOf('eval')+5 + $Out = $LogLine.indexof(']') + + return $LogLine.substring($In, $Out - $In) +} + diff --git a/scripts/performance_testing/Get-QueryString.ps1 b/scripts/performance_testing/Get-QueryString.ps1 new file mode 100644 index 0000000000..d39ee863c1 --- /dev/null +++ b/scripts/performance_testing/Get-QueryString.ps1 @@ -0,0 +1,12 @@ +function Get-QueryString { + param( + [Parameter(Mandatory)] + [string] + $LogLine + ) + $In = $LogLine.IndexOf('Evaluation done; writing results to ')+36 + $Out = $LogLine.IndexOf('.bqrs') + + return $LogLine.SubString($In, $Out - $In) +} + diff --git a/scripts/performance_testing/Get-TestTmpDirectory.ps1 b/scripts/performance_testing/Get-TestTmpDirectory.ps1 new file mode 100644 index 0000000000..d2e0fb3f8d --- /dev/null +++ b/scripts/performance_testing/Get-TestTmpDirectory.ps1 @@ -0,0 +1,5 @@ +function Get-TestTmpDirectory { + $Dir = [System.IO.Path]::GetTempPath() + return Join-Path $Dir "$([System.Guid]::NewGuid())" +} + diff --git a/scripts/performance_testing/README.md b/scripts/performance_testing/README.md new file mode 100644 index 0000000000..90b997b6fc --- /dev/null +++ b/scripts/performance_testing/README.md @@ -0,0 +1,213 @@ +# Performance Testing + +Performance testing may be accomplished by using the performance testing tool found in this directory, `Test-ReleasePerformance.ps1`. These results may be further processed to provide predicate level performance details by using the script `profile_predicates.py`, which is documented in the [Profiling Predicates section.](#profiling-predicates), below. + +Note that this script depends on other files from this repository. It may be run on external builds of Coding Standards through the `-CodingStandardsPath` flag, but it should be run from a fresh checkout of this repository. + +This script requires `pwsh` to be installed. Note that the Windows native Powershell is not sufficient and you should download PowerShell Core. + +- Installing on Windows: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.3 +- Installing on Linux: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux?view=powershell-7.3 +- Installing on MacOS: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.3 + +Before invoking this script you should start a powershell session by typing `pwsh` at a command prompt. + +## Usage + +``` +NAME + .\scripts\performance_testing\Test-ReleasePerformance.ps1 + +SYNOPSIS + Test release performance. Generates outputs 2 csv files containing the slowest predicates as well as the queries + causing work. Note that the method of computing query execution time is inaccurate due to the shared nature of + predicates. + + +SYNTAX + C:\Projects\codeql-coding-standards\scripts\performance_testing\Test-ReleasePerformance.ps1 -RunTests [-Threads ] -DatabaseArchive + [-TestTimestamp ] [-CodingStandardsPath ] [-ResultsDirectory ] [-ReleaseTag ] -Suite [-Platform ] -Language + [] + + C:\Projects\codeql-coding-standards\scripts\performance_testing\Test-ReleasePerformance.ps1 -ProcessResults -ResultsFile [-ResultsDirectory ] + [-ReleaseTag ] -Suite [-Platform ] -Language [] + + +DESCRIPTION + Test release performance. Generates outputs 2 csv files containing the slowest predicates as well as the queries + causing work. Note that the method of computing query execution time is inaccurate due to the shared nature of + predicates. + + +PARAMETERS + -RunTests [] + Configures tool to run tests. + + Required? true + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + -Threads + Specifies the number of threads to use. + + Required? false + Position? named + Default value 5 + Accept pipeline input? false + Accept wildcard characters? false + + -DatabaseArchive + Specifies the database to use for testing. Should be a zipped database + directory. + + Required? true + Position? named + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -TestTimestamp + The timestamp to use for the test. + + Required? false + Position? named + Default value (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") + Accept pipeline input? false + Accept wildcard characters? false + + -CodingStandardsPath + The path to the coding standards root directory. This can be either the + root of the repository or the root of the coding standards directory. + + Required? false + Position? named + Default value "$PSScriptRoot../../" + Accept pipeline input? false + Accept wildcard characters? false + + -ProcessResults [] + + Required? true + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + -ResultsFile + Configures tool to process results. + + Required? true + Position? named + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -ResultsDirectory + Where results should be written to. + + Required? false + Position? named + Default value (Get-Location) + Accept pipeline input? false + Accept wildcard characters? false + + -ReleaseTag + The release tag to use for the test. + + Required? false + Position? named + Default value current + Accept pipeline input? false + Accept wildcard characters? false + + -Suite + Which suite to run. + + Required? true + Position? named + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -Platform + The platform to run on. This is just a descriptive string. + + Required? false + Position? named + Default value $PSVersionTable.Platform + Accept pipeline input? false + Accept wildcard characters? false + + -Language + The language to run on. + + Required? true + Position? named + Default value + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). + +INPUTS + +OUTPUTS + + +RELATED LINKS + +``` +## Example Usage + +Run the `cert` suite for `c` from within the Coding Standards repository. + +``` +.\scripts\performance_testing\Test-ReleasePerformance.ps1 -RunTests -DatabaseArchive ..\codeql-coding-standards-release-engineering\data\commaai-openpilot-72d1744d830bc249d8761a1d843a98fb0ced49fe-cpp.zip -Suite cert -Language c +``` + +Run the `cert` suite for `c` on an external release, specifying a `-ReleaseTag` as well. The `-ReleaseTag` parameter is used for configuring performance tool to generate files within subdirectories with the `-ReleaseTag` as a prefix. For example, specifying `-ReleaseTag "2.16.0"` will cause files to be generated in the `release=2.16.0` directory. + +``` +.\scripts\performance_testing\Test-ReleasePerformance.ps1 -RunTests -DatabaseArchive ..\codeql-coding-standards-release-engineering\data\commaai-openpilot-72d1744d830bc249d8761a1d843a98fb0ced49fe-cpp.zip -Suite cert -Language c -ReleaseTag "2.16.0" -CodingStandardsPath "Downloads\code-scanning-cpp-query-pack-2.16.0\codeql-coding-standards\" +``` + + + +## Outputs + +The `Test-ReleasePerformance.ps1` produces three files in the `ResultsDirectory` location, which defaults `performance_tests` within the current working directory. + +- `suite=$Suite,datum=queries.csv` - Which contains the run time for each query. +- `suite=$Suite,datum=evaluator-log.json` - Which contains the evaluator log. +- `suite=$Suite,datum=sarif.sarif` - The sarif log file for the run. + +## Profiling Predicates + +If you wish to extract predicate-level profiling information, you may use the script `profile_predicates.py` located in this directory. It requires Python3 with `pandas` and `numpy` to work. If you wish to use a virtual environment you may create one as follows on a Unix-based platform: + +``` +python -mvenv venv +source venv/bin/activate +pip install pandas numpy +``` + +The script works by summarizing ALL of the csv and json files within a given directory. Thus, if you want to profile multiple suites or multiple releases you may place the files within that directory by repeatedly invoking `Test-ReleasePerformance.ps1.` Make sure to supply the same output directory each time so that the results accumulate in the correct location. + +To invoke the script run: + +``` +python scripts/performance_testing/profile_predicates.py +``` + +For example: +``` +python .\scripts\performance_testing\profile_predicates.py .\performance_tests\ +``` + +This will produce an additional CSV file per release, platform, and language within that directory called: `slow-log,datum=predicates,release={release},platform={platform},language={language}.csv` which will contain the execution times of all of the predicates used during execution. diff --git a/scripts/performance_testing/Test-ReleasePerformance.ps1 b/scripts/performance_testing/Test-ReleasePerformance.ps1 new file mode 100644 index 0000000000..c82c3f3e5c --- /dev/null +++ b/scripts/performance_testing/Test-ReleasePerformance.ps1 @@ -0,0 +1,213 @@ +<# +.SYNOPSIS + Test release performance. Generates outputs 2 csv files containing the slowest predicates as well as the queries + causing work. Note that the method of computing query execution time is inaccurate due to the shared nature of + predicates. + +.DESCRIPTION + Test release performance. Generates outputs 2 csv files containing the slowest predicates as well as the queries + causing work. Note that the method of computing query execution time is inaccurate due to the shared nature of + predicates. +#> +param( + # Configures tool to run tests. + [Parameter(Mandatory, ParameterSetName = 'RunTests')] + [switch] + $RunTests, + + # Specifies the number of threads to use. + [Parameter(Mandatory=$false, ParameterSetName = 'RunTests')] + [string] + $Threads=5, + + # Specifies the database to use for testing. Should be a zipped database + # directory. + [Parameter(Mandatory, ParameterSetName = 'RunTests')] + [string] + $DatabaseArchive, + + # The timestamp to use for the test. + [Parameter(Mandatory = $false, ParameterSetName = 'RunTests')] + [string] + $TestTimestamp=(Get-Date -Format "yyyy-MM-dd_HH-mm-ss"), + + # The path to the coding standards root directory. This can be either the + # root of the repository or the root of the coding standards directory. + [Parameter(Mandatory=$false, ParameterSetName = 'RunTests')] + [string] + $CodingStandardsPath="$PSScriptRoot/../../", + + [Parameter(Mandatory, ParameterSetName = 'ProcessResults')] + [switch] + $ProcessResults, + + # Configures tool to process results. + [Parameter(Mandatory, ParameterSetName = 'ProcessResults')] + [string] + $ResultsFile, + # Where results should be written to. + [Parameter(Mandatory=$false)] + [string] + $ResultsDirectory = (Join-Path (Get-Location) "performance_tests"), + + # The release tag to use for the test. + [Parameter(Mandatory=$false)] + [string] + $ReleaseTag = "current", + # Which suite to run. + [Parameter(Mandatory)] + [ValidateSet('cert', 'misra', 'autosar')] + [string] + $Suite, + # The platform to run on. This is just a descriptive string. + [Parameter(Mandatory=$false)] + [string] + $Platform=$PSVersionTable.Platform, + # The language to run on. + [Parameter(Mandatory)] + [ValidateSet('c', 'cpp')] + [string] + $Language +) + +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" + +. "$PSScriptRoot/Config.ps1" +. "$PSScriptRoot/Get-TestTmpDirectory.ps1" +. "$PSScriptRoot/Convert-DurationStringToMs.ps1" +. "$PSScriptRoot/Get-DurationString.ps1" +. "$PSScriptRoot/Get-QueryString.ps1" + +# Test Programs +Write-Host "Checking 'codeql' program...." -NoNewline +Test-ProgramInstalled -Program "codeql" +Write-Host -ForegroundColor ([ConsoleColor]2) "OK" + +$CODEQL_VERSION = (codeql version --format json | ConvertFrom-Json).version + +Write-Host "Checking 'codeql' version = $REQUIRED_CODEQL_VERSION...." -NoNewline +if (-Not ($CODEQL_VERSION -eq $REQUIRED_CODEQL_VERSION)) { + throw "Invalid CodeQL version $CODEQL_VERSION. Please install $REQUIRED_CODEQL_VERSION." +} +Write-Host -ForegroundColor ([ConsoleColor]2) "OK" + + + +# Create the results/work directory +$RESULTS_DIRECTORY = Get-TestTmpDirectory +New-Item -Path $RESULTS_DIRECTORY -ItemType Directory | Out-Null + +Write-Host "Writing Results to $RESULTS_DIRECTORY" + +if (-Not $ProcessResults){ + + $DB_UNPACKED_TMP = Join-Path $RESULTS_DIRECTORY db-unpacked + $DB_UNPACKED = Join-Path $RESULTS_DIRECTORY db + $DB_FILENAME = (Get-Item $DatabaseArchive).Name + Write-Host "Copying database to $RESULTS_DIRECTORY..." + # Copy and unpack the dataset + Copy-Item -Path $DatabaseArchive -Destination $RESULTS_DIRECTORY + + Expand-Archive -LiteralPath $RESULTS_DIRECTORY\$DB_FILENAME -DestinationPath $DB_UNPACKED_TMP + + foreach($f in Get-ChildItem $DB_UNPACKED_TMP){ + Move-Item -Path $f -Destination $DB_UNPACKED + } + + + $SARIF_OUT = Join-Path $RESULTS_DIRECTORY "suite=$Suite,datum=sarif.sarif" + $EvaluatorLog = Join-Path $RESULTS_DIRECTORY "evaluator-log.json" + $EvaluatorResults = Join-Path $RESULTS_DIRECTORY "evaluator-results.json" + + + $stdOut = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) + $stdErr = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) + + Write-Host "Standard Out Buffered to: $stdOut" + Write-Host "Standard Error Buffered to: $stdErr" + + $SuiteRoot = Join-Path $Language $Suite "src" "codeql-suites" + # For some reason nothing is written to stdout so we use stderr + $SuitePath = Join-Path $CodingStandardsPath $SuiteRoot ($Suite + "-default.qls") + $procDetails = Start-Process -FilePath "codeql" -PassThru -NoNewWindow -Wait -ArgumentList "database analyze --rerun --threads $Threads --debug --tuple-counting --evaluator-log=$EvaluatorLog --format sarif-latest --search-path $(Resolve-Path $CodingStandardsPath) --output $SARIF_OUT $DB_UNPACKED $SuitePath" -RedirectStandardOutput $stdOut -RedirectStandardError $stdErr + + if (-Not $procDetails.ExitCode -eq 0) { + Get-Content $stdErr | Out-String | Write-Host + Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" + throw "Performance suite failed to run. Will not report data." + } + else { + Write-Host -ForegroundColor ([ConsoleColor]2) "OK" + $runData = $stdErr + } + +}else{ + $runData = $ResultsFile +} +# Step 1: Compile data from queries +# +$PERFORMANCE_DATA = @() + +foreach($l in Get-Content $runData){ + + # skip lines that aren't ones we can process + if(-Not $l.Contains("Evaluation done;")){ + continue + } + + $durationString = Get-DurationString -LogLine $l + $queryString = Get-QueryString -LogLine $l + $timeInMs = Convert-DurationStringToMs -DurationString $durationString + + $row = @{ + "Query" = $queryString; + "TimeInMs" = $timeInMs; + } + + Write-Host "LOG: Duration=$durationString; TimeInMs=$timeInMs; Query=$queryString" + + $PERFORMANCE_DATA += $row +} +# Step 2: Compile predicate data +# +# + +# the data must first be transformed +$procDetails = Start-Process -FilePath "codeql" -PassThru -NoNewWindow -Wait -ArgumentList "generate log-summary $EvaluatorLog $EvaluatorResults" + +if (-Not $procDetails.ExitCode -eq 0) { + Write-Host -ForegroundColor ([ConsoleColor]4) "FAILED" + throw "Did not find performance results summary." +} +else { + Write-Host -ForegroundColor ([ConsoleColor]2) "OK" +} + + +# Step 3: Write out granular performance data +# +# We root this in $ResultsDirectory/release-$Release-/platform-/$Suite.csv + +# Create the Directory (and it's parents) +$outputDirectory = (Join-Path $ResultsDirectory "release=$ReleaseTag,testedOn=$TestTimestamp" "platform=$Platform" "language=$Language") +$outputDirectorySARIF = $outputDirectory + +$queryOutputFile = Join-Path $outputDirectory "suite=$Suite,datum=queries.csv" +$evaluatorResultsFile = Join-Path $outputDirectory "suite=$Suite,datum=evaluator-log.json" + +# Create the output directory. +# note there is no need to create the sarif out directory -- it will be created +# by the copy command, below. + +New-Item -Type Directory -Path $outputDirectory -ErrorAction Ignore | Out-Null + + +# Copy processed results out +Copy-Item -Path $EvaluatorResults -Destination $evaluatorResultsFile +Copy-Item -Path $SARIF_OUT -Destination $outputDirectorySARIF + +# Write out the report +Write-Host "Writing report to $queryOutputFile" +foreach ($r in $PERFORMANCE_DATA) { + [PSCustomObject]$r | Export-CSV -Path $queryOutputFile -Append -NoTypeInformation +} \ No newline at end of file diff --git a/scripts/performance_testing/profile_predicates.py b/scripts/performance_testing/profile_predicates.py new file mode 100644 index 0000000000..f584503bb5 --- /dev/null +++ b/scripts/performance_testing/profile_predicates.py @@ -0,0 +1,203 @@ +# %% +import numpy as np +import pandas as pd +import subprocess +import glob +from pathlib import Path +import json +import math +import sys +# %% + +if len(sys.argv) < 2: + print("Usage: python profile_predicates.py ") + sys.exit(0) + +root_path = Path(sys.argv[1]) +#%% +# root_path = Path("../../performance_tests/") + + +# We only process the LATEST run for a given release x suite x platform. To support this function +# we loop over all of the possible CSV files and add a file to the "load" list +# only if it has a newer `testedOn` value. +datafiles = {} + + +def path_to_tuple(path): + parts = path.parts + + part_suite = parts[-1] + part_language = parts[-2] + part_platform = parts[-3] + part_release = parts[-4] + + release = part_release.split(",")[0].split("=")[1] + testedOn = part_release.split(",")[1].split("=")[1] + platform = part_platform.split("=")[1] + language = part_language.split("=")[1] + suite = part_suite.split(".")[0].split("=")[1].split(",")[0] + + return release, testedOn, platform, language, suite +#%% + +for f in root_path.glob(f"release*/**/*datum=evaluator-log.json"): + release, testedOn, platform, language, suite = path_to_tuple(f) + + hashEntry = { + "release": release, + "testedOn": testedOn, + "platform": platform, + "language": language, + "suite": suite, + "dataFile": f + } + + if not release in datafiles.keys(): + datafiles[(release, platform, suite, language)] = hashEntry + else: + existing = datafiles[(release, platform, suite, language)] + + if existing["testedOn"] > testedOn: + datafiles[(release, platform, suite, language)] = hashEntry +# %% +summary_df = pd.DataFrame(columns=[ + 'Release', + 'Run', + 'Platform', + 'Language', + 'Suite', + 'Predicate', + 'Execution_Time_Ms' +]) + + +new_rows = { + 'Release': [], + 'Run': [], + 'Platform': [], + 'Language': [], + 'Suite': [], + 'Predicate': [], + 'Execution_Time_Ms': [] +} + +for K, V in datafiles.items(): + print(f"Loading {str(V['dataFile'])}...", end=None) + + # we need to load the data file and then parse each JSON row + with open(V['dataFile'], 'r') as f: + json_line_data = f.read() + #json_line_objects = re.split(r"(?m)^\n", json_line_data) + json_line_objects = json_line_data.split('\n\n') + + + print(f"Done.") + + for json_line_object in json_line_objects: + + #print(".", end="None") + + # quickly do this before bothering to parse the JSON + if not ("predicateName" in json_line_object and "COMPUTE_SIMPLE" in json_line_object): + continue + + json_object = json.loads(json_line_object) + + if not "predicateName" in json_object: + continue + + if json_object["predicateName"] == "output": + continue + + + if not json_object["evaluationStrategy"] == "COMPUTE_SIMPLE": + continue + + new_rows['Release'].append(V["release"]) + new_rows['Run'].append(V["testedOn"]) + new_rows['Platform'].append(V["platform"]) + new_rows['Language'].append(V["language"]) + new_rows['Suite'].append(V["suite"]) + new_rows['Predicate'].append(json_object["predicateName"]) + new_rows['Execution_Time_Ms'].append(json_object["millis"]) + +new_df = pd.DataFrame(new_rows) +summary_df = pd.concat([summary_df, new_df]) + +# %% +# %% +performance_df = pd.DataFrame( + columns=[ + 'Release', + 'Platform', + 'Language', + 'Total_Serialized_Execution_Time_Ms', + 'Mean_Predicate_Execution_Time_Ms', + 'Median_Predicate_Execution_Time_Ms', + 'Standard_Deviation_Ms', + 'Total_Serialized_Execution_Time_s', + 'Mean_Query_Execution_Time_s', + 'Median_Predicate_Execution_Time_s', + 'Percentile95_Ms', + 'Number_of_Predicates' + ] +) + +summary_df_grouped = summary_df.groupby(['Release', 'Platform', 'Language']) + +for _, df_group in summary_df_grouped: + + release = df_group["Release"].iloc[0] + platform = df_group["Platform"].iloc[0] + language = df_group["Language"].iloc[0] + + print(f"Processing Platform={platform}, Language={language}, Release={release}") + + + execution_time = df_group["Execution_Time_Ms"].sum() + execution_time_mean = df_group["Execution_Time_Ms"].mean() + execution_time_median = df_group["Execution_Time_Ms"].median() + execution_time_std = df_group["Execution_Time_Ms"].std() + percentile_95 = df_group["Execution_Time_Ms"].quantile(.95) + num_queries = len(df_group) + + row_df = pd.DataFrame({ + 'Release' : [release], + 'Platform' : [platform], + 'Language' : [language], + 'Total_Serialized_Execution_Time_Ms' : [execution_time], + 'Mean_Predicate_Execution_Time_Ms' : [execution_time_mean], + 'Median_Predicate_Execution_Time_Ms' : [execution_time_median], + 'Standard_Deviation_Ms' : [execution_time_std], + 'Total_Serialized_Execution_Time_s' : [execution_time/1000], + 'Mean_Query_Execution_Time_s' : [execution_time_mean/1000], + 'Median_Predicate_Execution_Time_s' : [execution_time_median/1000], + 'Percentile95_Ms' : [percentile_95], + 'Number_of_Predicates' : [num_queries] + }) + + performance_df = pd.concat([performance_df, row_df]) + +#%% +# write out the high level performance summary +performance_df.to_csv(root_path.joinpath('performance-history,datum=predicate.csv'), index=False) +#%% +# write out all queries for every suite that are greater than the 95th +# percentile +for _, row in performance_df.iterrows(): + + + release = row["Release"] + platform = row["Platform"] + language = row["Language"] + percentile_95 = row["Percentile95_Ms"] + + rpl_df = summary_df[(summary_df["Release"] == release) & (summary_df["Platform"] == platform) & (summary_df["Language"] == language)] + g95 = rpl_df[(rpl_df["Execution_Time_Ms"] >= percentile_95)] + + g95 = g95.sort_values(by='Execution_Time_Ms', ascending=False) + + g95.to_csv(root_path.joinpath(f"slow-log,datum=predicates,release={release},platform={platform},language={language}.csv"), index=False) + + diff --git a/scripts/release/bump-version.sh b/scripts/release/bump-version.sh new file mode 100755 index 0000000000..5bbd0eeae0 --- /dev/null +++ b/scripts/release/bump-version.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + + +if [[ -z $1 ]]; +then + echo "Usage: bump-version.sh " + exit +fi + +echo "Setting Local Branch Version to $1." + +# update the qlpacks +find . -name 'qlpack.yml' | grep -v './codeql_modules' | grep -v './scripts' | xargs sed -i "s/^version.*$/version: ${1}/" + +# update the documentation. + +find docs -name 'user_manual.md' -print0 | xargs -0 sed -i "s/code-scanning-cpp-query-pack-.*\.zip\`/code-scanning-cpp-query-pack-${1}.zip\`/" +find docs -name 'user_manual.md' -print0 | xargs -0 sed -i "s/coding-standard-codeql-pack-.*\.zip\`/coding-standard-codeql-pack-${1}.zip\`/" +find docs -name 'user_manual.md' -print0 | xargs -0 sed -i "s/supported_rules_list_.*\.csv\`/supported_rules_list_${1}.csv\`/" +find docs -name 'user_manual.md' -print0 | xargs -0 sed -i "s/supported_rules_list_.*\.md\`/supported_rules_list_${1}.md\`/" +find docs -name 'user_manual.md' -print0 | xargs -0 sed -i "s/user_manual_.*\.md\`/user_manual_${1}.md\`/" +find docs -name 'user_manual.md' -print0 | xargs -0 sed -i "s/This user manual documents release \`.*\` of/This user manual documents release \`${1}\` of/" + +echo "Done." \ No newline at end of file diff --git a/scripts/release/create_draft_release.sh b/scripts/release/create_draft_release.sh deleted file mode 100755 index fa3000d450..0000000000 --- a/scripts/release/create_draft_release.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# Script for generating a draft release for the CodeQL Coding Standards repository, for the given branch. - -set -o errexit -set -o nounset - -BRANCH="$1" -VERSION="$2" - -if [[ ! $BRANCH == rc/* ]]; then - echo "$BRANCH is not an rc branch of the form rc/" - exit 1 -fi - -if [ -z "$VERSION" ]; then - VERSION="${BRANCH#rc/}" - echo "Version not set explicitly; auto-detecting $VERSION." -fi - -COMMIT_SHA="$(git rev-parse $BRANCH)" - -echo "Creating draft release for $VERSION from $BRANCH at commit $COMMIT_SHA." - -echo "Identifying code-scanning-pack-gen.yml" -CODE_SCANNING_PACK_GEN_RUN_ID=$(gh api -X GET repos/github/codeql-coding-standards/actions/workflows/code-scanning-pack-gen.yml/runs -F branch="$BRANCH" -F event="push" -F conclusion="success" --jq "first(.workflow_runs.[] | select(.head_sha==\"$COMMIT_SHA\") | .id)") -if [ -z "$CODE_SCANNING_PACK_GEN_RUN_ID" ]; then - echo "No successful run of the code-scanning-pack-gen.yml file for $COMMIT_SHA on branch $BRANCH." - exit 1 -fi - -# Create a temp directory to store the artifacts in -TEMP_DIR="$(mktemp -d)" - -echo "Identified code-scanning-pack-gen.yml run id: $CODE_SCANNING_PACK_GEN_RUN_ID" - -echo "Fetching Code Scanning pack" -CODE_SCANNING_ARTIFACT_NAME="code-scanning-cpp-query-pack.zip" -CODE_SCANNING_VERSIONED_ARTIFACT_NAME="code-scanning-cpp-query-pack-$VERSION.zip" -gh run download $CODE_SCANNING_PACK_GEN_RUN_ID -n "$CODE_SCANNING_ARTIFACT_NAME" -mv "$CODE_SCANNING_ARTIFACT_NAME" "$TEMP_DIR/$CODE_SCANNING_VERSIONED_ARTIFACT_NAME" - -echo "Generating release notes." -python3 scripts/release/generate_release_notes.py > "$TEMP_DIR/release_notes_$VERSION.md" -python3 scripts/release/create_supported_rules_list.py > "$TEMP_DIR/supported_rules_list_$VERSION.md" -python3 scripts/release/create_supported_rules_list.py --csv > "$TEMP_DIR/supported_rules_list_$VERSION.csv" - -echo "Copy Docs to Artifact Directory" -cp docs/user_manual.md "$TEMP_DIR/user_manual_$VERSION.md" - -echo "Generating Checksums" -sha256sum $TEMP_DIR/* > "$TEMP_DIR/checksums.txt" - -gh release create "v$VERSION" -d --target "$BRANCH" -F "$TEMP_DIR/release_notes_$VERSION.md" -t "v$VERSION" "$TEMP_DIR/$CODE_SCANNING_VERSIONED_ARTIFACT_NAME" "$TEMP_DIR/supported_rules_list_$VERSION.md" "$TEMP_DIR/checksums.txt" "$TEMP_DIR/supported_rules_list_$VERSION.csv" "$TEMP_DIR/user_manual_$VERSION.md" - -curl \ - -X POST \ - -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $INTEGRATION_TESTING_ACCESS_TOKEN" \ - https://api.github.com/repos/coding-standards-integration-testing/integration-testing-production/actions/workflows/$WORKFLOW_ID/dispatches \ - -d '{"ref":"refs/heads/main", "inputs": { "release_version_tag":"'"$VERSION"'", "codeql_analysis_threads":"'"$CODEQL_ANALYSIS_THREADS"'", "aws_ec2_instance_type":"'"$AWS_EC2_INSTANCE_TYPE"'" }}' diff --git a/scripts/release/create_supported_rules_list.py b/scripts/release/create_supported_rules_list.py index 15a8b5d6b7..6d8f3e0991 100644 --- a/scripts/release/create_supported_rules_list.py +++ b/scripts/release/create_supported_rules_list.py @@ -12,9 +12,6 @@ When run without any arguments, the script iterates through each of the rule package description files stored in the `rule_packages` directory, and identifies which rules are supported by one or more queries. - -This script needs to be run with the codeql-coding-standards git repository as the current working -directory. """ if (len(sys.argv) == 2 and sys.argv[1] == "--help"): @@ -30,7 +27,7 @@ repo_root = Path(__file__).parent.parent.parent -rules_covered = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}} +rules_covered = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}, "MISRA-C++-2023" : {},} # Iterate over rule packages for language_name in ["cpp", "c"]: diff --git a/scripts/release/generate_release_notes.py b/scripts/release/generate_release_notes.py index c6cea8d19f..3852a5eeb7 100644 --- a/scripts/release/generate_release_notes.py +++ b/scripts/release/generate_release_notes.py @@ -79,7 +79,7 @@ def transform_legacy_rule_path(p): diff_from_last_release = latest_release_commit.diff(head_commit) # Store a mapping from standard -> rules with new queries -> new queries for those rules -new_rules = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}} +new_rules = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}, "MISRA-C++-2023" : {}} # Store the text of the newly added change notes change_notes = [] # Store the names of the rule packages with new queries diff --git a/scripts/release/is-hotfix-release.py b/scripts/release/is-hotfix-release.py new file mode 100644 index 0000000000..fdf46ceed2 --- /dev/null +++ b/scripts/release/is-hotfix-release.py @@ -0,0 +1,27 @@ +from semantic_version import Version # type: ignore +from subprocess import run +from typing import List, Literal, TYPE_CHECKING +from sys import stderr + +if TYPE_CHECKING: + from argparse import Namespace + +def main(args: 'Namespace') -> Literal[0,1]: + try: + version = Version(args.version) + if version.patch > 0: + print("true") + else: + print("false") + return 0 + except ValueError: + print(f"Invalid version string: {args.version}", file=stderr) + return 1 + +if __name__ == '__main__': + from sys import stderr, exit + import argparse + + parser = argparse.ArgumentParser(description="Check if a version is a hotfix release") + parser.add_argument("version", help="The version string to compare against the base branches") + exit(main(parser.parse_args())) \ No newline at end of file diff --git a/scripts/release/next-version.py b/scripts/release/next-version.py new file mode 100644 index 0000000000..548371f0d9 --- /dev/null +++ b/scripts/release/next-version.py @@ -0,0 +1,28 @@ +from semantic_version import Version +import argparse + +parser = argparse.ArgumentParser(description='Prints the next release version') +parser.add_argument('-c', '--component', default="minor", help='The component to increment (major, minor, patch)') +parser.add_argument('-p', '--pre-release', nargs='*', help='The pre-release label(s) (e.g. alpha, dev). Multiple labels can be specified so separate the options and the version using `--`!') +parser.add_argument('-b', '--build', nargs='*', help='The build identifier(s). Multiple identifiers can be specified so separate the options and the version using `--`!') +parser.add_argument('current_version', type=Version, help='The current version') + +if __name__ == "__main__": + args = parser.parse_args() + version : Version = args.current_version + next_version = None + if args.component== "major": + next_version = version.next_major() + elif args.component == "minor": + next_version = version.next_minor() + elif args.component == "patch": + next_version = version.next_patch() + else: + raise ValueError(f"Invalid release type: {args.release_type}") + + if args.pre_release: + next_version.prerelease = args.pre_release + if args.build: + next_version.build = args.build + + print(next_version) \ No newline at end of file diff --git a/scripts/release/release-layout.yml b/scripts/release/release-layout.yml new file mode 100644 index 0000000000..4ced0b4d30 --- /dev/null +++ b/scripts/release/release-layout.yml @@ -0,0 +1,29 @@ +version: 0.1.0 + +layout: + certification_kit.zip: + - workflow-log: + name: ".*" + - workflow-artifact: + not-name: "Code Scanning Query Pack Generation" + code-scanning-cpp-query-pack.zip: + - workflow-artifact: + name: "Code Scanning Query Pack Generation" + artifact: code-scanning-cpp-query-pack.zip + coding-standards-codeql-packs.zip: + - workflow-artifact: + name: "Code Scanning Query Pack Generation" + artifact: coding-standards-codeql-packs + supported_rules_list.csv: + - shell: | + python ${{ coding-standards.root }}/scripts/release/create_supported_rules_list.py --csv > supported_rules_list.csv + supported_rules_list.md: + - shell: | + python ${{ coding-standards.root }}/scripts/release/create_supported_rules_list.py > supported_rules_list.md + user_manual.md: + - file: docs/user_manual.md + checksums.txt: + - shell: | + sha256sum ${{ layout.root }}/* > checksums.txt + # Remove the layout root from the paths in checksums.txt + sed -i -e "s|${{ layout.root }}/||g" checksums.txt \ No newline at end of file diff --git a/scripts/release/requirements.txt b/scripts/release/requirements.txt new file mode 100644 index 0000000000..653323eaaa --- /dev/null +++ b/scripts/release/requirements.txt @@ -0,0 +1,5 @@ +semantic-version==2.10.0 +PyGithub==1.59.1 +PyYAML==6.0.1 +GitPython==3.1.41 +pytest==7.4.3 diff --git a/scripts/release/test-data/release-layout.yml b/scripts/release/test-data/release-layout.yml new file mode 100644 index 0000000000..9c6850fd5d --- /dev/null +++ b/scripts/release/test-data/release-layout.yml @@ -0,0 +1,19 @@ +version: 0.1.0 + +layout: + hello-world.txt: + - shell: | + echo "hello world!" > hello-world.txt + hello-world.zip: + - shell: | + echo "hello!" > hello.txt + echo "world!" > world.txt + # reset the creation and modification times to a fixed value + touch -a -m -t 197001010000.00 hello.txt world.txt + checksums.txt: + - shell: | + shasum -a 256 ${{ layout.root }}/* > checksums.txt + # Remove the layout root from the checksums.txt + # We don't use inplace because of BSD vs GNU shenanigans + sed -e "s|${{ layout.root }}/||g" checksums.txt > checksums-rewritten.txt + mv checksums-rewritten.txt checksums.txt \ No newline at end of file diff --git a/scripts/release/update-release-notes.py b/scripts/release/update-release-notes.py new file mode 100644 index 0000000000..964fcb339e --- /dev/null +++ b/scripts/release/update-release-notes.py @@ -0,0 +1,83 @@ +from __future__ import annotations # This enables postponed evaluation of type annotations. Required for typing.TYPE_CHECKING. See https://peps.python.org/pep-0563/ +from typing import TYPE_CHECKING +import subprocess +from pathlib import Path + +if TYPE_CHECKING: + from argparse import Namespace + +def generate_release_note(previous_release_tag: str) -> str: + script_path = Path(__file__).parent / "generate_release_notes.py" + cp = subprocess.run(["python", str(script_path), previous_release_tag], capture_output=True) + + if cp.returncode != 0: + raise Exception(f"Error generating release notes: {cp.stderr.decode('utf-8')}") + + return cp.stdout.decode("utf-8") + +def main(args: Namespace) -> int: + from github import Github, Auth + import semantic_version # type: ignore + import re + import sys + from functools import cmp_to_key + + repo = Github(auth=Auth.Token(args.github_token)).get_repo(args.repo) + + pull_candidates = [pr for pr in repo.get_pulls(state="open") if pr.head.sha == args.head_sha] + if len(pull_candidates) != 1: + print(f"Error: expected exactly one PR for SHA {args.head_sha}, but found {len(pull_candidates)}", file=sys.stderr) + return 1 + + pull_request = pull_candidates[0] + + if pull_request.state != "open": + print(f"Error: PR for version {args.version} is not open", file=sys.stderr) + return 1 + + rc_branch_regex = r"^rc/(?P.*)$" + rc_branch_match = re.match(rc_branch_regex, pull_request.base.ref) + if not rc_branch_match: + print(f"Error: PR {pull_request.url} is not based on a release candidate branch", file=sys.stderr) + return 1 + + release_version = rc_branch_match.group("version") + + try: + semantic_version.Version.parse(release_version) # type: ignore + except ValueError as e: + print(f"Error: invalid version {release_version} use by release branch. Reason {e}", file=sys.stderr) + return 1 + + releases = repo.get_releases() + candidate_releases= [release for release in releases if release.tag_name == f"v{release_version}"] + if len(candidate_releases) != 1: + print(f"Error: expected exactly one release with tag v{release_version}, but found {len(candidate_releases)}", file=sys.stderr) + return 1 + release = candidate_releases[0] + + # All the releases that are not draft and have a valid semantic version tag, our current release is assumed to be in draft (i.e. not yet released) + previous_releases = [release for release in releases if semantic_version.validate(release.tag_name[1:]) and not release.draft] # type: ignore + if len(previous_releases) == 0: + print(f"Error: no previous releases found", file=sys.stderr) + return 1 + # Sort them based on their semantic version tags. + previous_releases.sort(key=cmp_to_key(lambda a,b: semantic_version.compare(a.tag_name[1:], b.tag_name[1:])), reverse=True) # type: ignore + previous_release = previous_releases[0].tag_name + print(f"Using previous release: {previous_release}") + + release_notes = generate_release_note(previous_release) + + release.update_release(name=release.title, message=release_notes, draft=release.draft, prerelease=release.prerelease, tag_name=release.tag_name) + return 0 + +if __name__ == '__main__': + import argparse + from sys import exit + + parser = argparse.ArgumentParser() + parser.add_argument('--head-sha', help="The head SHA of the release PR for which we update it's corresponding release", required=True) + parser.add_argument('--repo', help="The owner and repository name. For example, 'octocat/Hello-World'. Used when testing this script on a fork", required=True, default="github/codeql-coding-standards") + parser.add_argument('--github-token', help="The GitHub token to use to update the release", required=True) + args = parser.parse_args() + exit(main(args)) \ No newline at end of file diff --git a/scripts/release/update_release_assets.py b/scripts/release/update_release_assets.py new file mode 100644 index 0000000000..1beb543c77 --- /dev/null +++ b/scripts/release/update_release_assets.py @@ -0,0 +1,379 @@ +from __future__ import annotations # This enables postponed evaluation of type annotations. Required for typing.TYPE_CHECKING. See https://peps.python.org/pep-0563/ +from typing import TYPE_CHECKING, List, Union, cast, Dict, Any, TypeVar, Callable, Sequence, Optional +import shutil +from tempfile import TemporaryDirectory +import subprocess +import re +from pathlib import Path +import sys +import semantic_version # type: ignore +import requests +import yaml + +if TYPE_CHECKING: + from github import WorkflowRun, Repository + + +script_path = Path(__file__).resolve() +root_path = script_path.parent.parent.parent + +def monkey_patch_github() -> None: + from github import Repository, PaginatedList, CheckRun + + class MyRepository(Repository.Repository): + def get_check_runs(self: Repository.Repository, ref: str, **kwargs: str) -> PaginatedList.PaginatedList[CheckRun.CheckRun]: + assert isinstance(ref, str), ref + + return PaginatedList.PaginatedList( + CheckRun.CheckRun, + self._requester, + f"{self.url}/commits/{ref}/check-runs", + firstParams=None, + list_item="check_runs") + + Repository.Repository = MyRepository + + from github import WorkflowRun, Artifact + class MyWorkflowRun(WorkflowRun.WorkflowRun): + def download_logs(self, path: Path) -> None: + """ + Download the logs for this workflow and store them in the directory specified by path. + + This method tries to minimize the dependency on the internal workings of the class Requester by using + requests directly. Ideally we would like to monkey patch __rawRequest to deal with 302 redirects, but + that didn't work out because it would fail to call other private methods with an AttributeError for an unknown reason. + """ + url = f"{self.url}/logs" + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28" + } + if self._requester._Requester__auth is not None: # type: ignore + headers["Authorization"] = f"{self._requester._Requester__auth.token_type} {self._requester._Requester__auth.token}" # type: ignore + headers["User-Agent"] = self._requester._Requester__userAgent # type: ignore + + resp = requests.get(url, headers=headers, allow_redirects=True) + + if resp.status_code != 200: + raise Exception(f"Unable to download logs: {resp.status_code} {resp.reason}") + + with (path / f"{self.name}-{self.head_sha}-{self.run_number}.zip").open("wb") as f: + f.write(resp.content) + + def download_artifacts(self, path: Path) -> None: + for artifact in self.get_artifacts(): # type: ignore + artifact = cast(Artifact.Artifact, artifact) + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28" + } + if self._requester._Requester__auth is not None: # type: ignore + headers["Authorization"] = f"{self._requester._Requester__auth.token_type} {self._requester._Requester__auth.token}" # type: ignore + headers["User-Agent"] = self._requester._Requester__userAgent # type: ignore + + resp = requests.get(artifact.archive_download_url, headers=headers, allow_redirects=True) + + if resp.status_code != 200: + raise Exception(f"Unable to download artifact ${artifact.name}. Received status code {resp.status_code} {resp.reason}") + + with (path / f"{artifact.name}.zip").open("wb") as f: + f.write(resp.content) + + def download_artifact(self, name: str, path: Path) -> None: + candidates: List[Artifact.Artifact] = [artifact for artifact in self.get_artifacts() if artifact.name == name] # type: ignore + if len(candidates) == 0: + raise Exception(f"Unable to find artifact {name}") + assert(len(candidates) == 1) + + artifact = candidates[0] + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28" + } + if self._requester._Requester__auth is not None: # type: ignore + headers["Authorization"] = f"{self._requester._Requester__auth.token_type} {self._requester._Requester__auth.token}" # type: ignore + headers["User-Agent"] = self._requester._Requester__userAgent # type: ignore + + resp = requests.get(artifact.archive_download_url, headers=headers, allow_redirects=True) + + if resp.status_code != 200: + raise Exception(f"Unable to download artifact ${artifact.name}. Received status code {resp.status_code} {resp.reason}") + + with (path / f"{artifact.name}.zip").open("wb") as f: + f.write(resp.content) + + + WorkflowRun.WorkflowRun = MyWorkflowRun + +class ReleaseLayout: + def __init__(self, specification: Path, skip_checks: bool = False) -> None: + self.specification = specification + self.artifacts = [] + self.skip_checks = skip_checks + + def make(self, directory: Path, workflow_runs: List[WorkflowRun.WorkflowRun]) -> None: + spec = yaml.safe_load(self.specification.read_text()) + artifacts : List[ReleaseArtifact] = [] + for artifact, action_specs in spec["layout"].items(): + actions : List[Union[WorkflowArtifactAction, WorkflowLogAction, ShellAction, FileAction]] = [] + for action_spec in action_specs: + assert(len(action_spec) == 1) + action_type, action_args = action_spec.popitem() + if action_type == "workflow-log": + actions.append(WorkflowLogAction(workflow_runs, **cast(Dict[str, Any], action_args))) + elif action_type == "workflow-artifact": + actions.append(WorkflowArtifactAction(workflow_runs, **cast(Dict[str, Any], action_args))) + elif action_type == "shell": + modifiers : List[Callable[[str], str]] = [ + lambda cmd: re.sub(pattern=r"\${{\s*coding-standards\.root\s*}}", repl=str(root_path), string=cmd), + lambda cmd: re.sub(pattern=r"\${{\s*layout\.root\s*}}", repl=str(directory), string=cmd) + ] + actions.append(ShellAction(action_args, modifiers=modifiers)) + elif action_type == "file": + actions.append(FileAction(action_args)) + else: + raise Exception(f"Unknown action type {action_type}") + + artifacts.append(ReleaseArtifact(artifact, actions, self.skip_checks)) + + for artifact in artifacts: + artifact.make(directory) + +class WorkflowLogAction(): + + def __init__(self, workflow_runs: List[WorkflowRun.WorkflowRun], **kwargs: str) -> None: + self.workflow_runs = workflow_runs + self.kwargs: dict[str, str] = kwargs + self.temp_workdir = TemporaryDirectory() + + def run(self) -> List[Path]: + workflow_runs = self.workflow_runs + if "name" in self.kwargs: + workflow_runs = [workflow_run for workflow_run in self.workflow_runs if re.match(self.kwargs["name"], workflow_run.name)] + if "not-name" in self.kwargs: + workflow_runs = [workflow_run for workflow_run in self.workflow_runs if not re.match(self.kwargs["not-name"], workflow_run.name)] + print(f"Downloading the logs for {len(workflow_runs)} workflow runs") + for workflow_run in workflow_runs: + print(f"Downloading logs for {workflow_run.name}") + workflow_run.download_logs(Path(self.temp_workdir.name)) # type: ignore + return list(map(Path, Path(self.temp_workdir.name).glob("**/*"))) + +class WorkflowArtifactAction(): + + def __init__(self, workflow_runs: List[WorkflowRun.WorkflowRun], **kwargs: str) -> None: + self.workflow_runs = workflow_runs + self.kwargs: dict[str, str] = kwargs + self.temp_workdir = TemporaryDirectory() + + def run(self) -> List[Path]: + workflow_runs = self.workflow_runs + if "name" in self.kwargs: + workflow_runs = [workflow_run for workflow_run in self.workflow_runs if re.match(self.kwargs["name"], workflow_run.name)] + if "not-name" in self.kwargs: + workflow_runs = [workflow_run for workflow_run in self.workflow_runs if not re.match(self.kwargs["not-name"], workflow_run.name)] + print(f"Downloading the artifacts for {len(workflow_runs)} workflow runs") + for workflow_run in workflow_runs: + if "artifact" in self.kwargs: + print(f"Downloading artifact {self.kwargs['artifact']} for {workflow_run.name} to {self.temp_workdir.name}") + workflow_run.download_artifact(self.kwargs["artifact"], Path(self.temp_workdir.name)) # type: ignore + else: + print(f"Downloading artifacts for {workflow_run.name} to {self.temp_workdir.name}") + workflow_run.download_artifacts(Path(self.temp_workdir.name)) # type: ignore + return list(map(Path, Path(self.temp_workdir.name).glob("**/*"))) + +class ShellAction(): + def __init__(self, command: str, **kwargs: Any) -> None: + self.command = command.strip() + self.temp_workdir = TemporaryDirectory() + self.options = kwargs + + def _rewrite_command(self) -> str: + E = TypeVar("E") + R = TypeVar("R") + def lfold(fn: Callable[[R, E], R], lst: Sequence[E], init: R) -> R: + return lfold(fn, lst[1:], fn(init, lst[0])) if lst else init + if 'modifiers' in self.options: + return lfold(lambda acc, x: x(acc), self.options['modifiers'], self.command) + else: + return self.command + + def run(self) -> List[Path]: + #concrete_command = re.sub(pattern=r"\${{\s*coding-standards\.root\s*}}", repl=str(root_path), string=self.command) + concrete_command = self._rewrite_command() + subprocess.run(concrete_command, cwd=self.temp_workdir.name, check=True, shell=True) + return list(map(Path, Path(self.temp_workdir.name).glob("**/*"))) + +class FileAction(): + def __init__(self, path: Path) -> None: + self.path = path + + def run(self) -> List[Path]: + return [self.path] + +class ReleaseArtifact(): + def __init__(self, name: str, actions: List[Union[WorkflowLogAction, WorkflowArtifactAction, ShellAction, FileAction]], allow_no_files: bool = False) -> None: + self.name = Path(name) + self.actions = actions + self.allow_no_files = allow_no_files + + def make(self, directory: Path) -> Optional[Path]: + files: list[Path] = [file for action in self.actions for file in action.run()] + if len(files) == 0: + if not self.allow_no_files: + raise Exception(f"Artifact {self.name} has no associated files!") + elif len(files) == 1: + shutil.copy(files[0], directory / self.name) + return directory / self.name + else: + extension = "".join(self.name.suffixes)[1:] + if not extension in ["zip", "tar", "tar.gz", "tar.bz2", "tar.xz"]: + raise Exception(f"Artifact {self.name} is not a support archive file, but has multiple files associated with it!") + + ext_format_map = { + "zip": "zip", + "tar": "tar", + "tar.gz": "gztar", + "tar.bz2": "bztar", + "tar.xz": "xztar" + } + + with TemporaryDirectory() as temp_dir: + temp_dir_path = Path(temp_dir) + for file in files: + shutil.copy(file, temp_dir_path / file.name) + + return Path(shutil.make_archive(str(directory / self.name.with_suffix("")), ext_format_map[extension], root_dir=temp_dir_path)) + +def main(args: 'argparse.Namespace') -> int: + monkey_patch_github() + + import github + from github import CheckRun + + repos: Dict[str, Repository.Repository] = {} + if len(args.github_token) == 1: + repos[args.repo] = github.Github(auth=github.Auth.Token(args.github_token[0])).get_repo(args.repo) + else: + for token in args.github_token: + nwo, token = token.split(":") + repos[nwo] = github.Github(auth=github.Auth.Token(token)).get_repo(nwo) + + repo = repos[args.repo] + + pull_candidates = [pr for pr in repo.get_pulls(state="open") if pr.head.sha == args.head_sha] + if len(pull_candidates) != 1: + print(f"Error: expected exactly one PR for SHA {args.head_sha}, but found {len(pull_candidates)}", file=sys.stderr) + return 1 + + pull_request = pull_candidates[0] + + if pull_request.state != "open": + print(f"Error: PR {pull_request.url} is not open", file=sys.stderr) + return 1 + + print(f"Found PR {pull_request.url} based on {pull_request.base.ref}") + + rc_branch_regex = r"^rc/(?P.*)$" + rc_branch_match = re.match(rc_branch_regex, pull_request.base.ref) + if not rc_branch_match: + print(f"Error: PR {pull_request.url} is not based on a release candidate branch", file=sys.stderr) + return 1 + + release_version = rc_branch_match.group("version") + + try: + semantic_version.Version.parse(release_version) # type: ignore + except ValueError as e: + print(f"Error: invalid version {release_version} use by release branch. Reason {e}", file=sys.stderr) + return 1 + + print(f"Looking for release with tag v{release_version} associated with the PR's base ref {pull_request.base.ref}") + all_releases = repo.get_releases() + for release in all_releases: + print(f"Found release {release.title} with tag {release.tag_name}") + releases = [release for release in all_releases if release.tag_name == f"v{release_version}"] + if len(releases) != 1: + print(f"Error: expected exactly one release for {release_version}, but found {len(releases)}", file=sys.stderr) + return 1 + release = releases[0] + + print(f"Collecting workflow runs for ref {args.head_sha}") + check_runs: List[CheckRun.CheckRun] = repo.get_check_runs(args.head_sha) # type: ignore + + action_workflow_run_url_regex = r"^https://(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/actions/runs/(?P\d+)$" + action_workflow_job_run_url_regex = r"^https://(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/actions/runs/(?P\d+)/job/(?P\d+)$" + + workflow_runs: List[WorkflowRun.WorkflowRun] = [] + for check_run in check_runs: # type: ignore + check_run = cast(CheckRun.CheckRun, check_run) + if check_run.name in args.skip_checkrun: + print(f"Skipping check run {check_run.name} with id {check_run.id} because it is on the skip list.") + continue + job_run_match = re.match(action_workflow_job_run_url_regex, check_run.details_url) + if job_run_match: + workflow_run = repo.get_workflow_run(int(job_run_match.group("run_id"))) + workflow_runs.append(workflow_run) + else: + run_match = re.match(action_workflow_run_url_regex, check_run.external_id) + if run_match: + #print(f"External workflow on {run_match.group('owner')} {run_match.group('repo')} with id {run_match.group('run_id')}") + workflow_run = repos[f"{run_match.group('owner')}/{run_match.group('repo')}"].get_workflow_run(int(run_match.group("run_id"))) + workflow_runs.append(workflow_run) + else: + print(f"Unable to handle checkrun {check_run.name} with id {check_run.id} with {check_run.details_url}") + return 1 + + print("Filtering workflow runs to only include the latest run for each workflow.") + workflow_runs_per_id: Dict[int, WorkflowRun.WorkflowRun] = {} + for workflow_run in workflow_runs: + if not workflow_run.id in workflow_runs_per_id: + workflow_runs_per_id[workflow_run.id] = workflow_run + else: + latest_run = workflow_runs_per_id[workflow_run.id] + if latest_run.run_number < workflow_run.run_number: + workflow_runs_per_id[workflow_run.id] = workflow_run + latests_workflow_runs = list(workflow_runs_per_id.values()) + + if not args.skip_checks: + print(f"Checking that all workflow runs for ref {args.head_sha} succeeded") + for workflow_run in latests_workflow_runs: + if workflow_run.status != "completed": + print(f"Error: workflow run {workflow_run.name} with id {workflow_run.id} is not completed", file=sys.stderr) + return 1 + # Consider success or skipped as success + if workflow_run.conclusion == "failure": + print(f"Error: workflow run {workflow_run.name} with id {workflow_run.id} failed", file=sys.stderr) + return 1 + + with TemporaryDirectory() as temp_dir: + print(f"Using temporary directory {temp_dir}") + try: + ReleaseLayout(Path(args.layout), args.skip_checks).make(Path(temp_dir), latests_workflow_runs) + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + return 1 + + print("Deleting existing assets") + for asset in release.assets: + asset.delete_asset() + + print("Uploading new assets from generated release layout") + for file in Path(temp_dir).glob("**/*"): + print(f"Uploading {file}") + release.upload_asset(str(file)) + + return 0 + +if __name__ == '__main__': + import argparse + from sys import exit + + parser = argparse.ArgumentParser() + parser.add_argument('--head-sha', help="The head SHA of the release PR for which we update it's corresponding release", required=True) + parser.add_argument('--repo', help="The owner and repository name. For example, 'octocat/Hello-World'. Used when testing this script on a fork", required=True, default="github/codeql-coding-standards") + parser.add_argument('--github-token', help="The github token to access repo and the repositories provided as external ids in check runs. When multiple tokens are provided use the format 'owner/repo:token'", required=True, nargs="+") + parser.add_argument('--layout', help="The layout to use for the release", required=True) + parser.add_argument('--skip-checkrun', help="Name of check run to exclude from consideration. Can be specified multiple times", nargs='+', default=["release-status"]) + parser.add_argument('--skip-checks', help="Skip the checks that ensure that the workflow runs succeeded", action="store_true") + args = parser.parse_args() + exit(main(args)) \ No newline at end of file diff --git a/scripts/release/update_release_assets_test.py b/scripts/release/update_release_assets_test.py new file mode 100644 index 0000000000..35e5baf0bb --- /dev/null +++ b/scripts/release/update_release_assets_test.py @@ -0,0 +1,30 @@ +from pathlib import Path +from tempfile import TemporaryDirectory +import yaml +from update_release_assets import ReleaseLayout + +SCRIPT_PATH = Path(__file__) +TEST_DIR = SCRIPT_PATH.parent / 'test-data' + +def test_release_layout(): + spec = TEST_DIR / 'release-layout.yml' + release_layout = ReleaseLayout(spec) + with TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + release_layout.make(tmp_path, []) + + for artifact in yaml.safe_load(spec.read_text())['layout'].keys(): + artifact_path = tmp_path / artifact + assert artifact_path.is_file() + + if artifact == "hello-world.txt": + content = artifact_path.read_text() + assert content == "hello world!\n" + if artifact == "checksums.txt": + content = artifact_path.read_text() + # The hash of the hello-world.txt is deterministic, so we can assert it here. + assert "ecf701f727d9e2d77c4aa49ac6fbbcc997278aca010bddeeb961c10cf54d435a hello-world.txt" in content + # The has of the hello-world.zip is not deterministic, so we can't assert its hash. + assert "hello-world.zip" in content + + diff --git a/scripts/release/utils.py b/scripts/release/utils.py index 4e9bb99dd2..cdb747c076 100644 --- a/scripts/release/utils.py +++ b/scripts/release/utils.py @@ -1,5 +1,6 @@ import re import yaml +from pathlib import Path def get_query_short_names(rule_dict): """Gets a list of the query "short_name" properties for the given rule""" @@ -18,7 +19,9 @@ def split_rule_id(rule_id): def get_standard_version(standard): """Gets the qlpack version for the given standard.""" - qlpack_path = "cpp/" + standard.split("-")[0].lower() + "/src/qlpack.yml" + module_path = Path(__file__) + repo_root = module_path.parent.parent.parent + qlpack_path = repo_root / "cpp" / standard.split("-")[0].lower() /"src" / "qlpack.yml" with open(qlpack_path, 'r') as qlpack_file: try: qlpack = yaml.safe_load(qlpack_file) diff --git a/scripts/release/validate-version.py b/scripts/release/validate-version.py new file mode 100644 index 0000000000..3e8168d5b1 --- /dev/null +++ b/scripts/release/validate-version.py @@ -0,0 +1,47 @@ +import semantic_version # type: ignore +from typing import Literal, TYPE_CHECKING +from subprocess import run + +if TYPE_CHECKING: + from argparse import Namespace + +def get_release_version_of_ref() -> semantic_version.Version: + cp = run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True) + if cp.returncode != 0: + raise RuntimeError("Failed to get current branch name") + branch_name = cp.stdout.strip() + ns, version_str = branch_name.split("/") + if ns != "rc": + raise RuntimeError("Not on a release branch!") + + try: + return semantic_version.Version(version_str) # type: ignore + except ValueError as e: + raise RuntimeError(f"Invalid version string: {e}") + +def main(args :'Namespace') -> Literal[1, 0]: + try: + release_version = semantic_version.Version(args.version) # type: ignore + if args.hotfix: + branch_release_version = get_release_version_of_ref() + expected_version = branch_release_version.next_patch() + if release_version != expected_version: + print(f"Error: Hotfix version `{release_version}` does not iterate on {branch_release_version}. Expected `{expected_version}`. ", file=stderr) + return 1 + return 0 + except ValueError as e: + print(f"Error: invalid version: {e}", file=stderr) + return 1 + except RuntimeError as e: + print(f"Error: {e}", file=stderr) + return 1 + +if __name__ == '__main__': + from sys import stderr, exit + import argparse + + parser = argparse.ArgumentParser(description="Validate a version string") + parser.add_argument("version", help="The version string to validate") + parser.add_argument('--hotfix', action='store_true', help="Whether the release is to hotfix an existing release.") + + exit(main(parser.parse_args())) \ No newline at end of file diff --git a/scripts/release/webhook-handler.js b/scripts/release/webhook-handler.js new file mode 100644 index 0000000000..6197bedb48 --- /dev/null +++ b/scripts/release/webhook-handler.js @@ -0,0 +1,229 @@ +/** + * This function should be installed as an Azure Function with a HTTP trigger and configured as a GitHub webhook. + * It expects the following environment variables to be set: + * - GITHUB_APP_ID: the ID of the GitHub App used to authenticate + * - GITHUB_APP_INSTALLATION_ID: the ID of the GitHub App installation + * - GITHUB_APP_PRIVATE_KEY: the private key of the GitHub App + * - GITHUB_WEBHOOK_SECRET: the secret used to sign the webhook + * - GITHUB_WORKFLOW_ID: the ID of the workflow to trigger, this should be the id of the workflow `update-release-status.yml` + */ +const crypto = require('crypto'); +const { Buffer } = require('buffer'); +const https = require('https'); + +function encode(obj) { + return Buffer.from(JSON.stringify(obj)).toString('base64url'); +} + +function createJwtToken() { + + const signingKey = crypto.createPrivateKey(Buffer.from(process.env['GITHUB_APP_PRIVATE_KEY'], 'base64')); + + const claims = { + // Issue 60 seconds in the past to account for clock drift. + iat: Math.floor(Date.now() / 1000) - 60, + // The token is valid for 1 minute(s). + exp: Math.floor(Date.now() / 1000) + (1 * 60), + iss: process.env["GITHUB_APP_ID"] + }; + + const header = { + alg: "RS256", + typ: "JWT" + }; + + const payload = `${encode(header)}.${encode(claims)}`; + const signer = crypto.createSign('RSA-SHA256'); + const signature = (signer.update(payload), signer.sign(signingKey, 'base64url')); + + return `${payload}.${signature}`; +} + +function createAccessToken(context) { + return new Promise((resolve, reject) => { + const options = { + hostname: 'api.github.com', + path: `/app/installations/${process.env["GITHUB_APP_INSTALLATION_ID"]}/access_tokens`, + method: 'POST' + }; + + const req = https.request(options, (res) => { + res.on('data', (data) => { + const body = JSON.parse(data.toString('utf8')); + access_token = body.token; + //context.log(access_token); + resolve(access_token); + }); + + res.on('error', (error) => { + reject(error); + }) + }); + + req.setHeader('Accept', 'application/vnd.github+json'); + const token = createJwtToken(); + //context.log(`JWT Token ${token}`); + req.setHeader('Authorization', `Bearer ${token}`); + req.setHeader('X-GitHub-Api-Version', '2022-11-28'); + req.setHeader('User-Agent', 'CodeQL Coding Standards Automation'); + + req.end(); + }); +} + +function triggerReleaseUpdate(context, access_token, head_sha) { + context.log(`Triggering release update for head sha ${head_sha}`) + return new Promise((resolve, reject) => { + const options = { + hostname: 'api.github.com', + path: `/repos/github/codeql-coding-standards/actions/workflows/${process.env["GITHUB_WORKFLOW_ID"]}/dispatches`, + method: 'POST' + }; + + const req = https.request(options, (res) => { + res.on('error', (error) => { + reject(error); + }) + }); + + req.setHeader('Accept', 'application/vnd.github+json'); + req.setHeader('Authorization', `Bearer ${access_token}`); + req.setHeader('X-GitHub-Api-Version', '2022-11-28'); + req.setHeader('User-Agent', 'CodeQL Coding Standards Automation'); + + const params = { + ref: 'main', + inputs: { + "head-sha": head_sha + } + }; + req.on('response', (response) => { + context.log(`Received status code ${response.statusCode} with message ${response.statusMessage}`); + resolve(); + }); + req.end(JSON.stringify(params)); + }); +} + +function listCheckRunsForRefPerPage(context, access_token, ref, page = 1) { + context.log(`Listing check runs for ${ref}`) + return new Promise((resolve, reject) => { + const options = { + hostname: 'api.github.com', + path: `/repos/github/codeql-coding-standards/commits/${ref}/check-runs?page=${page}&per_page=100`, + method: 'GET', + headers: { + 'Accept': 'application/vnd.github+json', + 'Authorization': `Bearer ${access_token}`, + 'X-GitHub-Api-Version': '2022-11-28', + 'User-Agent': 'CodeQL Coding Standards Automation' + } + }; + + const req = https.request(options, (res) => { + if (res.statusCode != 200) { + reject(`Received status code ${res.statusCode} with message ${res.statusMessage}`); + } else { + var body = []; + res.on('data', (chunk) => { + body.push(chunk); + }); + res.on('end', () => { + try { + body = JSON.parse(Buffer.concat(body).toString('utf8')); + resolve(body); + } catch (error) { + reject(error); + } + }); + } + }); + req.on('error', (error) => { + reject(error); + }); + + req.end(); + }); +} + +async function listCheckRunsForRef(context, access_token, ref) { + let page = 1; + let check_runs = []; + const first_page = await listCheckRunsForRefPerPage(context, access_token, ref, page); + check_runs = check_runs.concat(first_page.check_runs); + while (first_page.total_count > check_runs.length) { + page++; + const next_page = await listCheckRunsForRefPerPage(context, access_token, ref, page); + check_runs = check_runs.concat(next_page.check_runs); + } + return check_runs; +} + +function hasReleaseStatusCheckRun(check_runs) { + return check_runs.some(check_run => check_run.name == 'release-status'); +} + +function isValidSignature(req) { + const hmac = crypto.createHmac("sha256", process.env["GITHUB_WEBHOOK_SECRET"]); + const signature = hmac.update(JSON.stringify(req.body)).digest('hex'); + const shaSignature = `sha256=${signature}`; + const gitHubSignature = req.headers['x-hub-signature-256']; + + return !shaSignature.localeCompare(gitHubSignature); +} + +module.exports = async function (context, req) { + context.log('Webhook received.'); + + if (isValidSignature(req)) { + const event = req.headers['x-github-event']; + + if (event == 'check_run') { + webhook = req.body; + + // To avoid infinite loops, we skip triggering the workflow for the following checkruns. + const check_runs_to_skip = [ + // check run created by manual dispatch of Update Release workflow + 'Update release', + // check runs created by job in Update release status workflow + 'update-release', + // when update-release calls reusable workflow Update release + 'update-release / Update release', + 'validate-check-runs', + // check run that validates the whole release + 'release-status']; + const update_release_actions = ['completed', 'rerequested']; + + if (update_release_actions.includes(webhook.action) && !check_runs_to_skip.includes(webhook.check_run.name)) { + context.log(`Triggering update release status because ${webhook.check_run.name} received action ${webhook.action}`); + + try { + const access_token = await createAccessToken(context); + const check_runs = await listCheckRunsForRef(context, access_token, webhook.check_run.head_sha); + if (hasReleaseStatusCheckRun(check_runs)) { + context.log(`Release status check run found for ${webhook.check_run.head_sha}`); + await triggerReleaseUpdate(context, access_token, webhook.check_run.head_sha); + } else { + context.log(`Skippping, no release status check run found for ${webhook.check_run.head_sha}`); + } + } catch (error) { + context.log(`Failed with error: ${error}`); + } + } else { + context.log(`Skipping action ${webhook.action} for ${webhook.check_run.name}`) + } + } else { + context.log(`Skipping event: ${event}`) + } + + context.res = { + status: 200 + }; + } else { + context.log('Received invalid GitHub signature') + context.res = { + status: 401, + body: 'Invalid x-hub-signature-256 value' + }; + } +} \ No newline at end of file diff --git a/scripts/reports/analysis_report.py b/scripts/reports/analysis_report.py index b54237581a..1afc56d89b 100644 --- a/scripts/reports/analysis_report.py +++ b/scripts/reports/analysis_report.py @@ -1,5 +1,6 @@ import diagnostics import deviations +import guideline_recategorizations from pathlib import Path import sys import utils @@ -53,6 +54,8 @@ deviations.generate_deviations_report( database_path, repo_root, output_directory) +guideline_recategorizations.generate_guideline_recategorizations_report(database_path, repo_root, output_directory) + # Load the SARIF file and generate a results summary sarif_results_summary = utils.CodingStandardsResultSummary( sarif_results_file_path) diff --git a/scripts/reports/analysis_report_test.py b/scripts/reports/analysis_report_test.py new file mode 100644 index 0000000000..aaa90cf0e1 --- /dev/null +++ b/scripts/reports/analysis_report_test.py @@ -0,0 +1,62 @@ +import pytest +from pathlib import Path +import sys +from guideline_recategorizations import generate_guideline_recategorizations_report +from deviations import generate_deviations_report + +script_path = Path(__file__) +# Add the shared modules to the path so we can import them. +sys.path.append(str(script_path.parent.parent / 'shared')) +from codeql import CodeQL, CodeQLError + +REPO_ROOT = Path(__file__).resolve().parent.parent.parent +SCRIPTS_DIR = REPO_ROOT / 'scripts' +TEST_DATA_DIR = Path(__file__).resolve().parent / 'test-data' + +def test_guideline_recategorizations_report(tmp_path): + + db_path = tmp_path / 'test-db' + src_root = TEST_DATA_DIR / 'guideline-recategorizations' + codeql = CodeQL() + + compile_src_command = "clang -fsyntax-only test.cpp" + index_coding_standards_config_command = f"python3 {SCRIPTS_DIR}/configuration/process_coding_standards_config.py" + + try: + codeql.create_database(src_root, 'cpp', db_path, compile_src_command, index_coding_standards_config_command) + except CodeQLError as err: + print(err.stdout) + print(err.stderr) + raise err + + generate_guideline_recategorizations_report(db_path, REPO_ROOT, tmp_path) + + expected = (TEST_DATA_DIR / 'guideline-recategorizations' / 'guideline_recategorizations_report.md.expected').read_text() + expected = expected.replace("$codeql-version$", codeql.version).replace("$database-path$", str(db_path)) + actual = (tmp_path / "guideline_recategorizations_report.md").read_text() + + assert(expected == actual) + +def test_deviations_report(tmp_path): + + db_path = tmp_path / 'test-db' + src_root = TEST_DATA_DIR / 'deviations' + codeql = CodeQL() + + compile_src_command = "clang -fsyntax-only test.cpp" + index_coding_standards_config_command = f"python3 {SCRIPTS_DIR}/configuration/process_coding_standards_config.py" + + try: + codeql.create_database(src_root, 'cpp', db_path, compile_src_command, index_coding_standards_config_command) + except CodeQLError as err: + print(err.stdout) + print(err.stderr) + raise err + + generate_deviations_report(db_path, REPO_ROOT, tmp_path) + + expected = (TEST_DATA_DIR / 'deviations' / 'deviations_report.md.expected').read_text() + expected = expected.replace("$codeql-version$", codeql.version).replace("$database-path$", str(db_path)) + actual = (tmp_path / "deviations_report.md").read_text() + + assert(expected == actual) \ No newline at end of file diff --git a/scripts/reports/codeql.py b/scripts/reports/codeql.py deleted file mode 100644 index 29a7d00c0e..0000000000 --- a/scripts/reports/codeql.py +++ /dev/null @@ -1,23 +0,0 @@ -import utils -import sys -from pathlib import Path - -script_path = Path(__file__) -# Add the shared modules to the path so we can import them. -sys.path.append(script_path.parent.parent / 'shared') -from codeql import CodeQL - - -class CodeQLValidationSummary(): - def __init__(self): - """Validate that a compatible version of the CodeQL CLI is on the path.""" - - self.codeql = CodeQL() - - # Determine our supported environments - supported_environments = utils.load_supported_environments() - - self.supported_cli = False - for supported_environment in supported_environments: - if supported_environment["codeql_cli"] == self.codeql.version: - self.supported_cli = True diff --git a/scripts/reports/codeqlvalidation.py b/scripts/reports/codeqlvalidation.py new file mode 100644 index 0000000000..e65a1c103f --- /dev/null +++ b/scripts/reports/codeqlvalidation.py @@ -0,0 +1,23 @@ +import utils +import sys +from pathlib import Path + +script_path = Path(__file__) +# Add the shared modules to the path so we can import them. +sys.path.append(str(script_path.parent.parent / 'shared')) +from codeql import CodeQL + + +class CodeQLValidationSummary(): + def __init__(self): + """Validate that a compatible version of the CodeQL CLI is on the path.""" + + self.codeql = CodeQL() + + # Determine our supported environments + supported_environments = utils.load_supported_environments() + + self.supported_cli = False + for supported_environment in supported_environments: + if supported_environment["codeql_cli"] == self.codeql.version: + self.supported_cli = True diff --git a/scripts/reports/deviations.py b/scripts/reports/deviations.py index 29e977c3c0..d511d35dbb 100644 --- a/scripts/reports/deviations.py +++ b/scripts/reports/deviations.py @@ -1,8 +1,15 @@ from contextlib import redirect_stdout from pathlib import Path -from codeql import CodeQLError, CodeQLValidationSummary +from codeqlvalidation import CodeQLValidationSummary from error import failure import re +import sys + +script_path = Path(__file__) +# Add the shared modules to the path so we can import them. +sys.path.append(str(script_path.parent.parent / 'shared')) +from codeql import CodeQLError + if __name__ == '__main__': failure("Error: this Python module does not support standalone execution!") @@ -35,7 +42,7 @@ def __init__(self, database_path, repo_root): # Get a list of deviations print("Running the deviation query...") self.codeql_summary.codeql.run_queries( - database_path, *query_paths, search_path=str(repo_root), no_rerun=True) + database_path, *query_paths, no_rerun=True) print("Decoding deviation query results") diff --git a/scripts/reports/diagnostics.py b/scripts/reports/diagnostics.py index 3b95bef4a8..78535a8f9a 100644 --- a/scripts/reports/diagnostics.py +++ b/scripts/reports/diagnostics.py @@ -1,8 +1,13 @@ from contextlib import redirect_stdout from pathlib import Path -from codeql import CodeQLError, CodeQLValidationSummary +from codeqlvalidation import CodeQLValidationSummary from error import failure +import sys +script_path = Path(__file__) +# Add the shared modules to the path so we can import them. +sys.path.append(str(script_path.parent.parent / 'shared')) +from codeql import CodeQLError class DiagnosticsSummary: def __init__(self, database_path, repo_root): @@ -39,7 +44,7 @@ def __init__(self, database_path, repo_root): # Run all the diagnostics over the database print("Running the diagnostic queries...") self.codeql_summary.codeql.run_queries( - database_path, *queries, search_path=str(repo_root), no_rerun=True) + database_path, *queries, no_rerun=True) print("Decoding diagnostic query results") self.extraction_errors = self.codeql_summary.codeql.decode_results( diff --git a/scripts/reports/guideline_recategorizations.py b/scripts/reports/guideline_recategorizations.py new file mode 100644 index 0000000000..ebd0fa59de --- /dev/null +++ b/scripts/reports/guideline_recategorizations.py @@ -0,0 +1,114 @@ +from contextlib import redirect_stdout +from pathlib import Path +from codeqlvalidation import CodeQLValidationSummary +from error import failure +import sys + +script_path = Path(__file__) +# Add the shared modules to the path so we can import them. +sys.path.append(str(script_path.parent.parent / 'shared')) +from codeql import CodeQLError + + +if __name__ == '__main__': + failure("Error: this Python module does not support standalone execution!") + + +class GuidelineRecategorizationsSummary: + def __init__(self, database_path, repo_root): + if isinstance(database_path, str): + database_path = Path(database_path) + if isinstance(repo_root, str): + repo_root = Path(repo_root) + + self.database_path = database_path + try: + self.codeql_summary = CodeQLValidationSummary() + except CodeQLError as err: + failure("Error: Could not initialize CodeQL", err) + + guideline_recategorizations_path = repo_root.joinpath( + 'cpp', 'common', 'src', 'codingstandards', 'cpp', 'guideline_recategorizations') + + queries = ['ListGuidelineRecategorizations.ql', 'InvalidGuidelineRecategorizations.ql'] + + query_paths = map(guideline_recategorizations_path.joinpath, queries) + + try: + # Cleanup database cache to prevent potential cache issue + self.codeql_summary.codeql.cleanup(database_path, mode="brutal") + # Get a list of guideline recategorizations + print("Running the guideline recategorizations queries...") + self.codeql_summary.codeql.run_queries( + database_path, *query_paths, no_rerun=True) + + print("Decoding guideline recategorizations queries results") + + for query in queries: + if query.startswith("List"): + decoded_results = self.codeql_summary.codeql.decode_results( + database_path, guideline_recategorizations_path.joinpath(query), no_titles=True) + self.guideline_recategorizations = decoded_results + elif query.startswith("Invalid"): + decoded_results = self.codeql_summary.codeql.decode_results( + database_path, guideline_recategorizations_path.joinpath(query), entities='url,string', no_titles=True) + self.invalid_guideline_recategorizations = decoded_results + else: + failure( + f"Error: Don't know how to decode query results for {query}") + except CodeQLError as err: + failure("Error: Failed to run guideline recategorizations queries", err) + + +def generate_guideline_recategorizations_report(database_path, repo_root, output_directory): + """Print a "guideline recategorizations report".""" + + guideline_recategorizations_summary = GuidelineRecategorizationsSummary(database_path, repo_root) + guideline_recategorizations_report_path = output_directory.joinpath( + "guideline_recategorizations_report.md") + try: + guideline_recategorizations_report_file = open( + guideline_recategorizations_report_path, "w") + except PermissionError: + failure( + f"Error: No permission to write to the output file located at '{guideline_recategorizations_report_path}'") + else: + with guideline_recategorizations_report_file: + # Print to report file, rather than stdout + with redirect_stdout(guideline_recategorizations_report_file): + print("# Guideline recategorizations report") + print() + print("## Overview") + print() + print( + f" - Report generated with {'supported' if guideline_recategorizations_summary.codeql_summary.supported_cli else 'unsupported'} CodeQL version {guideline_recategorizations_summary.codeql_summary.codeql.version}") + print( + f" - Database path: {str(guideline_recategorizations_summary.database_path.resolve())}") + number_of_guideline_recategorizations = len( + guideline_recategorizations_summary.guideline_recategorizations) + number_of_invalid_guideline_recategorizations = len( + guideline_recategorizations_summary.invalid_guideline_recategorizations) + print( + f" - { number_of_guideline_recategorizations } applicable guideline recategorizations and {number_of_invalid_guideline_recategorizations} invalid guideline recategorizations found in the database") + print() + print("## Guideline recategorizations") + print() + print( + "| Rule ID | Category | Recategorized category") + print( + "| --- | --- | --- |") + for guideline_recategorization in guideline_recategorizations_summary.guideline_recategorizations: + rule_id = guideline_recategorization[0] + category = guideline_recategorization[1] + recategorized_category = guideline_recategorization[2] + print( + f"| { rule_id } | { category } | { recategorized_category } | ") + print() + print("## Invalid guideline recategorizations") + print("| Path | Reason |") + print("| --- | --- |") + for invalid_guideline_recategorization in guideline_recategorizations_summary.invalid_guideline_recategorizations: + location = invalid_guideline_recategorization[1].split(':', 2)[2] + path, reason = map( + str.strip, invalid_guideline_recategorization[2].split(':')) + print(f"| {path}:{location} | {reason} |") diff --git a/scripts/reports/requirements.txt b/scripts/reports/requirements.txt index 9d90467955..219271be96 100644 --- a/scripts/reports/requirements.txt +++ b/scripts/reports/requirements.txt @@ -1 +1,2 @@ -pyyaml==5.4 \ No newline at end of file +pyyaml==5.4 +pytest==7.2.0 \ No newline at end of file diff --git a/scripts/reports/test-data/deviations/coding-standards.yml b/scripts/reports/test-data/deviations/coding-standards.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/reports/test-data/deviations/deviations_report.md.expected b/scripts/reports/test-data/deviations/deviations_report.md.expected new file mode 100644 index 0000000000..e9874023dc --- /dev/null +++ b/scripts/reports/test-data/deviations/deviations_report.md.expected @@ -0,0 +1,48 @@ +# Deviations report + +## Overview + + - Report generated with supported CodeQL version $codeql-version$ + - Database path: $database-path$ + - 3 valid deviation records and 14 invalid deviation records found in the database + - 3 valid deviation permits and 2 invalid deviation permits found in the database + +## Deviation Records + +| Rule ID | Query ID | Automated Scope | Scope | Justification | Background | Requirements +| --- | --- | --- | --- | --- | --- | --- | +| A0-1-1 | cpp/autosar/useless-assignment | Applies to the following file paths: invalid | | | | | +| A0-1-1 | cpp/autosar/useless-assignment | Applies to the following file paths: valid | | This useless assignment is required. | | | +| A0-4-2 | cpp/autosar/type-long-double-used | Identified by the use of the code-identifier: a-0-4-2-deviation | | long double is required for interaction with third-party libraries. | | | + +## Invalid Deviation Records +| Path | Reason | +| --- | --- | +| invalid/coding-standards.xml:5:7:5:26 | No rule-id and query-id specified for this deviation record. | +| invalid/coding-standards.xml:6:7:8:26 | The rule-id `bad rule id` for this deviation matches none of the available queries. | +| invalid/coding-standards.xml:9:7:11:26 | A query-id of `bad rule id` is specified for this deviation, but not rule-id is specified. | +| invalid/coding-standards.xml:15:7:17:26 | A query-id of `cpp/autosar/useless-assignment` is specified for this deviation, but not rule-id is specified. | +| invalid/coding-standards.xml:22:7:26:26 | A deviation `raised-by` is specified without providing an `approved-by`. | +| invalid/coding-standards.xml:22:7:26:26 | A deviation `raised-by` is specified without providing both a `name` and `date`. | +| invalid/coding-standards.xml:27:7:33:26 | A deviation `raised-by` is specified without providing an `approved-by`. | +| invalid/coding-standards.xml:27:7:33:26 | A deviation `raised-by` is specified without providing both a `name` and `date`. | +| invalid/coding-standards.xml:34:7:41:26 | A deviation `raised-by` is specified without providing an `approved-by`. | +| invalid/coding-standards.xml:42:7:50:26 | A deviation `approved-by` is specified without providing both a `name` and `date`. | +| invalid/coding-standards.xml:51:7:61:26 | A deviation `approved-by` is specified without providing both a `name` and `date`. | +| invalid/coding-standards.xml:74:7:78:26 | There is no deviation permit with id `non-existing-permit`. | +| invalid/coding-standards.xml:79:7:81:26 | No rule-id and query-id specified for this deviation record. | +| invalid/coding-standards.xml:85:7:88:26 | The deviation is applied to a query with the rule category 'mandatory' that does not permit a deviation. | + +## Deviation Permits + +| Permit ID | Rule ID | Query ID | Automated Scope | Scope | Justification | Background | Requirements +| --- | --- | --- | --- | --- | --- | --- | --- | +| DP1 | | | Application depends on the associated deviation records | | foo bar baz | | | +| DP2 | A0-1-1 | cpp/autosar/useless-assignment | Application depends on the associated deviation records | | | | | +| DP3 | | | Application depends on the associated deviation records | | | | | + +## Invalid Deviation Permits +| Path | Reason | +| --- | --- | +| invalid/coding-standards.xml:100:7:103:33 | Deviation permit does not specify a permit identifier. | +| invalid/coding-standards.xml:104:7:107:33 | Deviation permit specifies unknown property `invalid-property`. | diff --git a/scripts/reports/test-data/deviations/invalid/coding-standards.yml b/scripts/reports/test-data/deviations/invalid/coding-standards.yml new file mode 100644 index 0000000000..1ce8cc718a --- /dev/null +++ b/scripts/reports/test-data/deviations/invalid/coding-standards.yml @@ -0,0 +1,58 @@ +deviations: + - + - rule-id: bad rule id + - query-id: bad rule id + - rule-id: A0-1-1 + - query-id: cpp/autosar/useless-assignment + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + raised-by: + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + raised-by: + name: foo1 + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + raised-by: + name: foo2 + date: 1970-01-01Z + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + raised-by: + name: foo3 + date: 1970-01-01Z + approved-by: + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + raised-by: + name: foo4 + date: 1970-01-01Z + approved-by: + name: bar1 + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + raised-by: + name: foo5 + date: 1970-01-01Z + approved-by: + name: bar2 + date: 1970-01-01Z + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + permit-id: non-existing-permit + - permit-id: DP1 + - permit-id: DP2 + - rule-id: RULE-9-1 + query-id: c/misra/object-with-auto-storage-duration-read-before-init +deviation-permits: + - permit-id: DP1 + justification: foo bar baz + - permit-id: DP2 + rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + - permit-id: DP3 + invalid-property: invalid-property diff --git a/scripts/reports/test-data/deviations/test.cpp b/scripts/reports/test-data/deviations/test.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/reports/test-data/deviations/valid/coding-standards.yml b/scripts/reports/test-data/deviations/valid/coding-standards.yml new file mode 100644 index 0000000000..34b12a3b90 --- /dev/null +++ b/scripts/reports/test-data/deviations/valid/coding-standards.yml @@ -0,0 +1,7 @@ +deviations: + - rule-id: A0-1-1 + query-id: cpp/autosar/useless-assignment + justification: This useless assignment is required. + - rule-id: A0-4-2 + justification: long double is required for interaction with third-party libraries. + code-identifier: a-0-4-2-deviation diff --git a/scripts/reports/test-data/guideline-recategorizations/coding-standards.yml b/scripts/reports/test-data/guideline-recategorizations/coding-standards.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/reports/test-data/guideline-recategorizations/guideline_recategorizations_report.md.expected b/scripts/reports/test-data/guideline-recategorizations/guideline_recategorizations_report.md.expected new file mode 100644 index 0000000000..425eba1bc3 --- /dev/null +++ b/scripts/reports/test-data/guideline-recategorizations/guideline_recategorizations_report.md.expected @@ -0,0 +1,29 @@ +# Guideline recategorizations report + +## Overview + + - Report generated with supported CodeQL version $codeql-version$ + - Database path: $database-path$ + - 8 applicable guideline recategorizations and 5 invalid guideline recategorizations found in the database + +## Guideline recategorizations + +| Rule ID | Category | Recategorized category +| --- | --- | --- | +| A0-1-1 | required | advisory | +| A0-1-1 | required | mandatory | +| A0-1-2 | required | disapplied | +| RULE-9-1 | mandatory | required | +| CON50-CPP | rule | required | +| A0-1-6 | advisory | disapplied | +| A10-4-1 | advisory | required | +| A11-0-1 | advisory | mandatory | + +## Invalid guideline recategorizations +| Path | Reason | +| --- | --- | +| invalid/coding-standards.xml:5:7:8:43 | 'Invalid recategorization from 'required' to 'advisory'.' for rule A0-1-1. | +| invalid/coding-standards.xml:9:7:12:43 | 'Invalid recategorization from 'required' to 'disapplied'.' for rule A0-1-2. | +| invalid/coding-standards.xml:13:7:16:43 | 'Unknown rule id 'A1-4-3'.' for rule A1-4-3. | +| invalid/coding-standards.xml:17:7:20:43 | 'Invalid recategorization from 'mandatory' to 'required'.' for rule RULE-9-1. | +| invalid/coding-standards.xml:21:7:24:43 | 'Invalid recategorization from 'rule' to 'required'.' for rule CON50-CPP. | diff --git a/scripts/reports/test-data/guideline-recategorizations/invalid/coding-standards.yml b/scripts/reports/test-data/guideline-recategorizations/invalid/coding-standards.yml new file mode 100644 index 0000000000..cd6abbf120 --- /dev/null +++ b/scripts/reports/test-data/guideline-recategorizations/invalid/coding-standards.yml @@ -0,0 +1,11 @@ +guideline-recategorizations: + - rule-id: "A0-1-1" + category: "advisory" + - rule-id: "A0-1-2" + category: "disapplied" + - rule-id: "A1-4-3" + category: "mandatory" + - rule-id: "RULE-9-1" + category: "required" + - rule-id: "CON50-CPP" + category: "required" diff --git a/scripts/reports/test-data/guideline-recategorizations/test.cpp b/scripts/reports/test-data/guideline-recategorizations/test.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/reports/test-data/guideline-recategorizations/valid/coding-standards.yml b/scripts/reports/test-data/guideline-recategorizations/valid/coding-standards.yml new file mode 100644 index 0000000000..ed778a5cc1 --- /dev/null +++ b/scripts/reports/test-data/guideline-recategorizations/valid/coding-standards.yml @@ -0,0 +1,9 @@ +guideline-recategorizations: + - rule-id: "A0-1-1" + category: "mandatory" + - rule-id: "A0-1-6" + category: "disapplied" + - rule-id: "A10-4-1" + category: "required" + - rule-id: "A11-0-1" + category: "mandatory" diff --git a/scripts/reports/utils.py b/scripts/reports/utils.py index 4fae73d564..6f5785808e 100644 --- a/scripts/reports/utils.py +++ b/scripts/reports/utils.py @@ -149,7 +149,7 @@ def __init__(self, sarif_results_file_path): if standard_rule_id in self.guideline_obligation_level[standard_short_name]: if not self.guideline_obligation_level[standard_short_name][standard_rule_id] == obligation_level: print( - f"WARNING: Rule { rule['id'] } specifies a conflicting obligation level of { obligation_level }, was previously specified as { guideline_obligation_level[standard_short_name][standard_rule_id] }.") + f"WARNING: Rule { rule['id'] } specifies a conflicting obligation level of { obligation_level }, was previously specified as { self.guideline_obligation_level[standard_short_name][standard_rule_id] }.") else: self.guideline_obligation_level[standard_short_name][standard_rule_id] = obligation_level # Add deviation counts for the rule @@ -183,7 +183,7 @@ def generate_guideline_compliance_summary(output_directory, results_summary): print( "**Result**: " + ("Not compliant" if total_guidelines_violated > 0 else "Compliant")) standard_pretty_name = { - "cert": "CERT C++ 2016", "autosar": "AUTOSAR C++ 20-11"} + "cert": "CERT C++ 2016", "autosar": "AUTOSAR C++ R22-11, R21-11, R20-11, R19-11 and R19-03"} print("**Coding Standards applied**: " + ", ".join([standard_pretty_name[standard_short_name] for standard_short_name in results_summary.guideline_violation_count.keys()])) diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 56682f6332..8a240a6dab 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,16 +1,16 @@ beautifulsoup4==4.9.3 -certifi==2020.11.8 +certifi==2023.7.22 chardet==3.0.4 gitdb==4.0.5 -GitPython==3.1.12 +GitPython==3.1.41 idna==2.10 Jinja2==2.11.3 MarkupSafe==1.1.1 -requests==2.25.0 +requests==2.31.0 smmap==3.0.5 soupsieve==2.0.1 -urllib3==1.26.5 -pyyaml==5.4 -wheel==0.37.0 +pyyaml==6.0.1 +urllib3==1.26.18 +wheel==0.38.1 jsonschema==4.9.1 marko==1.2.1 \ No newline at end of file diff --git a/scripts/shared/codeql.py b/scripts/shared/codeql.py index 41100ed6d7..095348c618 100644 --- a/scripts/shared/codeql.py +++ b/scripts/shared/codeql.py @@ -20,7 +20,7 @@ def __str__(self): class CodeQL(): - def __init__(self) -> 'CodeQL': + def __init__(self) -> None: codeql_result = subprocess.run( ["codeql", "version", "--format=json"], capture_output=True) if not codeql_result.returncode == 0: @@ -36,7 +36,7 @@ def __init__(self) -> 'CodeQL': raise CodeQLError( f"Failed to retrieve codeql version information with error: {e.msg}") - def __build_command_options(self, **options: Dict[str, str]) -> List[str]: + def __build_command_options(self, **options: str) -> List[str]: command_options = [] for key, value in options.items(): command_options.append(f"--{key.replace('_', '-')}") @@ -59,7 +59,7 @@ def cleanup(self, database_path: Path, mode: str = "normal") -> None: raise CodeQLError( f"Unable to cleanup database {database_path}", stdout=result.stdout, stderr=result.stderr, returncode=result.returncode) - def run_queries(self, database_path: Path, *queries: List[Path], **options: Dict[str, str]) -> None: + def run_queries(self, database_path: Path, *queries: Path, **options: str) -> None: database_path = database_path.resolve() command_options = self.__build_command_options(**options) @@ -91,7 +91,7 @@ def get_qlpack(self, qlpack_path: Path) -> Any: with qlpack_path.open() as f: return yaml.safe_load(f) - def decode_results(self, database_path: Path, query_path: Path, **options: Dict[str, str]) -> Iterator: + def decode_results(self, database_path: Path, query_path: Path, **options: str) -> List: qlpack_path = self.resolve_qlpack_path(query_path) qlpack = self.get_qlpack(qlpack_path) relative_query_path = query_path.relative_to(qlpack_path.parent) @@ -116,9 +116,9 @@ def decode_results(self, database_path: Path, query_path: Path, **options: Dict[ raise CodeQLError( f"Could not read the output of the query {query_path}", stdout=result.stdout, stderr=result.stderr, returncode=result.returncode) with open(temp_file) as tmp: - return csv.reader(tmp) + return list(csv.reader(tmp)) - def generate_query_help(self, query_help_path: Path, output: Path, format : str = "markdown", **options: Dict[str, str]) -> None: + def generate_query_help(self, query_help_path: Path, output: Path, format : str = "markdown", **options: str) -> None: command = ['codeql', 'generate', 'query-help'] options['output'] = str(output) options['format'] = format @@ -130,4 +130,27 @@ def generate_query_help(self, query_help_path: Path, output: Path, format : str result = subprocess.run(command, capture_output=True) if not result.returncode == 0: raise CodeQLError( - f"Failed to generate query help file {query_help_path}", stdout=result.stdout, stderr=result.stderr, returncode=result.returncode) \ No newline at end of file + f"Failed to generate query help file {query_help_path}", stdout=result.stdout, stderr=result.stderr, returncode=result.returncode) + + def format(self, path: Path) -> None: + command = ['codeql', 'query', 'format', '--in-place', str(path)] + + result = subprocess.run(command, capture_output=True) + if not result.returncode == 0: + raise CodeQLError( + f"Failed to format file {path}", stdout=result.stdout, stderr=result.stderr, returncode=result.returncode) + + def create_database(self, src_root: Path, language: str, database: Path, *build_commands : str, **options: str) -> None: + command = ['codeql', 'database', 'create'] + options['source-root'] = str(src_root) + options['language'] = language + + command_options = self.__build_command_options(**options) + command.extend(command_options) + command.extend([f'--command={build_command}' for build_command in build_commands]) + command.append(str(database)) + + result = subprocess.run(command, capture_output=True) + if not result.returncode == 0: + raise CodeQLError( + f"Failed to build database {database} from {src_root} with language {language} and commands [{','.join(build_commands)}]", stdout=result.stdout, stderr=result.stderr, returncode=result.returncode) \ No newline at end of file diff --git a/scripts/update_codeql_dependency.sh b/scripts/update_codeql_dependency.sh new file mode 100755 index 0000000000..5aba4bae2c --- /dev/null +++ b/scripts/update_codeql_dependency.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# If there aren't two arguments, print usage and exit. +if [[ -z $2 ]]; +then + echo "Usage: update_codeql_dependencies.sh " + exit +fi + +echo "Updating CodeQL dependency $1 to $2." + +# update the qlpacks +find . -name 'qlpack.yml' | grep -v './codeql_modules' | xargs sed -i -r "s#${1}: [^\s]+#${1}: ${2}#" + +# update the lock files +find . -name 'codeql-pack.lock.yml' | grep -v './codeql_modules' | xargs sed -i -r -z "s#${1}:\n(\s*)version: [^\s]+\n#${1}:\n\1version: ${2}\n#" + +echo "Done." diff --git a/scripts/upgrade-codeql-dependencies/requirements.txt b/scripts/upgrade-codeql-dependencies/requirements.txt new file mode 100644 index 0000000000..55b810e4aa --- /dev/null +++ b/scripts/upgrade-codeql-dependencies/requirements.txt @@ -0,0 +1,7 @@ +certifi==2023.7.22 +charset-normalizer==3.2.0 +idna==3.4 +requests==2.31.0 +semantic-version==2.10.0 +urllib3==1.26.18 +pyyaml==6.0.1 \ No newline at end of file diff --git a/scripts/upgrade-codeql-dependencies/upgrade-codeql-dependencies.py b/scripts/upgrade-codeql-dependencies/upgrade-codeql-dependencies.py new file mode 100644 index 0000000000..c76303e654 --- /dev/null +++ b/scripts/upgrade-codeql-dependencies/upgrade-codeql-dependencies.py @@ -0,0 +1,116 @@ +import json +import requests +from typing import Optional, Dict, List, Tuple +from semantic_version import Version +from pathlib import Path +import yaml + +SCRIPT_PATH = Path(__file__) +CODING_STANDARDS_ROOT = SCRIPT_PATH.parent.parent.parent +SUPPORTED_VERSIONS_PATH = CODING_STANDARDS_ROOT / "supported_codeql_configs.json" + +def get_compatible_stdlib(version: Version) -> Optional[Tuple[str, str]]: + tag = f"codeql-cli/v{version}" + response = requests.get(f"https://raw.githubusercontent.com/github/codeql/{tag}/cpp/ql/lib/qlpack.yml") + + if response.status_code == 200: + # Parse the qlpack.yml returned in the response as a yaml file to read the version property + qlpack = yaml.safe_load(response.text) + if qlpack is not None and "version" in qlpack: + return (tag, qlpack["version"]) + return None + +def get_compatible_bundle(version: Version, token: str) -> Optional[str]: + tag = f"codeql-bundle-v{version}" + response = requests.get(f"https://api.github.com/repos/github/codeql-action/releases/tags/{tag}", headers={ + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {token}", + "X-GitHub-Api-Version": "2022-11-28" + }) + + if response.status_code == 200: + return tag + return None + +def main(cli_version : str, github_token: str) -> None: + try: + parsed_cli_version = Version(cli_version) + compatible_stdlib_return = get_compatible_stdlib(parsed_cli_version) + if compatible_stdlib_return is None: + print(f"Unable to find compatible standard library for: {parsed_cli_version}") + exit(1) + compatible_bundle = get_compatible_bundle(parsed_cli_version, github_token) + if compatible_bundle is None: + print(f"Unable to find compatible bundle for: {parsed_cli_version}") + exit(1) + + compatible_stdlib_tag, compatible_stdlib_version = compatible_stdlib_return + + with SUPPORTED_VERSIONS_PATH.open("r") as f: + supported_versions = json.load(f) + + supported_envs: List[Dict[str, str]] = supported_versions["supported_environment"] + if len(supported_envs) != 1: + print("Expected exactly one supported environment, cannot upgrade!") + exit(1) + supported_env = supported_envs[0] + supported_env["codeql_cli"] = str(parsed_cli_version) + supported_env["codeql_cli_bundle"] = compatible_bundle + supported_env["codeql_standard_library"] = compatible_stdlib_tag + + with SUPPORTED_VERSIONS_PATH.open("w") as f: + json.dump(supported_versions, f, indent=2) + + # Find every qlpack.yml file in the repository + qlpack_files = list(CODING_STANDARDS_ROOT.rglob("qlpack.yml")) + # Filter out any files that are in a hidden directory + qlpack_files = [f for f in qlpack_files if not any(part for part in f.parts if part.startswith("."))] + + # Update the "codeql/cpp-all" entries in the "dependencies" property in every qlpack.yml file + for qlpack_file in qlpack_files: + with qlpack_file.open("r") as f: + qlpack = yaml.safe_load(f) + print("Updating dependencies in " + str(qlpack_file)) + if "codeql/cpp-all" in qlpack["dependencies"]: + qlpack["dependencies"]["codeql/cpp-all"] = compatible_stdlib_version + with qlpack_file.open("w") as f: + yaml.safe_dump(qlpack, f, sort_keys=False) + + # Call CodeQL to update the lock files by running codeql pack upgrade + # Note: we need to do this after updating all the qlpack files, + # otherwise we may get dependency resolution errors + # Note: we need to update all qlpack files, because they may + # transitively depend on the packs we changed + for qlpack_file in qlpack_files: + qlpack = qlpack_file.parent + print("Updating lock files for " + str(qlpack)) + os.system(f"codeql pack upgrade {qlpack}") + + + except ValueError as e: + print(e) + exit(1) + +if __name__ == '__main__': + import sys + import argparse + import os + + parser = argparse.ArgumentParser(description='Upgrade CodeQL dependencies') + + parser.add_argument('--cli-version', type=str, required=True, help='CodeQL CLI version') + parser.add_argument('--github-auth-stdin', action='store_true', help='Authenticate to the GitHub API by providing a GitHub token via standard input.') + + args = parser.parse_args() + if args.github_auth_stdin: + token = sys.stdin.read() + else: + if "GITHUB_TOKEN" not in os.environ: + print("GITHUB_TOKEN environment variable not set") + exit(1) + token = os.environ["GITHUB_TOKEN"] + + main(args.cli_version, token) + + + diff --git a/scripts/util/Get-DuplicateRules.ps1 b/scripts/util/Get-DuplicateRules.ps1 new file mode 100644 index 0000000000..d90f6fc716 --- /dev/null +++ b/scripts/util/Get-DuplicateRules.ps1 @@ -0,0 +1,47 @@ +#!/usr/bin/env pwsh +param( + [ValidateSet('c', 'cpp', 'all')] + [string] + $Language = 'all', + [switch] + $CIMode + +) + +Import-Module -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" + +# load the rules. +$rules = Get-RulesFromCSV -Language $Language + +# find out duplicates +$counter = @{} + +foreach($rule in $rules){ + $key = "$($rule.Language):$($rule.Standard):$($rule.ID)" + if($counter.Contains($key)){ + $counter[$key] += $rule + }else{ + $counter[$key] = @() + $counter[$key] += $rule + } +} + +$duplicates = @() +$numDuplicates = 0 + +foreach($k in $counter.Keys){ + if($counter[$k].Count -gt 1){ + $numDuplicates = $numDuplicates + 1 + foreach($v in $counter[$k]){ + $duplicates += $v + } + } +} + +$duplicates | Format-Table + +if(($CIMode) -and ($numDuplicates -gt 0)){ + throw "Found $numDuplicates duplicate Rule IDs" +}else{ + Write-Host "Found $numDuplicates duplicate Rule IDs" +} \ No newline at end of file diff --git a/scripts/util/Test-SharedImplementationsHaveTestCases.ps1 b/scripts/util/Test-SharedImplementationsHaveTestCases.ps1 new file mode 100644 index 0000000000..09d2b49a81 --- /dev/null +++ b/scripts/util/Test-SharedImplementationsHaveTestCases.ps1 @@ -0,0 +1,117 @@ +#!/usr/bin/env pwsh +param( + [ValidateSet('c', 'cpp')] + [string] + $Language = 'c', + [Parameter(Mandatory = $false)] + [string] + $ReportDir = (Get-Location), + [switch] + $CIMode +) + +Import-Module -Force -Name "$PSScriptRoot/../PSCodingStandards/CodingStandards" + +$allQueries = @() +$queriesToCheck = @() + +# load all the queries +foreach ($s in $AVAILABLE_SUITES) { + $allQueries += Get-RulesInSuite -Suite $s -Language $Language +} + +foreach ($q in $allQueries){ + if($q | Get-Member "shared_implementation_short_name"){ + $queriesToCheck += $q + } +} + +if ($queriesToCheck.Count -eq 0) { + throw "No queries loaded." +} +else { + Write-Host "Loaded $($queriesToCheck.Count) queries with shared implementations." +} + +# What we want to verify is that IF a shared implementation is used, then we +# have a valid test case WITHIN the language that is using it. + +$REPORT = @() + +foreach($q in $queriesToCheck){ + # Get test directory + $testDirectory = Get-TestDirectory -RuleObject $q -Language $Language + Write-Host "Verifying $Language language tests in $testDirectory..." + + + $row = @{ + "SUITE" = $q.__memberof_suite; + "PACKAGE" = $q.__memberof_package; + "RULE" = $q.__memberof_rule; + "QUERY" = $q.short_name; + "SHARED_NAME" = $q.shared_implementation_short_name; + "TEST_DIR_EXISTS" = $false; + "SOURCE_CODE_EXISTS" = $false; + "EXPECTED_EXISTS" = $false; + "REFERENCE_EXISTS" = $false; + } + + # require a .c for language cpp + # require a .expected + # require a .ql + + if(-not (Test-Path $testDirectory)){ + Write-Host "Test directory $testDirectory does not exist." + $REPORT += $row + + continue + } + + $dirName = (Get-Item $testDirectory).Basename + $dirNameLower = $dirName.ToLower() + $sharedName = $q.shared_implementation_short_name + + $row["TEST_DIR_EXISTS"] = $true + + if((Test-Path (Join-Path $testDirectory "test.$Language"))){ + $row["SOURCE_CODE_EXISTS"] = $true + }else{ + Write-Host "-SOURCE $((Join-Path $testDirectory "test.$Language")) missing" + } + + if((Test-Path (Join-Path $testDirectory "$sharedName.expected"))){ + $row["EXPECTED_EXISTS"] = $true + }else{ + Write-Host "-EXPECTED $((Join-Path $testDirectory "$sharedName.expected")) missing" + } + + if((Test-Path (Join-Path $testDirectory "$sharedName.ql"))){ + $row["REFERENCE_EXISTS"] = $true + }else{ + Write-Host "-QL $((Join-Path $testDirectory "$sharedName.ql")) missing" + } + + $REPORT += $row +} + +# output a CSV containing the elements that do not contain +$fileTag = "$Language-$(Get-Date -Format "yyyy-MM-dd_HH-mm-ss")" +$reportOutputFile = Join-Path $ReportDir "TestReport-$fileTag.csv" +$missingReportOutputFile = Join-Path $ReportDir "MissingTestReport-$fileTag.csv" + +$failCount = 0 +foreach ($r in $REPORT) { + if(($r["TEST_DIR_EXISTS"] -eq $false) -or ($r["SOURCE_CODE_EXISTS"] -eq $false) -or ($r["EXPECTED_EXISTS"] -eq $false) -or ($r["REFERENCE_EXISTS"] -eq $false)){ + $failCount += 1 + [PSCustomObject]$r | Export-CSV -Path $missingReportOutputFile -Append -NoTypeInformation + } + [PSCustomObject]$r | Export-CSV -Path $reportOutputFile -Append -NoTypeInformation +} + +Write-Host "Write report to $reportOutputFile" + +if(($CIMode) -and ($failCount -gt 0)){ + throw "Found $failCount/$($queriesToCheck.Count) invalid shared test uses" +}else{ + Write-Host "Found $failCount/$($queriesToCheck.Count) invalid shared test uses" +} diff --git a/scripts/validate-amendments-csv.py b/scripts/validate-amendments-csv.py new file mode 100644 index 0000000000..9d83b7d0c9 --- /dev/null +++ b/scripts/validate-amendments-csv.py @@ -0,0 +1,128 @@ +from collections import defaultdict +import csv +import os +from pathlib import Path +import sys +import json + +help_statement = """ +Usage: {script_name} + +A script which detects invalid entries in amendments.csv. +""" + +if (len(sys.argv) == 2 and sys.argv[1] == "--help"): + print(help_statement.format(script_name=sys.argv[0])) + sys.exit(0) + +if not len(sys.argv) == 2: + print("Error: incorrect number of arguments", file=sys.stderr) + print("Usage: " + sys.argv[0] + " [--help]", file=sys.stderr) + sys.exit(1) + +repo_root = Path(__file__).parent.parent +rules_file_path = repo_root.joinpath('rules.csv') +amendments_file_path = repo_root.joinpath('amendments.csv') +language_name = sys.argv[1] + +failed = False + +rules_from_csv = {} +try: + rules_file = open(rules_file_path, "r") +except PermissionError: + print("Error: No permission to read the rules file located at '" + str(rules_file_path) + "'") + sys.exit(1) +else: + with rules_file: + rules_reader = csv.reader(rules_file) + # Skip header row + next(rules_reader, None) + for rule in rules_reader: + language = rule[0] + rule_id = rule[2] + + # only validate rules for the specified language + if not language == language_name: + continue + + rule_dict = { + "standard": rule[1], + "rule_id": rule_id, + "supportable": rule[3] + } + rules_from_csv[rule_id] = rule_dict + +print(f"Found {len(rules_from_csv)} rules.") +print(f"Verifying amendments") + +seen_amendments = set() +try: + amendments_file = open(amendments_file_path, "r") +except PermissionError: + print("Error: No permission to read the amendments file located at '" + str(amendments_file_path) + "'") + sys.exit(1) +else: + with amendments_file: + amendments_reader = csv.reader(amendments_file) + # Skip header row + next(amendments_reader, None) + for amendment in amendments_reader: + language = amendment[0] + + # only validate rules for the specified language + if not language == language_name: + continue + + if len(amendment) != 8: + print(f"🔴 Error: amendment {amendment} has wrong number of fields") + failed = True + continue + + standard = amendment[1] + amendment_name = amendment[2] + rule_id = amendment[3] + supportable = amendment[4] + implemented = amendment[6] + amendment_id = f"{rule_id}-{amendment_name}" + + if not rule_id in rules_from_csv: + print(f"🔴 Error: Amendment {amendment_id} references rule {rule_id}, not found in rules.csv") + failed = True + continue + + rule = rules_from_csv[rule_id] + + if rule["standard"] != standard: + print(f"🟡 Invalid: {amendment_id} has a different standard than the {rule_id} in rules.csv") + print(f" '{standard}' vs '{rule['standard']}'") + failed = True + + if supportable not in {"Yes", "No"}: + print(f"🟡 Invalid: {amendment_id} 'supportable' field should be 'Yes' or 'No'.") + print(f" got '{supportable}'") + failed = True + + if rule["supportable"] != supportable: + print(f"🟡 Invalid: {amendment_id} supportable does not match rules.csv supportable.") + print(f" '{supportable}' vs '{rule['supportable']}'") + failed = True + + if implemented not in {"Yes", "No"}: + print(f"🟡 Invalid: {amendment_id} 'implemented' field should be 'Yes' or 'No'.") + print(f" got '{implemented}'") + failed = True + + if amendment_id in seen_amendments: + print(f"🔴 Error: {amendment_id} has duplicate entries") + failed = True + + seen_amendments.add(amendment_id) + +print(f"Checked {len(seen_amendments)} amendments.") + +if failed: + print("❌ FAILED: Validity issues found in amendments.csv!") + sys.exit(1) +else: + print("✅ PASSED: No validity issues found in amendments.csv! 🎉") diff --git a/scripts/verify-standard-library-version.py b/scripts/verify-standard-library-version.py new file mode 100644 index 0000000000..0b46068e1d --- /dev/null +++ b/scripts/verify-standard-library-version.py @@ -0,0 +1,68 @@ +import argparse +import json +import os +import subprocess +import yaml +import get_workspace_packs + +def get_codeql_packs(codeql_repo, codeql): + command = [codeql, 'resolve', 'qlpacks', '--additional-packs', codeql_repo, '--format', 'json'] + print(f'Running `{" ".join(command)}`') + packs_json = subprocess.check_output(command) + print(packs_json) + packs = json.loads(packs_json) + return packs + +parser = argparse.ArgumentParser(description='Ensure that CodeQL library pack dependency versions match the supported configuration.') +parser.add_argument('--codeql-repo', required=True, help='Path to checkout of `github/codeql` repo at desired branch.') +parser.add_argument('--mode', required=False, choices=['verify', 'update'], default='verify', help="`verify` to fail on mismatch; `update` to change `qlpack.lock.yml` files to use new version.") +parser.add_argument('--codeql', required=False, default='codeql', help='Path to the `codeql` executable.') +args = parser.parse_args() + +# Find the root of the repo +root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Get the packs for the repo's workspace. +workspace_packs = get_workspace_packs.get_workspace_packs(root) + +# Get the packs from the `codeql` repo checkout. +codeql_packs = get_codeql_packs(args.codeql_repo, args.codeql) + +failed = False +for pack in workspace_packs: + pack_path = os.path.join(root, pack) + + print(f"Scanning dependencies of '{pack_path}'...") + + # Read our pack's configuration file. + with open(pack_path) as pack_file: + pack_yaml = yaml.safe_load(pack_file) + + updated = False + if 'dependencies' in pack_yaml: + dependencies = pack_yaml['dependencies'] + for ref_name in dependencies: + ref_version = dependencies[ref_name] + if ref_name in codeql_packs: + # Found this reference in the `codeql` repo. The version of the reference should match + # the version of that pack in the `codeql` repo. + lib_path = codeql_packs[ref_name][0] + lib_path = os.path.join(lib_path, 'qlpack.yml') + with open(lib_path) as lib_file: + lib_yaml = yaml.safe_load(lib_file) + lib_version = lib_yaml['version'] + if ref_version != lib_version: + print(f"Mismatched versions for '{ref_name}', referenced from '{pack_path}'. " + + f"referenced version is '{ref_version}', but should be '{lib_version}'.") + if args.mode == 'verify': + failed = True # Report an error at the end. + else: + pack_yaml['dependencies'][ref_name] = lib_version + updated = True # Update our pack in-place. + + if updated: + print(f"Updating '{pack_path}'...") + with open(pack_path, 'w', newline='\n') as pack_file: # Always use LF even on Windows + yaml.safe_dump(pack_yaml, pack_file, sort_keys=False) + +exit(1 if failed else 0) diff --git a/scripts/verify_rule_package_consistency.py b/scripts/verify_rule_package_consistency.py index 67d09e3590..b9eaa5b934 100644 --- a/scripts/verify_rule_package_consistency.py +++ b/scripts/verify_rule_package_consistency.py @@ -47,7 +47,7 @@ standard = rule[1] rule_id = rule[2] - queryable = rule[3] + supportable = rule[3] obligation_level = rule[4] enforcement_level = rule[5] allocated_targets = rule[6] @@ -57,9 +57,9 @@ difficulty = rule[10] # If the rule is associated with a package if package: - if not queryable == "Yes": + if not supportable == "Yes": print( - f"ERROR: {standard} {rule_id} is included as part of package {package} but is not marked as queryable.") + f"ERROR: {standard} {rule_id} is included as part of package {package} but is not marked as supportable.") failed = True else: package_rules_from_csv[package].add(rule_id) @@ -98,8 +98,35 @@ failed = True if not rule_id in package_rules_from_csv[package_name]: print( - f" - ERROR: Rule {rule_id} included in {package_name}.json but not marked as queryable in rules.csv.") + f" - ERROR: Rule {rule_id} included in {package_name}.json but not marked as supportable in rules.csv.") failed = True + for query in rule_details["queries"]: + if standard_name == "MISRA-C-2012" and not any(tag for tag in query["tags"] if tag.startswith("external/misra/c/2012/")): + print( + f' - ERROR: MISRA C 2012 query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json is missing a `external/misra/c/2012/...` tag.') + failed = True + if not standard_name == "MISRA-C-2012" and any(tag for tag in query["tags"] if tag.startswith("external/misra/c/2012/")): + print( + f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json has a spurious `external/misra/c/2012/...` tag.') + failed = True + if standard_name == "CERT-C" or standard_name == "CERT-C++": + expected_properties = [ + "severity", + "likelihood", + "remediation-cost", + "priority", + "level" + ] + for expected_property in expected_properties: + if not any(tag for tag in query["tags"] if tag.startswith(f"external/cert/{expected_property}/")): + print( + f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json is missing a `external/cert/{expected_property}/...` tag.') + failed = True + if not standard_name == "CERT-C" and not standard_name == "CERT-C++": + if any(tag for tag in query["tags"] if tag.startswith("external/cert/")): + print( + f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json has a spurious `external/cert/...` tag.') + failed = True rules_csv_rule_ids = package_rules_from_csv[package_name] json_missing_rules = rules_csv_rule_ids.difference(package_json_rule_ids) diff --git a/supported_codeql_configs.json b/supported_codeql_configs.json index 269af480e3..9b89dd849e 100644 --- a/supported_codeql_configs.json +++ b/supported_codeql_configs.json @@ -1,17 +1,17 @@ { "supported_environment": [ { - "codeql_cli": "2.9.4", - "codeql_standard_library": "codeql-cli/v2.9.4", - "codeql_cli_bundle": "codeql-bundle-20220615" + "codeql_cli": "2.20.7", + "codeql_standard_library": "codeql-cli/v2.20.7", + "codeql_cli_bundle": "codeql-bundle-v2.20.7" } - ], - "supported_language" : [ + ], + "supported_language": [ { - "language" : "cpp" + "language": "cpp" }, { - "language" : "c" + "language": "c" } ] } \ No newline at end of file